catalogue service , hook
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import GridWrapper from "@/components/GridWrapper";
|
import GridWrapper from "@/components/GridWrapper";
|
||||||
import { usePageTitle } from "@/hooks/usePageTitle";
|
import { usePageTitle } from "@/hooks/usePageTitle";
|
||||||
import { type FC } from "react";
|
import { type FC } from "react";
|
||||||
import { useGetCatalog } from "../home/hooks/useHomeData";
|
import { useGetCatalog } from "./hooks/useCatalogueData";
|
||||||
import ButtonAddCatalogue from "./components/ButtonAddCatalogue";
|
import ButtonAddCatalogue from "./components/ButtonAddCatalogue";
|
||||||
import CatalogueGridSkeleton from "./components/CatalogueGridSkeleton";
|
import CatalogueGridSkeleton from "./components/CatalogueGridSkeleton";
|
||||||
import CatalogueItem from "./components/CatalogueItem";
|
import CatalogueItem from "./components/CatalogueItem";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { toast } from "@/components/Toast";
|
|||||||
import TrashWithConfrim from "@/components/TrashWithConfrim";
|
import TrashWithConfrim from "@/components/TrashWithConfrim";
|
||||||
import { Paths } from "@/config/Paths";
|
import { Paths } from "@/config/Paths";
|
||||||
import { extractErrorMessage } from "@/helpers/utils";
|
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 { requestViewerFullscreen } from "@/pages/viewer/utils/viewerFullscreen";
|
||||||
import { Edit, Eye, Share } from "iconsax-react";
|
import { Edit, Eye, Share } from "iconsax-react";
|
||||||
import moment from "moment-jalaali";
|
import moment from "moment-jalaali";
|
||||||
|
|||||||
@@ -1,6 +1,127 @@
|
|||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import * as api from "../service/CatalogueService";
|
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 = () => {
|
export const usePurchaseInitate = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: api.purchaseInitate,
|
mutationFn: api.purchaseInitate,
|
||||||
|
|||||||
@@ -1,5 +1,47 @@
|
|||||||
import axios from "@/config/axios";
|
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) => {
|
export const purchaseInitate = async (params: PurchaseInitiate) => {
|
||||||
const { data } = await axios.post(
|
const { data } = await axios.post(
|
||||||
|
|||||||
@@ -16,3 +16,16 @@ export type CatalogByIdResponseType = BaseResponse<CatalogItemType>;
|
|||||||
export type PurchaseInitiate = {
|
export type PurchaseInitiate = {
|
||||||
count: number;
|
count: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CreateCatalogParamsType = {
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
size: string;
|
||||||
|
content?: string;
|
||||||
|
id?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateCatalogParamsType = {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import Konva from "konva";
|
|||||||
import { type FC, useEffect, useRef, useState } from "react";
|
import { type FC, useEffect, useRef, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useSharedStore } from "@/shared/store/sharedStore";
|
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 EditorCanvas from "./components/EditorCanvas";
|
||||||
import EditorSidebar from "./components/EditorSidebar";
|
import EditorSidebar from "./components/EditorSidebar";
|
||||||
import LayersPanel from "./components/LayersPanel";
|
import LayersPanel from "./components/LayersPanel";
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import { toast } from "@/components/Toast";
|
|||||||
import { Paths } from "@/config/Paths";
|
import { Paths } from "@/config/Paths";
|
||||||
import { clx, extractErrorMessage } from "@/helpers/utils";
|
import { clx, extractErrorMessage } from "@/helpers/utils";
|
||||||
import { usePageTitle } from "@/hooks/usePageTitle";
|
import { usePageTitle } from "@/hooks/usePageTitle";
|
||||||
|
import { useCreateCatalog } from "@/pages/catalogue/hooks/useCatalogueData";
|
||||||
import { ArrowLeft, DocumentText } from "iconsax-react";
|
import { ArrowLeft, DocumentText } from "iconsax-react";
|
||||||
import { useState, type FC } from "react";
|
import { useState, type FC } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import DirectLogin from "../auth/components/DirectLogin";
|
import DirectLogin from "../auth/components/DirectLogin";
|
||||||
import { useCreateCatalog } from "./hooks/useHomeData";
|
|
||||||
|
|
||||||
const CATALOG_SIZES = [
|
const CATALOG_SIZES = [
|
||||||
{ id: "a4", label: "A4" },
|
{ id: "a4", label: "A4" },
|
||||||
@@ -71,7 +71,7 @@ const Home: FC = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => setActive(id)}
|
onClick={() => setActive(id)}
|
||||||
className={clx(
|
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!",
|
active === id && "border-black!",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
export type CreateCatalogParamsType = {
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
size: string;
|
|
||||||
content?: string;
|
|
||||||
id?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UpdateCatalogParamsType = {
|
|
||||||
id: string;
|
|
||||||
content: string;
|
|
||||||
};
|
|
||||||
@@ -3,7 +3,7 @@ import type { DocumentSettings } from "@/pages/editor/store/editorStore";
|
|||||||
import {
|
import {
|
||||||
useGetCatalogById,
|
useGetCatalogById,
|
||||||
useGetCatalogBySlug,
|
useGetCatalogBySlug,
|
||||||
} from "@/pages/home/hooks/useHomeData";
|
} from "@/pages/catalogue/hooks/useCatalogueData";
|
||||||
import { type FC, useEffect, useState } from "react";
|
import { type FC, useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import BookViewer from "./components/BookViewer";
|
import BookViewer from "./components/BookViewer";
|
||||||
|
|||||||
Reference in New Issue
Block a user