This commit is contained in:
Samandar Turgunboyev
2026-02-20 15:47:02 +05:00
parent bb84da82a8
commit ef55c1ecd8
6 changed files with 105 additions and 104 deletions

View File

@@ -36,7 +36,7 @@ export async function generateMetadata({
openGraph: { openGraph: {
title, title,
description, description,
url: `/search${query ? `?q=${encodeURIComponent(query)}` : ''}`, url: `/search${query ? `?search=${encodeURIComponent(query)}` : ''}`,
type: 'website', type: 'website',
}, },
twitter: { twitter: {
@@ -64,7 +64,7 @@ export async function generateMetadata({
openGraph: { openGraph: {
title, title,
description, description,
url: `/search${query ? `?q=${encodeURIComponent(query)}` : ''}`, url: `/search${query ? `?search=${encodeURIComponent(query)}` : ''}`,
type: 'website', type: 'website',
}, },
twitter: { twitter: {

View File

@@ -40,17 +40,20 @@ const HistoryTabs = () => {
return ( return (
<div className="flex flex-col items-center justify-center py-16 text-center px-4"> <div className="flex flex-col items-center justify-center py-16 text-center px-4">
<div className="bg-gray-100 p-6 rounded-full mb-4"> <div className="bg-gray-100 p-6 rounded-full mb-4">
<Package className="w-16 h-16 text-gray-400" /> <Package className="w-12 h-12 text-gray-400" />
</div> </div>
<p className="text-xl font-bold text-gray-800 mb-2"> <p className="text-lg font-bold text-gray-800 mb-2">
{t('Buyurtmalar topilmadi')} {t('Buyurtmalar topilmadi')}
</p> </p>
<p className="text-sm text-gray-500 max-w-md"> <p className="text-sm text-gray-500 max-w-xs">
{t( {t(
"Hali buyurtma qilmagansiz. Mahsulotlarni ko'rib chiqing va birinchi buyurtmangizni bering!", "Hali buyurtma qilmagansiz. Mahsulotlarni ko'rib chiqing va birinchi buyurtmangizni bering!",
)} )}
</p> </p>
<Button onClick={() => router.push('/')} className="mt-6"> <Button
onClick={() => router.push('/')}
className="mt-6 w-full max-w-xs"
>
<ShoppingBag className="w-4 h-4 mr-2" /> <ShoppingBag className="w-4 h-4 mr-2" />
{t('Xarid qilish')} {t('Xarid qilish')}
</Button> </Button>
@@ -59,21 +62,21 @@ const HistoryTabs = () => {
} }
return ( return (
<div className="max-w-5xl mx-auto px-3 md:px-0"> <div className="w-full px-3 md:px-0">
{/* Header */} {/* Header */}
<div className="flex flex-col md:flex-row md:items-center justify-between mb-6 pb-4 border-b gap-2"> <div className="flex items-center justify-between mb-4 pb-3 border-b">
<div> <div>
<h2 className="text-2xl md:text-3xl font-bold text-gray-900"> <h2 className="text-xl md:text-3xl font-bold text-gray-900">
{t('Buyurtmalar tarixi')} {t('Buyurtmalar tarixi')}
</h2> </h2>
<p className="text-sm text-gray-500 mt-1"> <p className="text-xs md:text-sm text-gray-500 mt-0.5">
{data.length} {t('ta buyurtma')} {data.length} {t('ta buyurtma')}
</p> </p>
</div> </div>
</div> </div>
{/* Orders List */} {/* Orders List */}
<div className="space-y-4 md:space-y-6"> <div className="space-y-3 md:space-y-6">
{data.map((order: OrderList) => { {data.map((order: OrderList) => {
const totalPrice = order.items.reduce( const totalPrice = order.items.reduce(
(sum, item) => sum + Number(item.price) * item.quantity, (sum, item) => sum + Number(item.price) * item.quantity,
@@ -83,31 +86,31 @@ const HistoryTabs = () => {
return ( return (
<Card <Card
key={order.id} key={order.id}
className="border-2 border-gray-200 hover:border-blue-400 transition-all duration-200 shadow-sm hover:shadow-lg" className="border border-gray-200 hover:border-blue-400 transition-all duration-200 shadow-sm hover:shadow-md rounded-xl overflow-hidden"
> >
<CardContent className="p-0"> <CardContent className="p-0">
{/* Order Header */} {/* Order Header */}
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 p-3 md:p-4 border-b-2 border-gray-200"> <div className="bg-gradient-to-r from-blue-50 to-indigo-50 px-3 py-2.5 md:p-4 border-b border-gray-200">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-3"> <div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-3"> {/* Order ID */}
<div className="bg-blue-600 text-white px-3 py-1 rounded-full text-sm font-bold"> <div className="flex items-center gap-2">
<div className="bg-blue-600 text-white px-2.5 py-1 rounded-full text-xs font-bold">
#{order.id} #{order.id}
</div> </div>
<div>
<p className="text-xs text-gray-500"> <p className="text-xs text-gray-500">
{t('Buyurtma raqami')} {t('Buyurtma raqami')}
</p> </p>
</div> </div>
</div>
{/* Delivery date - compact on mobile */}
{order.delivery_date && ( {order.delivery_date && (
<div className="flex items-center gap-2 bg-white px-3 py-2 rounded-lg shadow-sm"> <div className="flex items-center gap-1.5 bg-white px-2 py-1 rounded-lg shadow-sm">
<Calendar className="w-4 h-4 text-blue-600" /> <Calendar className="w-3.5 h-3.5 text-blue-600 flex-shrink-0" />
<div> <div>
<p className="text-xs text-gray-500"> <p className="text-[10px] text-gray-500 leading-none">
{t('Yetkazib berish')} {t('Yetkazib berish')}
</p> </p>
<p className="text-sm font-semibold text-gray-800"> <p className="text-xs font-semibold text-gray-800 mt-0.5">
{order.delivery_date} {order.delivery_date}
</p> </p>
</div> </div>
@@ -117,14 +120,14 @@ const HistoryTabs = () => {
{/* Comment */} {/* Comment */}
{order.comment && ( {order.comment && (
<div className="mt-3 bg-white p-3 rounded-lg shadow-sm border border-gray-200"> <div className="mt-2 bg-white p-2.5 rounded-lg shadow-sm border border-gray-100">
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<MessageSquare className="w-4 h-4 text-blue-600 mt-0.5 flex-shrink-0" /> <MessageSquare className="w-3.5 h-3.5 text-blue-600 mt-0.5 flex-shrink-0" />
<div className="flex-1"> <div className="flex-1 min-w-0">
<p className="text-xs font-medium text-gray-500 mb-1"> <p className="text-[10px] font-medium text-gray-500 mb-0.5">
{t('Izoh')}: {t('Izoh')}:
</p> </p>
<p className="text-sm text-gray-700 leading-relaxed"> <p className="text-xs text-gray-700 leading-relaxed line-clamp-3">
{order.comment} {order.comment}
</p> </p>
</div> </div>
@@ -134,7 +137,7 @@ const HistoryTabs = () => {
</div> </div>
{/* Products */} {/* Products */}
<div className="p-3 md:p-4 space-y-3"> <div className="p-2.5 md:p-4 space-y-2 md:space-y-3">
{order.items.map((item, index) => { {order.items.map((item, index) => {
const product = item.product; const product = item.product;
@@ -147,12 +150,12 @@ const HistoryTabs = () => {
return ( return (
<div <div
key={item.id} key={item.id}
className="border-2 border-gray-100 rounded-xl p-3 md:p-4 bg-gradient-to-br from-white to-gray-50 hover:shadow-md transition-shadow" className="border border-gray-100 rounded-lg p-2.5 md:p-4 bg-white hover:shadow-sm transition-shadow"
> >
<div className="flex gap-3 md:gap-4"> <div className="flex gap-2.5 md:gap-4">
{/* Image */} {/* Image — smaller on mobile */}
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<div className="w-16 h-16 md:w-24 md:h-24 bg-gray-100 rounded-lg overflow-hidden border-2 border-gray-200"> <div className="w-14 h-14 md:w-24 md:h-24 bg-gray-50 rounded-lg overflow-hidden border border-gray-200">
<Image <Image
src={productImage} src={productImage}
alt={product.name} alt={product.name}
@@ -166,55 +169,47 @@ const HistoryTabs = () => {
{/* Info */} {/* Info */}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-2 mb-2"> {/* Name row */}
<div className="min-w-0"> <div className="flex items-start justify-between gap-1 mb-1.5">
<div className="flex items-center gap-2 mb-1"> <div className="flex items-center gap-1.5 min-w-0">
<span className="bg-blue-100 text-blue-800 text-xs font-semibold px-2 py-1 rounded"> <span className="bg-blue-100 text-blue-800 text-[10px] font-semibold px-1.5 py-0.5 rounded flex-shrink-0">
{index + 1} {index + 1}
</span> </span>
<h3 className="font-bold text-sm md:text-lg text-gray-900 truncate"> <h3 className="font-bold text-sm text-gray-900 truncate">
{product.name} {product.name}
</h3> </h3>
</div> </div>
<p className="text-sm font-bold text-blue-600 flex-shrink-0">
{product.short_name && (
<p className="text-xs md:text-sm text-gray-600 line-clamp-2">
{product.short_name}
</p>
)}
</div>
<div className="text-left md:text-right">
<p className="text-xs text-gray-500">
{t('Mahsulotlar narxi')}
</p>
<p className="text-base md:text-lg font-bold text-blue-600">
{formatPrice(Number(item.price), true)} {formatPrice(Number(item.price), true)}
</p> </p>
</div> </div>
</div>
{/* Details */} {product.short_name && (
<div className="grid grid-cols-2 gap-2 md:gap-3 p-2 md:p-3 bg-white rounded-lg border border-gray-200"> <p className="text-xs text-gray-500 line-clamp-1 mb-1.5">
{product.short_name}
</p>
)}
{/* Qty + Total — single row on mobile */}
<div className="flex items-center justify-between bg-gray-50 rounded-lg px-2.5 py-1.5 border border-gray-100">
<div> <div>
<span className="text-xs text-gray-500"> <span className="text-[10px] text-gray-400 block">
{t('Miqdor')} {t('Miqdor')}
</span> </span>
<p className="text-sm md:text-base font-bold"> <span className="text-xs font-bold">
{item.quantity} {item.quantity}
</p> </span>
</div> </div>
<div className="text-right"> <div className="text-right">
<span className="text-xs text-gray-500"> <span className="text-[10px] text-gray-400 block">
{t('Jami')} {t('Jami')}
</span> </span>
<p className="text-sm md:text-base font-bold text-green-600"> <span className="text-xs font-bold text-green-600">
{formatPrice( {formatPrice(
Number(item.price) * item.quantity, Number(item.price) * item.quantity,
true, true,
)} )}
</p> </span>
</div> </div>
</div> </div>
</div> </div>
@@ -225,18 +220,18 @@ const HistoryTabs = () => {
</div> </div>
{/* Footer */} {/* Footer */}
<div className="bg-gradient-to-r from-gray-50 to-blue-50 p-3 md:p-4 border-t-2 border-gray-200"> <div className="bg-gradient-to-r from-gray-50 to-blue-50 px-3 py-3 md:p-4 border-t border-gray-200">
<div className="space-y-1 md:space-y-2 text-sm"> <div className="space-y-1 text-xs md:text-sm">
<div className="flex justify-between text-gray-600"> <div className="flex justify-between text-gray-500">
<span>{t('Mahsulotlar narxi')}:</span> <span>{t('Mahsulotlar narxi')}:</span>
<span className="font-semibold"> <span className="font-semibold text-gray-700">
{formatPrice(totalPrice, true)} {formatPrice(totalPrice, true)}
</span> </span>
</div> </div>
<div className="flex justify-between text-gray-600"> <div className="flex justify-between text-gray-500">
<span>{t('Mahsulotlar soni')}:</span> <span>{t('Mahsulotlar soni')}:</span>
<span className="font-semibold"> <span className="font-semibold text-gray-700">
{order.items.reduce( {order.items.reduce(
(sum, item) => sum + item.quantity, (sum, item) => sum + item.quantity,
0, 0,
@@ -246,8 +241,10 @@ const HistoryTabs = () => {
</div> </div>
<div className="border-t border-dashed pt-2 flex justify-between items-center"> <div className="border-t border-dashed pt-2 flex justify-between items-center">
<span className="font-bold">{t('Umumiy summa')}:</span> <span className="font-bold text-sm md:text-base">
<span className="text-lg md:text-2xl font-bold text-green-600"> {t('Umumiy summa')}:
</span>
<span className="text-base md:text-2xl font-bold text-green-600">
{formatPrice(totalPrice, true)} {formatPrice(totalPrice, true)}
</span> </span>
</div> </div>
@@ -258,7 +255,7 @@ const HistoryTabs = () => {
onClick={() => onClick={() =>
router.push(`/profile/refresh-order?id=${order.id}`) router.push(`/profile/refresh-order?id=${order.id}`)
} }
className="w-full md:w-auto mt-3 gap-2" className="w-full mt-3 gap-2 text-sm h-9"
> >
<RefreshCw className="w-4 h-4" /> <RefreshCw className="w-4 h-4" />
{t('Qayta buyurtma')} {t('Qayta buyurtma')}

View File

@@ -129,7 +129,7 @@ const Profile = () => {
</div> </div>
{/* Main Content */} {/* Main Content */}
<main className="flex-1 md:p-4 lg:p-8 lg:pb-8"> <main className="flex-1 p-2 overflow-hidden">
<div className="lg:hidden flex items-center justify-between mb-4 md:mb-6"> <div className="lg:hidden flex items-center justify-between mb-4 md:mb-6">
<div className="flex items-center gap-2 md:gap-3"> <div className="flex items-center gap-2 md:gap-3">
<Avatar className="w-10 h-10 md:w-12 md:h-12 ring-2 ring-emerald-500 ring-offset-2"> <Avatar className="w-10 h-10 md:w-12 md:h-12 ring-2 ring-emerald-500 ring-offset-2">

View File

@@ -16,55 +16,57 @@ const SearchResult = () => {
const t = useTranslations(); const t = useTranslations();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const query = searchParams.get('q') || ''; const query = searchParams.get('search') || '';
const [inputValue, setInputValue] = useState(query); const [inputValue, setInputValue] = useState(query);
/* 🔹 Input va URL sync */ /* 🔹 URL → Input sync */
useEffect(() => { useEffect(() => {
setInputValue(query); setInputValue(query);
}, [query]); }, [query]);
/* 🔹 Debounce → Input → URL sync */
useEffect(() => {
const delay = setTimeout(() => {
if (!inputValue.trim()) {
router.replace('/search');
} else {
router.replace(`/search?search=${encodeURIComponent(inputValue)}`);
}
}, 400);
return () => clearTimeout(delay);
}, [inputValue, router]);
/* 🔹 Default product list */ /* 🔹 Default product list */
const { data: productList, isLoading: listLoading } = useQuery({ const { data: productList, isLoading: listLoading } = useQuery({
queryKey: ['product_list'], queryKey: ['product_list'],
queryFn: () => product_api.list({ page: 1, page_size: 12 }), queryFn: () => product_api.list({ page: 1, page_size: 12 }),
select: (res) => res.data.results, select: (res) => res.data.results,
enabled: !query, enabled: !query,
staleTime: 0,
}); });
/* 🔹 Search query */ /* 🔹 Search query */
const { data: searchList, isLoading: searchLoading } = useQuery({ const { data: searchList, isLoading: searchLoading } = useQuery({
queryKey: ['search', query], queryKey: ['search', query, inputValue],
queryFn: () => queryFn: () =>
product_api.search({ product_api.search({
search: query, search: inputValue, // agar backend `q` kutsa → q: query
page: 1, page: 1,
page_size: 12, page_size: 12,
}), }),
select: (res) => { select: (res) => res.data.products,
return res.data.products;
},
enabled: !!query, enabled: !!query,
staleTime: 0,
}); });
const data = query ? (searchList ?? []) : (productList ?? []); const data = query ? (searchList ?? []) : (productList ?? []);
const isLoading = query ? searchLoading : listLoading; const isLoading = query ? searchLoading : listLoading;
/* 🔹 Handlers */ /* 🔹 Handlers */
const handleSearch = (value: string) => {
setInputValue(value);
if (!value.trim()) {
router.push('/search');
return;
}
router.push(`/search?q=${encodeURIComponent(value)}`);
};
const clearSearch = () => { const clearSearch = () => {
setInputValue(''); setInputValue('');
router.push('/search'); router.replace('/search');
}; };
return ( return (
@@ -77,7 +79,7 @@ const SearchResult = () => {
<Input <Input
value={inputValue} value={inputValue}
placeholder={t('Mahsulot nomi')} placeholder={t('Mahsulot nomi')}
onChange={(e) => handleSearch(e.target.value)} onChange={(e) => setInputValue(e.target.value)}
className="w-full pl-10 pr-10 h-12" className="w-full pl-10 pr-10 h-12"
/> />
@@ -86,7 +88,7 @@ const SearchResult = () => {
onClick={clearSearch} onClick={clearSearch}
className="absolute right-3 top-1/2 -translate-y-1/2" className="absolute right-3 top-1/2 -translate-y-1/2"
> >
<X /> <X className="w-5 h-5" />
</button> </button>
)} )}
</div> </div>
@@ -97,10 +99,10 @@ const SearchResult = () => {
<div className="text-center py-20">{t('Yuklanmoqda')}...</div> <div className="text-center py-20">{t('Yuklanmoqda')}...</div>
) : data.length > 0 ? ( ) : data.length > 0 ? (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4"> <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
{data.map((products) => ( {data.map((product) => (
<ProductCard <ProductCard
product={products as ProductListResult} key={(product as ProductListResult).id}
key={(products as ProductListResult).id} product={product as ProductListResult}
/> />
))} ))}
</div> </div>

View File

@@ -196,6 +196,8 @@ export function ProductCard({
onSuccess: () => { onSuccess: () => {
queryClient.refetchQueries({ queryKey: ['product_list'] }); queryClient.refetchQueries({ queryKey: ['product_list'] });
queryClient.refetchQueries({ queryKey: ['list'] });
queryClient.refetchQueries({ queryKey: ['all_products'] });
queryClient.refetchQueries({ queryKey: ['favourite_product'] }); queryClient.refetchQueries({ queryKey: ['favourite_product'] });
}, },

View File

@@ -429,7 +429,7 @@ const Navbar = () => {
onBlur={() => setTimeout(() => setSearchOpen(false), 200)} onBlur={() => setTimeout(() => setSearchOpen(false), 200)}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === 'Enter' && query.trim()) { if (e.key === 'Enter' && query.trim()) {
router.push(`/search?q=${encodeURIComponent(query)}`); router.push(`/search?search=${encodeURIComponent(query)}`);
setSearchOpen(false); setSearchOpen(false);
} }
}} }}