request list + structure

This commit is contained in:
hamid zarghami
2025-10-20 12:28:48 +03:30
commit dbe37e371e
89 changed files with 9547 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
import { type FC } from 'react'
import Stats from './components/Stats'
import Services from './components/Services'
import Orders from './components/Orders'
import NewOrderImage from '@/assets/images/new_order.png'
import { t } from '@/locale'
import Button from '@/components/Button'
import { AddSquare, MessageQuestion } from 'iconsax-react'
import { COLORS } from '@/constants/colors'
import SupportImage from '@/assets/images/support1.png'
const Home: FC = () => {
return (
<div className='flex gap-6'>
<div className='flex-1'>
<Stats />
<Services />
<Orders />
</div>
<div className='min-w-[267px] mt-5'>
<div className='bg-white rounded-3xl p-6'>
<img src={NewOrderImage} className='w-[98px] mx-auto' />
<h4 className='mt-4 text-center font-medium'>
{t('home.submitNewOrder')}
</h4>
<Button
className='mt-2.5'>
<div className='flex gap-1'>
<AddSquare size={20} color='#292D32' />
<div className=''>{t('home.submitNewOrderButton')}</div>
</div>
</Button>
</div>
<div className='mt-7 bg-white rounded-3xl p-6 relative'>
<h4>
{t('home.specialSolution')}
</h4>
<p className='mt-2 text-[#7B7E8B] text-xs'>
{t('home.specialSolutionDescription')}
</p>
<div className='mt-2 flex flex-col gap-2'>
<div className='flex gap-2 items-center text-xs'>
<div className='size-1.5 rounded-full bg-primary'></div>
<div>{t('home.specialSolutionFeature1')}</div>
</div>
<div className='flex gap-2 items-center text-xs'>
<div className='size-1.5 rounded-full bg-primary'></div>
<div>{t('home.specialSolutionFeature2')}</div>
</div>
<div className='flex gap-2 items-center text-xs'>
<div className='size-1.5 rounded-full bg-primary'></div>
<div>{t('home.specialSolutionFeature3')}</div>
</div>
</div>
<Button
label={t('home.freeConsultation')}
className='mt-12'
/>
<div className='mt-2.5 bg-primary/11 py-6 px-4 rounded-[13px]'>
<div className='flex justify-between items-center'>
<div className='flex gap-2 items-center'>
<MessageQuestion size={20} color={COLORS.primary} />
<div className='text-xs font-light'>{t('home.contactUs')}</div>
</div>
<div>{t('home.phoneNumber')}</div>
</div>
</div>
<img src={SupportImage} className='w-[50px] absolute bottom-[159px] left-2' />
</div>
</div>
</div>
)
}
export default Home
@@ -0,0 +1,106 @@
import { type FC } from 'react';
import { Eye } from 'iconsax-react';
import StatusCircle from '@/components/StatusCircle';
import { type Order, type OrderStatus } from './types/OrderTypes';
// کامپوننت رندر شماره سفارش
export const OrderNumberRenderer: FC<{ order: Order }> = ({ order }) => {
return (
<span className="text-sm font-medium text-gray-900">
{order.number}
</span>
);
};
// کامپوننت رندر عنوان سفارش
export const OrderTitleRenderer: FC<{ order: Order }> = ({ order }) => {
return (
<span className="text-sm font-medium text-gray-900">
{order.title}
</span>
);
};
// کامپوننت رندر تاریخ
export const OrderDateRenderer: FC<{ order: Order }> = ({ order }) => {
return (
<div className="text-xs text-gray-500">
<div className="font-medium">
({order.creationDate.gregorian})
</div>
<div>
{order.creationDate.persian}
</div>
</div>
);
};
// کامپوننت رندر وضعیت سفارش
export const OrderStatusRenderer: FC<{ order: Order }> = ({ order }) => {
const getStatusConfig = (status: OrderStatus) => {
switch (status) {
case 'در حال طراحی':
return {
color: 'text-blue-600',
bgColor: 'bg-blue-50'
};
case 'تهیه متریال':
return {
color: 'text-gray-600',
bgColor: 'bg-gray-50'
};
case 'در حال چاپ':
return {
color: 'text-orange-600',
bgColor: 'bg-orange-50'
};
case 'تکمیل شده':
return {
color: 'text-green-600',
bgColor: 'bg-green-50'
};
default:
return {
color: 'text-gray-600',
bgColor: 'bg-gray-50'
};
}
};
const config = getStatusConfig(order.status);
return (
<span className={`text-sm font-medium ${config.color}`}>
{order.status}
</span>
);
};
// کامپوننت رندر پیام‌ها
export const OrderMessagesRenderer: FC<{ order: Order }> = ({ order }) => {
const hasUnreadMessages = order.unreadMessages > 0;
return (
<div className="flex items-center gap-2">
<StatusCircle color={hasUnreadMessages ? '#EF4444' : '#3B82F6'} />
<span className="text-xs text-gray-500">
{order.unreadMessages} پیام خوانده نشده
</span>
</div>
);
};
// کامپوننت رندر آیکون مشاهده
export const OrderViewRenderer: FC<{ order: Order }> = ({ order }) => {
return (
<button
className="p-1 hover:bg-gray-100 rounded-full transition-colors"
onClick={() => {
// اینجا می‌توانید منطق مشاهده جزئیات را اضافه کنید
console.log('View order:', order.id);
}}
>
<Eye color='black' size={16} />
</button>
);
};
+64
View File
@@ -0,0 +1,64 @@
import Table from '@/components/Table'
import { type FC } from 'react'
import { type ColumnType } from '@/components/types/TableTypes'
import { type Order } from './types/OrderTypes'
import { ordersData } from './data/ordersData'
import {
OrderNumberRenderer,
OrderTitleRenderer,
OrderDateRenderer,
OrderStatusRenderer,
OrderMessagesRenderer,
OrderViewRenderer
} from './OrderTableComponents'
const Orders: FC = () => {
const columns: ColumnType<Order>[] = [
{
title: 'شماره',
key: 'number',
render: (order) => <OrderNumberRenderer order={order} />
},
{
title: 'عنوان سفارش',
key: 'title',
render: (order) => <OrderTitleRenderer order={order} />
},
{
title: 'تاریخ ایجاد',
key: 'creationDate',
render: (order) => <OrderDateRenderer order={order} />
},
{
title: 'وضعیت سفارش',
key: 'status',
render: (order) => <OrderStatusRenderer order={order} />
},
{
title: 'پیام ها',
key: 'unreadMessages',
render: (order) => <OrderMessagesRenderer order={order} />
},
{
title: '',
key: 'actions',
render: (order) => <OrderViewRenderer order={order} />,
width: '60px'
}
]
return (
<div className='mt-6 bg-white p-6 rounded-3xl'>
<h4 className='text-lg font-light'>
سفارش های در حال انجام
</h4>
<Table<Order>
columns={columns}
data={ordersData}
/>
</div>
)
}
export default Orders
+13
View File
@@ -0,0 +1,13 @@
import { type FC } from 'react'
const ServiceItem: FC = () => {
return (
<div>
<div className='size-20 rounded-full bg-gray-200 border-[3px] border-primary'>
</div>
</div>
)
}
export default ServiceItem
+44
View File
@@ -0,0 +1,44 @@
import { t } from '@/locale'
import { type FC } from 'react'
import { Swiper, SwiperSlide } from 'swiper/react'
import ServiceItem from './ServiceItem'
const Services: FC = () => {
return (
<div className='mt-6 bg-white p-6 rounded-3xl'>
<h4 className='text-lg font-light'>
{t('home.services')}
</h4>
<div className='mt-6'>
<Swiper
spaceBetween={16}
slidesPerView={'auto'}
className='h-full'
breakpoints={{
640: {
spaceBetween: 24,
},
}}
>
<SwiperSlide className='!w-auto'>
<ServiceItem />
</SwiperSlide>
<SwiperSlide className='!w-auto'>
<ServiceItem />
</SwiperSlide>
<SwiperSlide className='!w-auto'>
<ServiceItem />
</SwiperSlide>
<SwiperSlide className='!w-auto'>
<ServiceItem />
</SwiperSlide>
</Swiper>
</div>
</div>
)
}
export default Services
+34
View File
@@ -0,0 +1,34 @@
import { COLORS } from '@/constants/colors'
import { ArrowLeft } from 'iconsax-react'
import { type FC, type ReactNode } from 'react'
type Props = {
icon: ReactNode,
count: number,
description: string
}
const StatCard: FC<Props> = (props) => {
const { icon, count, description } = props
return (
<div className='flex-1 bg-white rounded-3xl p-6'>
<div className='flex justify-between items-center'>
{icon}
<div className='size-8 bg-[#FFF1D7] rounded-full flex justify-center items-center'>
<ArrowLeft size={16} color={COLORS.primary} className='rotate-45' />
</div>
</div>
<div className='mt-2 text-2xl font-semibold'>
{count}
</div>
<div className='mt-2 text-sm'>
{description}
</div>
</div>
)
}
export default StatCard
+53
View File
@@ -0,0 +1,53 @@
import GridWrapper from '@/components/GridWrapper'
import { COLORS } from '@/constants/colors'
import { t } from '@/locale'
import { BoxTick, BoxTime, ReceiptText, TruckTick } from 'iconsax-react'
import { type FC } from 'react'
import StatCard from './StatCard'
const Stats: FC = () => {
return (
<GridWrapper
desktop={4}
mobile={2}
gapDesktop={24}
gapMobile={12}
className='mt-5'
>
<StatCard
count={2}
description={t('home.requestCount')}
icon={<BoxTime
size={27}
color={COLORS.primary}
/>}
/>
<StatCard
count={2}
description={t('home.factureCount')}
icon={<ReceiptText
size={27}
color={COLORS.primary}
/>}
/>
<StatCard
count={10}
description={t('home.orderCount')}
icon={<BoxTick
size={27}
color={COLORS.primary}
/>}
/>
<StatCard
count={20}
description={t('home.orderDoneCount')}
icon={<TruckTick
size={27}
color={COLORS.primary}
/>}
/>
</GridWrapper>
)
}
export default Stats
@@ -0,0 +1,37 @@
import { type Order } from '../types/OrderTypes';
export const ordersData: Order[] = [
{
id: 1,
number: '123455',
title: 'کارت ویزیت',
creationDate: {
gregorian: '2024-11-01',
persian: '1403/09/01'
},
status: 'در حال طراحی',
unreadMessages: 2
},
{
id: 2,
number: '123455',
title: 'شاپینگ بگ',
creationDate: {
gregorian: '2024-11-01',
persian: '1403/09/01'
},
status: 'تهیه متریال',
unreadMessages: 0
},
{
id: 3,
number: '123455',
title: 'کارت ویزیت',
creationDate: {
gregorian: '2024-11-01',
persian: '1403/09/01'
},
status: 'در حال چاپ',
unreadMessages: 2
}
];
@@ -0,0 +1,22 @@
import { type RowDataType } from '@/components/types/TableTypes';
export interface Order extends RowDataType {
id: string | number;
number: string;
title: string;
creationDate: {
gregorian: string;
persian: string;
};
status: OrderStatus;
unreadMessages: number;
[key: string]: unknown;
}
export type OrderStatus = 'در حال طراحی' | 'تهیه متریال' | 'در حال چاپ' | 'تکمیل شده';
export interface OrderStatusConfig {
status: OrderStatus;
color: string;
textColor: string;
}