catalogue service , hook

This commit is contained in:
hamid zarghami
2026-06-07 10:38:31 +03:30
parent 172cbea22c
commit 4daa03d0de
11 changed files with 186 additions and 187 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
import GridWrapper from "@/components/GridWrapper";
import { usePageTitle } from "@/hooks/usePageTitle";
import { type FC } from "react";
import { useGetCatalog } from "../home/hooks/useHomeData";
import { useGetCatalog } from "./hooks/useCatalogueData";
import ButtonAddCatalogue from "./components/ButtonAddCatalogue";
import CatalogueGridSkeleton from "./components/CatalogueGridSkeleton";
import CatalogueItem from "./components/CatalogueItem";
@@ -2,7 +2,7 @@ import { toast } from "@/components/Toast";
import TrashWithConfrim from "@/components/TrashWithConfrim";
import { Paths } from "@/config/Paths";
import { extractErrorMessage } from "@/helpers/utils";
import { useDleteCatalog } from "@/pages/home/hooks/useHomeData";
import { useDleteCatalog } from "@/pages/catalogue/hooks/useCatalogueData";
import { requestViewerFullscreen } from "@/pages/viewer/utils/viewerFullscreen";
import { Edit, Eye, Share } from "iconsax-react";
import moment from "moment-jalaali";
@@ -1,6 +1,127 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { useCallback, useEffect, useRef, useState } from "react";
import * as api from "../service/CatalogueService";
export const useCreateCatalog = () => {
return useMutation({
mutationFn: api.createCatalog,
});
};
export const useDleteCatalog = () => {
return useMutation({
mutationFn: (id: string) => api.deleteCatalogById(id),
});
};
export const useGetCatalog = () => {
return useQuery({
queryKey: ["catalogs"],
queryFn: api.getCatalogs,
});
};
const catalogMongoIdPattern = /^[a-f\d]{24}$/i;
export const isCatalogMongoId = (value: string) =>
catalogMongoIdPattern.test(value);
export const useGetCatalogById = (id: string) => {
return useQuery({
queryKey: ["catalog", id],
queryFn: () => api.getCatalogById(id),
enabled: !!id,
// جلوگیری از بازنویسی state محلی با refetch هنگام فوکوس پنجره (مثلاً دو کاربر همزمان)
refetchOnWindowFocus: false,
staleTime: 5 * 60 * 1000,
});
};
/** Route param may be Mongo id (editor/admin) or slug (public viewer-style URLs). */
export const useGetCatalogByIdOrSlug = (param: string) => {
const isId = isCatalogMongoId(param);
const byId = useGetCatalogById(isId ? param : "");
const bySlug = useGetCatalogBySlug(isId ? "" : param);
return isId ? byId : bySlug;
};
export const useGetCatalogBySlug = (slug: string) => {
return useQuery({
queryKey: ["catalog", slug],
queryFn: () => api.getCatalogBySlug(slug),
enabled: !!slug,
// جلوگیری از بازنویسی state محلی با refetch هنگام فوکوس پنجره (مثلاً دو کاربر همزمان)
refetchOnWindowFocus: false,
staleTime: 5 * 60 * 1000,
});
};
export const useUpdateCatalog = () => {
const mutation = useMutation({
mutationFn: api.updateCatalog,
});
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const pendingParamsRef = useRef<
Parameters<typeof api.updateCatalog>[0] | null
>(null);
const [isScheduledSaving, setIsScheduledSaving] = useState(false);
const cancelPendingUpdate = useCallback(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
pendingParamsRef.current = null;
setIsScheduledSaving(false);
}, []);
const scheduleUpdate = useCallback(
(params: Parameters<typeof api.updateCatalog>[0], delayMs = 3000) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
pendingParamsRef.current = params;
setIsScheduledSaving(true);
timeoutRef.current = setTimeout(() => {
const pendingParams = pendingParamsRef.current;
timeoutRef.current = null;
pendingParamsRef.current = null;
setIsScheduledSaving(false);
if (pendingParams) {
mutation.mutate(pendingParams);
}
}, delayMs);
},
[mutation],
);
useEffect(
() => () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
// اگر کاربر قبل از پایان debounce از ادیتور خارج شد،
// آخرین تغییرات معلق را همان لحظه ذخیره کن.
if (pendingParamsRef.current) {
mutation.mutate(pendingParamsRef.current);
pendingParamsRef.current = null;
}
setIsScheduledSaving(false);
},
[mutation],
);
return {
...mutation,
scheduleUpdate,
cancelPendingUpdate,
isSaving: isScheduledSaving || mutation.isPending,
};
};
export const usePurchaseInitate = () => {
return useMutation({
mutationFn: api.purchaseInitate,
@@ -1,5 +1,47 @@
import axios from "@/config/axios";
import type { CatalogResponseType, PurchaseInitiate } from "../types/Types";
import type {
CatalogByIdResponseType,
CatalogResponseType,
CreateCatalogParamsType,
PurchaseInitiate,
UpdateCatalogParamsType,
} from "../types/Types";
export const createCatalog = async (params: CreateCatalogParamsType) => {
const { data } = await axios.post("/admin/catalogue", params);
return data;
};
export const getCatalogs = async () => {
const { data } = await axios.get<CatalogResponseType>("/admin/catalogue");
return data;
};
export const getCatalogById = async (id: string) => {
const { data } = await axios.get<CatalogByIdResponseType>(
`/admin/catalogue/${id}`,
);
return data;
};
export const getCatalogBySlug = async (slug: string) => {
const { data } = await axios.get<CatalogByIdResponseType>(
`/public/catalogue/slug/${slug}`,
);
return data;
};
export const updateCatalog = async (params: UpdateCatalogParamsType) => {
const { data } = await axios.patch(`/admin/catalogue/${params.id}`, {
content: params.content,
});
return data;
};
export const deleteCatalogById = async (id: string) => {
const { data } = await axios.delete(`/admin/catalogue/${id}`);
return data;
};
export const purchaseInitate = async (params: PurchaseInitiate) => {
const { data } = await axios.post(
+13
View File
@@ -16,3 +16,16 @@ export type CatalogByIdResponseType = BaseResponse<CatalogItemType>;
export type PurchaseInitiate = {
count: number;
};
export type CreateCatalogParamsType = {
name: string;
slug: string;
size: string;
content?: string;
id?: string;
};
export type UpdateCatalogParamsType = {
id: string;
content: string;
};
+4 -1
View File
@@ -4,7 +4,10 @@ import Konva from "konva";
import { type FC, useEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
import { useSharedStore } from "@/shared/store/sharedStore";
import { useGetCatalogById, useUpdateCatalog } from "../home/hooks/useHomeData";
import {
useGetCatalogById,
useUpdateCatalog,
} from "@/pages/catalogue/hooks/useCatalogueData";
import EditorCanvas from "./components/EditorCanvas";
import EditorSidebar from "./components/EditorSidebar";
import LayersPanel from "./components/LayersPanel";
+2 -2
View File
@@ -6,11 +6,11 @@ import { toast } from "@/components/Toast";
import { Paths } from "@/config/Paths";
import { clx, extractErrorMessage } from "@/helpers/utils";
import { usePageTitle } from "@/hooks/usePageTitle";
import { useCreateCatalog } from "@/pages/catalogue/hooks/useCatalogueData";
import { ArrowLeft, DocumentText } from "iconsax-react";
import { useState, type FC } from "react";
import { useNavigate } from "react-router-dom";
import DirectLogin from "../auth/components/DirectLogin";
import { useCreateCatalog } from "./hooks/useHomeData";
const CATALOG_SIZES = [
{ id: "a4", label: "A4" },
@@ -71,7 +71,7 @@ const Home: FC = () => {
type="button"
onClick={() => setActive(id)}
className={clx(
"flex w-full h-[126px] sm:w-[120px] cursor-pointer items-center justify-center rounded-[20px] border border-border sm:h-[146px] sm:w-[140px] lg:h-[166px] lg:w-[160px]",
"flex w-full h-[126px] cursor-pointer items-center justify-center rounded-[20px] border border-border sm:h-[146px] sm:w-[140px] lg:h-[166px] lg:w-[160px]",
active === id && "border-black!",
)}
>
-123
View File
@@ -1,123 +0,0 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { useCallback, useEffect, useRef, useState } from "react";
import * as api from "../service/HomeService";
export const useCreateCatalog = () => {
return useMutation({
mutationFn: api.createCatalog,
});
};
export const useDleteCatalog = () => {
return useMutation({
mutationFn: (id: string) => api.deleteCatalogById(id),
});
};
export const useGetCatalog = () => {
return useQuery({
queryKey: ["catalogs"],
queryFn: api.getCatalogs,
});
};
const catalogMongoIdPattern = /^[a-f\d]{24}$/i;
export const isCatalogMongoId = (value: string) =>
catalogMongoIdPattern.test(value);
export const useGetCatalogById = (id: string) => {
return useQuery({
queryKey: ["catalog", id],
queryFn: () => api.getCatalogById(id),
enabled: !!id,
// جلوگیری از بازنویسی state محلی با refetch هنگام فوکوس پنجره (مثلاً دو کاربر همزمان)
refetchOnWindowFocus: false,
staleTime: 5 * 60 * 1000,
});
};
/** Route param may be Mongo id (editor/admin) or slug (public viewer-style URLs). */
export const useGetCatalogByIdOrSlug = (param: string) => {
const isId = isCatalogMongoId(param);
const byId = useGetCatalogById(isId ? param : "");
const bySlug = useGetCatalogBySlug(isId ? "" : param);
return isId ? byId : bySlug;
};
export const useGetCatalogBySlug = (slug: string) => {
return useQuery({
queryKey: ["catalog", slug],
queryFn: () => api.getCatalogBySlug(slug),
enabled: !!slug,
// جلوگیری از بازنویسی state محلی با refetch هنگام فوکوس پنجره (مثلاً دو کاربر همزمان)
refetchOnWindowFocus: false,
staleTime: 5 * 60 * 1000,
});
};
export const useUpdateCatalog = () => {
const mutation = useMutation({
mutationFn: api.updateCatalog,
});
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const pendingParamsRef = useRef<
Parameters<typeof api.updateCatalog>[0] | null
>(null);
const [isScheduledSaving, setIsScheduledSaving] = useState(false);
const cancelPendingUpdate = useCallback(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
pendingParamsRef.current = null;
setIsScheduledSaving(false);
}, []);
const scheduleUpdate = useCallback(
(params: Parameters<typeof api.updateCatalog>[0], delayMs = 3000) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
pendingParamsRef.current = params;
setIsScheduledSaving(true);
timeoutRef.current = setTimeout(() => {
const pendingParams = pendingParamsRef.current;
timeoutRef.current = null;
pendingParamsRef.current = null;
setIsScheduledSaving(false);
if (pendingParams) {
mutation.mutate(pendingParams);
}
}, delayMs);
},
[mutation],
);
useEffect(
() => () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
// اگر کاربر قبل از پایان debounce از ادیتور خارج شد،
// آخرین تغییرات معلق را همان لحظه ذخیره کن.
if (pendingParamsRef.current) {
mutation.mutate(pendingParamsRef.current);
pendingParamsRef.current = null;
}
setIsScheduledSaving(false);
},
[mutation],
);
return {
...mutation,
scheduleUpdate,
cancelPendingUpdate,
isSaving: isScheduledSaving || mutation.isPending,
};
};
-45
View File
@@ -1,45 +0,0 @@
import axios from "@/config/axios";
import type {
CatalogByIdResponseType,
CatalogResponseType,
} from "@/pages/catalogue/types/Types";
import type {
CreateCatalogParamsType,
UpdateCatalogParamsType,
} from "../types/Types";
export const createCatalog = async (params: CreateCatalogParamsType) => {
const { data } = await axios.post("/admin/catalogue", params);
return data;
};
export const getCatalogs = async () => {
const { data } = await axios.get<CatalogResponseType>("/admin/catalogue");
return data;
};
export const getCatalogById = async (id: string) => {
const { data } = await axios.get<CatalogByIdResponseType>(
`/admin/catalogue/${id}`,
);
return data;
};
export const getCatalogBySlug = async (slug: string) => {
const { data } = await axios.get<CatalogByIdResponseType>(
`/public/catalogue/slug/${slug}`,
);
return data;
};
export const updateCatalog = async (params: UpdateCatalogParamsType) => {
const { data } = await axios.patch(`/admin/catalogue/${params.id}`, {
content: params.content,
});
return data;
};
export const deleteCatalogById = async (id: string) => {
const { data } = await axios.delete(`/admin/catalogue/${id}`);
return data;
};
-12
View File
@@ -1,12 +0,0 @@
export type CreateCatalogParamsType = {
name: string;
slug: string;
size: string;
content?: string;
id?: string;
};
export type UpdateCatalogParamsType = {
id: string;
content: string;
};
+1 -1
View File
@@ -3,7 +3,7 @@ import type { DocumentSettings } from "@/pages/editor/store/editorStore";
import {
useGetCatalogById,
useGetCatalogBySlug,
} from "@/pages/home/hooks/useHomeData";
} from "@/pages/catalogue/hooks/useCatalogueData";
import { type FC, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import BookViewer from "./components/BookViewer";