api ulandi

This commit is contained in:
Samandar Turgunboyev
2025-12-22 11:35:55 +05:00
parent 37c7120d1b
commit 9978b4e3fe
75 changed files with 10255 additions and 11924 deletions

View File

@@ -0,0 +1,64 @@
import httpClient from '@/shared/config/api/httpClient';
import { API_URLS } from '@/shared/config/api/URLs';
import { AxiosResponse } from 'axios';
export interface OrderList {
count: number;
next: string;
previous: string;
results: OrderListRes[];
}
export interface OrderListRes {
id: string;
order_number: number;
status: 'NEW' | 'DONE';
total_price: number;
payment_type: 'CASH' | 'ACCOUNT_NUMBER';
delivery_type: 'YandexGo' | 'DELIVERY_COURIES' | 'PICKUP';
delivery_price: number;
contact_number: string;
comment: string;
name: string;
items: [
{
id: string;
product: {
id: string;
name: string;
image: string;
price: number;
description: string;
unity: string;
min_quantity: number;
is_active: true;
liked: string;
brand: string;
return_date: string;
expires_date: string;
manufacturer: string;
volume: string;
images: [
{
id: string;
image: string;
},
];
};
price: number;
quantity: number;
created_at: string;
},
];
created_at: string;
}
export const order_api = {
async list(params: {
page: number;
page_size: number;
}): Promise<AxiosResponse<OrderList>> {
const res = await httpClient.get(API_URLS.OrderList, { params });
return res;
},
};

View File

@@ -1,46 +1,29 @@
import { Button } from '@/shared/ui/button';
import { Card, CardContent } from '@/shared/ui/card';
import { Tabs, TabsList, TabsTrigger } from '@/shared/ui/tabs';
import { useQuery } from '@tanstack/react-query';
import { Calendar, CheckCircle, Clock, RefreshCw } from 'lucide-react';
import { useTranslations } from 'next-intl';
import Image from 'next/image';
import { useState } from 'react';
import { order_api } from '../lib/api';
import { orders } from '../lib/data';
const HistoryTabs = () => {
const [historyTab, setHistoryTab] = useState('all');
const t = useTranslations();
const { data } = useQuery({
queryKey: ['order_list'],
queryFn: () => order_api.list({ page: 1, page_size: 1 }),
});
console.log(data);
return (
<>
<div className="flex items-center justify-between mb-4 md:mb-6">
<h2 className="text-xl md:text-2xl font-bold text-foreground">Tarix</h2>
<h2 className="text-xl md:text-2xl font-bold text-foreground">
{t('Tarix')}
</h2>
</div>
<Tabs
value={historyTab}
onValueChange={setHistoryTab}
className="mb-4 md:mb-6"
>
<TabsList className="bg-slate-100 w-full grid grid-cols-3 h-auto p-1">
<TabsTrigger
value="all"
className="data-[state=active]:bg-white text-xs md:text-sm py-2"
>
Barchasi
</TabsTrigger>
<TabsTrigger
value="week"
className="data-[state=active]:bg-white text-xs md:text-sm py-2"
>
Bu hafta
</TabsTrigger>
<TabsTrigger
value="month"
className="data-[state=active]:bg-white text-xs md:text-sm py-2"
>
Bu oy
</TabsTrigger>
</TabsList>
</Tabs>
<div className="space-y-3 md:space-y-4">
{orders
.filter((o) => o.status === 'delivered')
@@ -100,7 +83,7 @@ const HistoryTabs = () => {
className="bg-transparent gap-1 md:gap-2 text-xs md:text-sm h-8 md:h-9 px-2 md:px-3"
>
<RefreshCw className="w-3 h-3 md:w-4 md:h-4" />
Qayta
{t('Qayta')}
</Button>
</div>
</CardContent>

View File

@@ -2,12 +2,14 @@ import { Badge } from '@/shared/ui/badge';
import { Card, CardContent } from '@/shared/ui/card';
import { Tabs, TabsList, TabsTrigger } from '@/shared/ui/tabs';
import { CheckCircle, MapPin, Package, Truck } from 'lucide-react';
import { useTranslations } from 'next-intl';
import Image from 'next/image';
import { useState } from 'react';
import { orders } from '../lib/data';
const Orders = () => {
const [ordersTab, setOrdersTab] = useState('active');
const t = useTranslations();
const getStatusInfo = (status: string) => {
const statusMap: Record<
@@ -61,7 +63,7 @@ const Orders = () => {
<div>
<div className="flex items-center justify-between mb-4 md:mb-6">
<h2 className="text-xl md:text-2xl font-bold text-foreground">
Buyurtmalar
{t('Buyurtmalar')}
</h2>
</div>
@@ -75,19 +77,21 @@ const Orders = () => {
value="active"
className="data-[state=active]:bg-white text-xs md:text-sm py-2"
>
Faol ({orders.filter((o) => o.status !== 'delivered').length})
{t('Faol')} ({orders.filter((o) => o.status !== 'delivered').length}
)
</TabsTrigger>
<TabsTrigger
value="completed"
className="data-[state=active]:bg-white text-xs md:text-sm py-2"
>
Tugadi ({orders.filter((o) => o.status === 'delivered').length})
{t('Tugadi')} (
{orders.filter((o) => o.status === 'delivered').length})
</TabsTrigger>
<TabsTrigger
value="all"
className="data-[state=active]:bg-white text-xs md:text-sm py-2"
>
Barcha ({orders.length})
{t('Barchasi')} ({orders.length})
</TabsTrigger>
</TabsList>
</Tabs>
@@ -128,7 +132,7 @@ const Orders = () => {
<Badge
className={`${statusInfo.bgColor} ${statusInfo.color} border-0 text-xs`}
>
{statusInfo.text}
{t(statusInfo.text)}
</Badge>
</div>
@@ -168,7 +172,8 @@ const Orders = () => {
{"so'm"}
</p>
<p className="text-[10px] md:text-xs text-muted-foreground">
Yetkazish: {order.deliveryFee.toLocaleString()} {"so'm"}
{t('Yetkazish')}: {order.deliveryFee.toLocaleString()}{' '}
{"so'm"}
</p>
</div>
</div>

View File

@@ -1,264 +1,34 @@
'use client';
import { PartnershipForm } from '@/features/about/ui/AboutPage';
import Faq from '@/features/faq/ui/Faq';
import Favourite from '@/features/favourite/ui/Favourite';
import { useRouter } from '@/shared/config/i18n/navigation';
import { getMe, removeToken } from '@/shared/lib/token';
import { Avatar, AvatarFallback, AvatarImage } from '@/shared/ui/avatar';
import { Badge } from '@/shared/ui/badge';
import { Button } from '@/shared/ui/button';
import { Card, CardContent } from '@/shared/ui/card';
import {
CheckCircle,
ChevronRight,
History,
Home,
LogOut,
MapPin,
Package,
RefreshCw,
ShoppingBag,
Truck,
} from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';
import { Headset, Home, LogOut } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useState } from 'react';
import { orders, user } from '../lib/data';
import HistoryTabs from './History';
import Orders from './Orders';
import CustomerSupport from './Support';
const Profile = () => {
const [activeSection, setActiveSection] = useState('overview');
const router = useRouter();
const getStatusInfo = (status: string) => {
const statusMap: Record<
string,
{ text: string; color: string; bgColor: string }
> = {
inTransit: {
text: "Yo'lda",
color: 'text-blue-600',
bgColor: 'bg-blue-100',
},
atPickup: {
text: 'Punktda',
color: 'text-amber-600',
bgColor: 'bg-amber-100',
},
delivered: {
text: 'Yetkazildi',
color: 'text-emerald-600',
bgColor: 'bg-emerald-100',
},
cancelled: {
text: 'Bekor qilindi',
color: 'text-red-600',
bgColor: 'bg-red-100',
},
};
return (
statusMap[status] || {
text: status,
color: 'text-muted-foreground',
bgColor: 'bg-muted',
}
);
};
const getStatusIcon = (status: string) => {
switch (status) {
case 'inTransit':
return <Truck className="w-4 h-4" />;
case 'atPickup':
return <MapPin className="w-4 h-4" />;
case 'delivered':
return <CheckCircle className="w-4 h-4" />;
default:
return <Package className="w-4 h-4" />;
}
};
const t = useTranslations();
const queryClient = useQueryClient();
const user = getMe();
const menuItems = [
{ id: 'overview', label: 'Umumiy', icon: Home },
{ id: 'orders', label: 'Buyurtmalar', icon: ShoppingBag },
{ id: 'history', label: 'Tarix', icon: History },
{ id: 'support', label: "Qo'llab-quvatlash", icon: Headset },
];
const renderContent = () => {
switch (activeSection) {
case 'orders':
return <Orders />;
case 'history':
return <HistoryTabs />;
case 'favorites':
return <Favourite />;
case 'agency':
return <PartnershipForm />;
case 'faq':
return <Faq />;
case 'support':
return <CustomerSupport />;
router.push('https://t.me/web_app_0515_bot');
default:
return (
<>
{/* Active Orders Section */}
<div className="mb-6 md:mb-8">
<div className="flex items-center justify-between mb-3 md:mb-4">
<h3 className="text-base md:text-lg font-semibold text-foreground">
Faol buyurtmalar
</h3>
<Button
variant="ghost"
size="sm"
className="text-emerald-600 hover:text-emerald-700 h-8 text-xs md:text-sm"
onClick={() => setActiveSection('orders')}
>
Barchasi
<ChevronRight className="w-3 h-3 md:w-4 md:h-4 ml-1" />
</Button>
</div>
<div className="space-y-3">
{orders
.filter((o) => o.status !== 'delivered')
.slice(0, 2)
.map((order) => {
const statusInfo = getStatusInfo(order.status);
return (
<Card
key={order.id}
className="border-0 shadow-sm hover:shadow-md transition-shadow"
>
<CardContent className="p-3 md:p-5">
<div className="flex items-start justify-between mb-3 md:mb-4">
<div className="flex items-center gap-2 md:gap-3">
<div
className={`w-10 h-10 md:w-12 md:h-12 rounded-xl ${statusInfo.bgColor} flex items-center justify-center shrink-0`}
>
<span className={statusInfo.color}>
{getStatusIcon(order.status)}
</span>
</div>
<div>
<p className="font-semibold text-sm md:text-base text-foreground">
{order.id}
</p>
<p className="text-xs md:text-sm text-muted-foreground">
{order.date} {order.time}
</p>
</div>
</div>
<Badge
className={`${statusInfo.bgColor} ${statusInfo.color} border-0 text-xs`}
>
{statusInfo.text}
</Badge>
</div>
<div className="flex flex-wrap gap-1.5 md:gap-2 mb-3 md:mb-4">
{order.items.map((item, idx) => (
<span
key={idx}
className="px-2 md:px-3 py-1 bg-slate-100 rounded-full text-xs text-muted-foreground"
>
{item.name} ×{item.quantity}
</span>
))}
</div>
<div className="flex flex-col md:flex-row md:items-center justify-between pt-3 md:pt-4 border-t border-slate-100 gap-2">
<div className="flex items-center gap-2 text-xs md:text-sm text-muted-foreground">
<MapPin className="w-3 h-3 md:w-4 md:h-4 shrink-0" />
<span className="truncate">{order.address}</span>
</div>
<p className="font-bold text-sm md:text-base text-foreground">
{(
order.total + order.deliveryFee
).toLocaleString()}{' '}
{"so'm"}
</p>
</div>
</CardContent>
</Card>
);
})}
</div>
</div>
{/* Order History */}
<div>
<div className="flex items-center justify-between mb-3 md:mb-4">
<h3 className="text-base md:text-lg font-semibold text-foreground">
Buyurtmalar tarixi
</h3>
<Button
variant="ghost"
size="sm"
className="text-emerald-600 hover:text-emerald-700 h-8 text-xs md:text-sm"
onClick={() => setActiveSection('history')}
>
Barchasi{' '}
<ChevronRight className="w-3 h-3 md:w-4 md:h-4 ml-1" />
</Button>
</div>
<div className="space-y-3">
{orders
.filter((o) => o.status === 'delivered')
.slice(0, 2)
.map((order) => (
<Card
key={order.id}
className="border-0 shadow-sm hover:shadow-md transition-shadow"
>
<CardContent className="p-3 md:p-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3 md:gap-4">
<div className="w-10 h-10 md:w-12 md:h-12 rounded-xl bg-emerald-100 flex items-center justify-center shrink-0">
<CheckCircle className="w-4 h-4 md:w-5 md:h-5 text-emerald-600" />
</div>
<div className="min-w-0">
<p className="font-semibold text-sm md:text-base text-foreground">
{order.id}
</p>
<p className="text-xs md:text-sm text-muted-foreground truncate">
{order.items.map((i) => i.name).join(', ')}
</p>
</div>
</div>
<div className="text-right shrink-0 ml-2">
<p className="font-bold text-sm md:text-base text-foreground">
{(
order.total + order.deliveryFee
).toLocaleString()}
</p>
<p className="text-xs text-muted-foreground">
{order.date}
</p>
</div>
</div>
<div className="flex gap-2 mt-3 md:mt-4">
<Button
variant="outline"
size="sm"
className="flex-1 gap-1 md:gap-2 bg-transparent h-8 md:h-9 text-xs md:text-sm"
>
<RefreshCw className="w-3 h-3 md:w-4 md:h-4" />
Qayta
</Button>
</div>
</CardContent>
</Card>
))}
</div>
</div>
</>
);
return <HistoryTabs />;
}
};
@@ -279,7 +49,7 @@ const Profile = () => {
}`}
>
<Icon className="w-4 h-4" />
{item.label}
{t(item.label)}
</button>
);
})}
@@ -289,20 +59,18 @@ const Profile = () => {
<div>
<div className="flex gap-4 md:gap-6">
{/* Desktop Sidebar - hidden on mobile */}
<div className="hidden md:block w-80 shrink-0">
<div className="hidden lg:block w-80 shrink-0">
<div className="flex items-center gap-4 mb-8">
<Avatar className="w-14 h-14 ring-2 ring-emerald-500 ring-offset-2 flex items-center justify-center">
<AvatarImage
src={user.avatar || '/placeholder.svg'}
alt={user.phone}
className="h-12 w-12"
/>
<AvatarFallback className="bg-emerald-500 text-white font-semibold">
U
<AvatarImage />
<AvatarFallback className="text-muted-foreground font-semibold">
{user?.slice(0, 1).toUpperCase()}
</AvatarFallback>
</Avatar>
<div>
<p className="text-sm text-muted-foreground">{user.phone}</p>
<p className="text-lg text-muted-foreground font-medium">
{user && user.charAt(0).toUpperCase() + user.slice(1)}
</p>
</div>
</div>
@@ -319,7 +87,7 @@ const Profile = () => {
}`}
>
<item.icon className="w-5 h-5" />
{item.label}
{t(item.label)}
</button>
))}
</nav>
@@ -328,13 +96,16 @@ const Profile = () => {
<Button
variant="ghost"
onClick={() => {
localStorage.removeItem('user');
queryClient.refetchQueries({ queryKey: ['product_list'] });
queryClient.refetchQueries({ queryKey: ['favourite_product'] });
queryClient.refetchQueries({ queryKey: ['search'] });
removeToken();
router.push('/');
}}
className="w-full justify-start gap-3 text-red-500 hover:text-red-600 hover:bg-red-50 mt-4"
>
<LogOut className="w-5 h-5" />
Chiqish
{t('Chiqish')}
</Button>
</div>
@@ -343,17 +114,14 @@ const Profile = () => {
<div className="lg:hidden flex items-center justify-between mb-4 md:mb-6">
<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">
<AvatarImage
src={user.avatar || '/placeholder.svg'}
alt={user.phone}
/>
<AvatarFallback className="bg-emerald-500 text-white text-sm md:text-base">
U
<AvatarImage />
<AvatarFallback className="text-muted-foreground font-semibold">
{user?.slice(0, 1).toUpperCase()}
</AvatarFallback>
</Avatar>
<div>
<p className="text-xs md:text-sm text-muted-foreground">
{user.phone}
<p className="text-md md:text-xl text-muted-foreground">
{user && user.charAt(0).toUpperCase() + user.slice(1)}
</p>
</div>
</div>
@@ -361,7 +129,12 @@ const Profile = () => {
variant="ghost"
size="icon"
onClick={() => {
localStorage.removeItem('user');
queryClient.refetchQueries({ queryKey: ['product_list'] });
queryClient.refetchQueries({
queryKey: ['favourite_product'],
});
queryClient.refetchQueries({ queryKey: ['search'] });
removeToken();
router.push('/');
}}
className="w-9 h-9 md:w-10 md:h-10"

View File

@@ -41,7 +41,6 @@ export default function CustomerSupport() {
}
const category = categories.find((c) => c.id === selectedCategory);
if (!category) {
alert('Kategoriya topilmadi');
return;
@@ -49,19 +48,27 @@ export default function CustomerSupport() {
const telegramText = `🔔 Yangi murojaat\n\n📋 Kategoriya: ${category.title}\n\n💬 Xabar:\n${message}`;
const telegramUsername = 'web_app_0515_bot';
const telegramUrl = `https://t.me/${telegramUsername}?text=${encodeURIComponent(
// Foydalanuvchi ID sini oling (masalan, auth orqali)
const userId = '6487794662'; // <-- bu yerda dinamik ID bo'lishi kerak
// Telegram link bot orqali yuborish
const botToken = 'web_app_0515_bot';
const telegramUrl = `https://api.telegram.org/bot${botToken}/sendMessage?chat_id=${userId}&text=${encodeURIComponent(
telegramText,
)}`;
window.open(telegramUrl, '_blank');
setShowSuccess(true);
setTimeout(() => {
setShowSuccess(false);
setSelectedCategory('');
setMessage('');
}, 3000);
fetch(telegramUrl)
.then(() => {
setShowSuccess(true);
setTimeout(() => {
setShowSuccess(false);
setSelectedCategory('');
setMessage('');
}, 3000);
})
.catch(() => {
alert('Xabar yuborishda xatolik yuz berdi');
});
};
return (