تقرير المبيعات

فايلات الفولدر
| الفايلات |
|---|
| columns.ts |
| useSalesReports.ts |
| SalesReportsView.tsx |
columns.ts
{
id: "id",
accessorKey: "id",
header: t("id"),
size: 30,
meta: {
align: "center",
},
},
- هاي جزء من كود الاعمدة الي راح تخليك تعرض التيبل مالتك ويا الاعمدة الي راح تنطيها الة وبيها مجموعة من القيم ظاهرة امامك
- بحكم تكرار الكود راح يتم شرحة بشكل عام وراح تنشرح تفاصيل المدخلات بعدين بالملفات المشتركة لان هاي البارميتر خاصة بالتيبل الي كاعد يستخدم باكثر من مكان وتم شرحها اكثر من مرة لهذا بعدا عن التكرار راح يتم التركيز عليها بالملفات المشتركة
useSalesReports.ts
export const GetInfinteScrollSalesReportsQuery = (
params: InfiniteScrollSalesReportsParams
) => {
return useInfiniteQuery<SalesReport[]>({
queryKey: ["infinteSalesReports", params],
queryFn: async ({ pageParam = 0 }) => {
const fetchSize = 10;
const offset = (pageParam as number) * fetchSize;
const fetchedData = await GetInfinteSalesReports(
offset,
fetchSize,
params
);
return fetchedData;
},
initialPageParam: 0,
getNextPageParam: (lastPage, allPages) => {
const nextOffset = lastPage.length ? allPages.length : undefined;
return nextOffset;
},
});
};
- هاي دالة تفيدك تعرض بيانات تقارير المبيعات على شكل "scroll لانهائي"، كل ما المستخدم ينزل، تجيب بيانات جديدة من السيرفر، وتاخذ:
type InfiniteScrollSalesReportsParams = {
startingDate?: Date | null;
endingDate?: Date | null;
userId?: string;
orderType?: string;
query: string;
limit?: number;
customerId?: string;
sort: string;
};
const debounceSearch = useMemo(
() => debounce((term: string) => setDebouncedSearch(term), 500),
[]
);
useEffect(() => {
debounceSearch(search);
}, [search]);
- هذا السطر يستخدم debounce حتى يقلل من عدد المرات اللي يتم فيها تنفيذ البحث لما المستخدم يكتب بسرعة بالإنبوت (input).
const openDeleteDialog = (id: number) => {
const bill = salesReports?.pages
.flatMap((d) => d)
?.find((bill) => bill.id == id);
if (bill) {
setSelectedBill(id);
setDeleteDialog(true);
}
};
const deleteBillMutation = useMutation({
mutationFn: deleteBill,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["infinteSalesReports"],
refetchType: "active",
});
setDeleteDialog(false);
setSelectedBill(null);
toast.success(t("successMessage"));
},
onError: (error) => {
const errorMessage = (error as any)?.response?.data?.message;
const errorReason = (error as any)?.response?.data[0]?.reason;
toast.error(t("errorMessage"), {
description: `${errorMessage}${errorReason ? `:${errorReason}` : ""}`,
});
},
});
//
const handleDelete = () => {
if (selectedBill) {
deleteBillMutation.mutate(selectedBill);
}
};
- هاي 3 دوال وحدة راح تفتح النافذة مال عملية تأكيد الحذف والثانية راح تنفذ عملية الحذف بس قبلها تتأكد اذا اكو معرف للفاتورة لو لا والثالثة هي راح تكون مسؤولة عن تنفيذ الحذف والتواصل ويا الباك ايند وتسوي كاش جديد عن طريق انو تسوي refetch
export type SalesHistory = {
billId: number;
id: number;
createdAt: string;
process: string;
user: string;
};
async function showHistoryDialog(row: SalesReport) {
try {
setOpenHistoryDialog(true);
const resp = await axiosInstance.get<SalesHistory[]>(
`/bills/${row.billId}/history`
);
setHistory(resp.data);
} catch (error) {
console.log(error);
}
}
- هاي دالة راح تفتح نافذة تعرض تفاصيل الحركات الي صارت بهذا التقرير مثل هل ضاف عنصر جديد او باع العنصر او انطاه السايق وغيرة والي راح تلكاه بعمود العمليات
SalesReportsView.tsx
<DeleteDialog open={deleteDialog} onOpenChange={setDeleteDialog} title={"هل ترغب في حذف الطلب؟"} handleDelete={handleDelete} />
<Dialog open={openHistoryDialog} onOpenChange={setOpenHistoryDialog}>
<DialogContent className={`overflow-y-scroll max-h-[90vh] max-w-[50vw]`}>
<DialogTitle className="text-[1.25rem] font-normal" dir={dir}>
تاريخ العمليات
</DialogTitle>
<div className="flex flex-col gap-2">
{history && history.length ? (
history.map((h) => (
<div className="p-4 border rounded-sm border-input-100">
<div className="flex gap-2 items-center justify-between">
<div className="font-bold">{h.user}</div>
<div dir="ltr">{formatDateTime(h.createdAt)}</div>
</div>
<div>{h.process}</div>
</div>
))
) : (
<div className="p-4 text-2xl flex justify-center">
<TbMoodEmpty />
</div>
)}
</div>
</DialogContent>
</Dialog>
- هذا راح يعرض الي تفاصيل تاريخ العمليات مالتي وراح يسوي Map للتاريخ مال العمليات ويعرض كل عملية منهن وتاريخها
{
salesReportsSumsLoading ? (
<div className="flex justify-center items-center w-full h-10">
<TbMoodEmpty className="animate-spin text-2xl" />
</div>
) : salesReportsSumsStatus == "success" && salesReportsSums ? (
<>
<div className="flex justify-between text-xs px-2 py-1">
<p>
{t("pages.reports.salesReports.sums.totalAmount")}:{" "}
{formatMoney(salesReportsSums.totalAmount)}
</p>
<p>
{t("pages.reports.salesReports.sums.totalDiscount")}:{" "}
{formatMoney(salesReportsSums.totalDiscount)}
</p>
</div>
<div className="flex justify-between text-xs px-2 py-1">
<p>
{t("pages.reports.salesReports.sums.totalPaid")}:{" "}
{formatMoney(salesReportsSums.totalPaidAmount)}
</p>
<p>
{t("pages.reports.salesReports.sums.totalLeft")}:{" "}
{formatMoney(salesReportsSums.totalAmountLeft)}
</p>
</div>
</>
) : salesReportsSumsStatus == "error" ? (
<div className="flex justify-center items-center w-full h-10">
<TbMoodEmpty className="text-2xl" />
</div>
) : null;
}
- هذا راح يعرض المجاميع الموجودة اسفل الصفحة والكود المستخدم علمود اجيب المجاميع هو
export const getSalesReportsSumsQuery = () =>
useQuery<SalesReportSums>({
queryFn: () => getSalesReportsSums(),
queryKey: ["getSalesReportsSums"],
});
- باقي الصفحة اما مكررة وراح يتم شرح الكود مالتها بعدين او بسيطة من ناحية اللوجك