crd warranty

This commit is contained in:
hamid zarghami
2025-09-24 10:32:51 +03:30
parent a8e2cbb065
commit c0b6f1577c
8 changed files with 290 additions and 9 deletions
+5
View File
@@ -134,5 +134,10 @@ export const Pages = {
update: "/products/brands/update/",
create: "/products/brands/create",
},
warranty: {
list: "/products/warranty",
create: "/products/warranty/create",
update: "/products/warranty/update/",
},
},
};
+123
View File
@@ -0,0 +1,123 @@
import { type FC, useState } from 'react'
import { useFormik } from 'formik'
import { type CreateWarrantyType } from './types/Types'
import * as Yup from 'yup'
import { useCreateWarranty } from './hooks/useWarrantyData'
import { useNavigate } from 'react-router-dom'
import Input from '../../components/Input'
import Button from '../../components/Button'
import { TickCircle } from 'iconsax-react'
import { useUploadSingle } from '../uploader/hooks/useUploaderData'
import UploadBoxDraggble from '@/components/UploadBoxDraggble'
import { toast } from 'react-toastify'
import { extractErrorMessage } from '@/helpers/utils'
import { Pages } from '@/config/Pages'
const CreateWarranty: FC = () => {
const navigate = useNavigate()
const uploadSingle = useUploadSingle()
const createWarrantyMutation = useCreateWarranty()
const [logoFile, setLogoFile] = useState<File[]>([])
const formik = useFormik<CreateWarrantyType>({
initialValues: {
name: '',
duration: '',
logoUrl: '',
},
validationSchema: Yup.object({
name: Yup.string().required('عنوان گارانتی الزامی است'),
duration: Yup.string().required('مدت زمان گارانتی الزامی است'),
}),
onSubmit: async (values) => {
let logoUrl = values.logoUrl
if (logoFile.length > 0) {
const logoResponse = await uploadSingle.mutateAsync(logoFile[0])
logoUrl = logoResponse.results?.url?.url || ''
}
const submitData = {
...values,
logoUrl
}
createWarrantyMutation.mutate(submitData, {
onSuccess: () => {
toast.success('گارانتی با موفقیت ایجاد شد')
navigate(Pages.products.warranty.list)
},
onError: (error) => {
toast.error(extractErrorMessage(error))
}
})
},
})
return (
<div className='mt-4'>
<div className='flex justify-between items-center'>
<div className='flex items-center gap-3'>
<h1>ساخت گارانتی جدید</h1>
</div>
<div>
<Button
className='px-6'
onClick={() => formik.handleSubmit()}
isLoading={createWarrantyMutation.isPending || uploadSingle.isPending}
disabled={!formik.isValid || createWarrantyMutation.isPending || uploadSingle.isPending}
>
<div className='flex gap-2 items-center'>
<TickCircle size={16} color='#fff' />
<div>ثبت گارانتی</div>
</div>
</Button>
</div>
</div>
<div className='flex gap-6 xl:mt-8 mt-6'>
{/* اطلاعات اصلی */}
<div className='flex-1 text-sm bg-white py-8 xl:px-10 px-6 rounded-3xl'>
<h2 className='text-lg font-medium mb-6'>اطلاعات اصلی</h2>
<div className='space-y-6'>
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
<Input
label='عنوان گارانتی'
placeholder='مثال: گارانتی طلایی'
{...formik.getFieldProps('name')}
error_text={formik.touched.name && formik.errors.name ? formik.errors.name : ''}
/>
<Input
label='مدت زمان گارانتی'
placeholder='مثال: 24 ماه'
{...formik.getFieldProps('duration')}
error_text={formik.touched.duration && formik.errors.duration ? formik.errors.duration : ''}
/>
</div>
</div>
</div>
{/* فایل‌ها */}
<div className='w-80 space-y-6'>
<div className='text-sm bg-white py-8 px-6 rounded-3xl'>
<h2 className='text-lg font-medium mb-6'>فایلها</h2>
<div className='space-y-6'>
<div>
<UploadBoxDraggble
label='لوگو گارانتی'
isMultiple={false}
onChange={(files) => setLogoFile(files)}
/>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default CreateWarranty
+117
View File
@@ -0,0 +1,117 @@
import { type FC, useState } from 'react'
import { useGetWarranties, useDeleteWarranty } from './hooks/useWarrantyData'
import { type Warranty } from './types/Types'
import PageLoading from '@/components/PageLoading'
import Error from '@/components/Error'
import PaginationUi from '@/components/PaginationUi'
import Td from '@/components/Td'
import TrashWithConfrim from '@/components/TrashWithConfrim'
import Button from '@/components/Button'
import { useNavigate } from 'react-router-dom'
import { Pages } from '@/config/Pages'
const WarrantyList: FC = () => {
const navigate = useNavigate()
const [currentPage, setCurrentPage] = useState(1);
const { data: warrantiesData, isLoading, error, refetch } = useGetWarranties(currentPage)
const deleteWarrantyMutation = useDeleteWarranty();
if (isLoading) {
return (
<div className="flex justify-center items-center min-h-[400px]">
<PageLoading />
</div>
);
}
if (error) {
return (
<div className="flex justify-center items-center min-h-[400px]">
<Error errorText="خطا در بارگذاری گارانتی‌ها" />
</div>
);
}
const handleDeleteWarranty = async (warrantyId: string) => {
await deleteWarrantyMutation.mutateAsync(warrantyId);
refetch()
};
const warranties = warrantiesData?.results?.warranties || [];
const pager = warrantiesData?.results?.pager;
return (
<div>
<div className='flex justify-end mt-5'>
<Button
label='افزودن گارانتی'
onClick={() => navigate(`${Pages.products.warranty.create}`)}
className='w-fit'
/>
</div>
<div className='relative overflow-x-auto rounded-3xl mt-5 w-full'>
<table className='w-full text-sm'>
<thead className='thead'>
<tr>
<Td text={'لوگو'} />
<Td text={'نام'} />
<Td text={'مدت زمان'} />
<Td text={'وضعیت'} />
<Td text={'عملیات'} />
</tr>
</thead>
<tbody>
{warranties.length === 0 ? (
<tr className='tr'>
<td colSpan={5} className="text-center py-8 text-gray-500">
هیچ گارانتی یافت نشد
</td>
</tr>
) : (
warranties.map((warranty: Warranty) => (
<tr key={warranty._id} className='tr'>
<Td text="">
<div className="w-12 h-12 rounded-lg overflow-hidden">
<img
src={warranty.logoUrl || '/placeholder-image.png'}
alt={warranty.name}
className="w-full h-full object-cover"
/>
</div>
</Td>
<Td text={warranty.name} />
<Td text={warranty.duration} />
<Td text="">
<div className="flex items-center gap-2">
<span className={`px-2 py-1 rounded-full text-xs`}>
{warranty.status}
</span>
</div>
</Td>
<Td text="">
<div className="flex items-center gap-2">
<TrashWithConfrim
onDelete={() => handleDeleteWarranty(warranty._id.toString())}
isLoading={deleteWarrantyMutation.isPending}
/>
</div>
</Td>
</tr>
))
)}
</tbody>
</table>
</div>
<PaginationUi
pager={pager}
onPageChange={(page) => {
setCurrentPage(page);
}}
/>
</div>
)
}
export default WarrantyList
+16 -4
View File
@@ -1,10 +1,22 @@
import { useQuery } from "@tanstack/react-query";
import { useMutation, useQuery } from "@tanstack/react-query";
import * as api from "../service/WarrantyService";
import type { WarrantyResponse } from "../types/Types";
export const useGetWarranties = () => {
export const useGetWarranties = (page: number = 1, limit: number = 10) => {
return useQuery<WarrantyResponse>({
queryKey: ["warranties"],
queryFn: () => api.getWarranties(),
queryKey: ["warranties", page, limit],
queryFn: () => api.getWarranties({ page, limit }),
});
};
export const useDeleteWarranty = () => {
return useMutation({
mutationFn: (id: string) => api.deleteWarranty(id),
});
};
export const useCreateWarranty = () => {
return useMutation({
mutationFn: api.createWarranty,
});
};
+15 -1
View File
@@ -1,5 +1,9 @@
import axios from "../../../config/axios";
import type { GetWarrantiesParams, WarrantyResponse } from "../types/Types";
import type {
GetWarrantiesParams,
WarrantyResponse,
CreateWarrantyType,
} from "../types/Types";
export const getWarranties = async (
params?: GetWarrantiesParams
@@ -7,3 +11,13 @@ export const getWarranties = async (
const { data } = await axios.get("/warranty", { params });
return data;
};
export const deleteWarranty = async (id: string) => {
const { data } = await axios.delete(`/admin/warranty/${id}`);
return data;
};
export const createWarranty = async (params: CreateWarrantyType) => {
const { data } = await axios.post(`/admin/warranty`, params);
return data;
};
+8 -2
View File
@@ -14,8 +14,8 @@ export interface Pager {
limit: number;
totalItems: number;
totalPages: number;
prevPage: boolean;
nextPage: boolean;
prevPage: boolean | null;
nextPage: string | null;
}
export interface WarrantyResults {
@@ -33,3 +33,9 @@ export interface GetWarrantiesParams {
page?: number;
limit?: number;
}
export type CreateWarrantyType = {
name: string;
duration: string;
logoUrl: string;
};
+4
View File
@@ -17,6 +17,8 @@ import UpdateProduct from '../pages/products/Update'
import ProductVariant from '@/pages/products/ProductVariant'
import BrandList from '@/pages/brand/List'
import BrandCreate from '@/pages/brand/Create'
import WarrantyList from '@/pages/warranty/List'
import CreateWarranty from '@/pages/warranty/Create'
const MainRouter: FC = () => {
@@ -46,6 +48,8 @@ const MainRouter: FC = () => {
<Route path={`${Pages.products.variants}:id`} element={<ProductVariant />} />
<Route path={Pages.products.brands.list} element={<BrandList />} />
<Route path={Pages.products.brands.create} element={<BrandCreate />} />
<Route path={Pages.products.warranty.list} element={<WarrantyList />} />
<Route path={Pages.products.warranty.create} element={<CreateWarranty />} />
</Routes>
</div>
</div>
+2 -2
View File
@@ -379,8 +379,8 @@ const ProductsSubMenu: FC = () => {
/>
<SubMenuItem
title={'گارانتی ها'}
isActive={isActive('warranties')}
link="/products/warranties"
isActive={isActive('warranty')}
link={Pages.products.warranty.list}
/>
</div>
</div>