Set background

This commit is contained in:
hamid zarghami
2026-06-21 10:06:56 +03:30
parent 5638f91c81
commit f639cbd3e6
@@ -1,17 +1,26 @@
import Button from "@/components/Button";
import ColorPicker from "@/components/ColorPicker"; import ColorPicker from "@/components/ColorPicker";
import Radio from "@/components/Radio"; import Radio from "@/components/Radio";
import { extractErrorMessage } from "@/config/func";
import type { ErrorType } from "@/helpers/types";
import { clx } from "@/helpers/utils"; import { clx } from "@/helpers/utils";
import DesignSlider from "@/pages/settings/components/DesignSlider"; import DesignSlider from "@/pages/settings/components/DesignSlider";
import { PATTERN_BACKGROUND_OPACITY, useBackgroundPatterns } from "@/pages/settings/hooks/usePatterns"; import { PATTERN_BACKGROUND_OPACITY, useBackgroundPatterns } from "@/pages/settings/hooks/usePatterns";
import { DocumentUpload, Gallery, Trash } from "iconsax-react"; import { useGetBackgrounds, useSetBackground } from "@/pages/settings/hooks/useSettingData";
import { useCallback, useEffect, useState, type FC } from "react"; import type { BackgroundItemType } from "@/pages/settings/types/Types";
import { useSingleUpload } from "@/pages/uploader/hooks/useUploaderData";
import { DocumentUpload, Gallery, TickCircle, Trash } from "iconsax-react";
import { useCallback, useEffect, useMemo, useState, type FC } from "react";
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
import { toast } from "react-toastify";
const COLOR_APPLY_DELAY_MS = 500; const COLOR_APPLY_DELAY_MS = 500;
type BackgroundType = "pattern" | "custom"; type BackgroundType = "pattern" | "custom";
const UPLOAD_BOX_CLASS = "w-[200px] h-[362px] rounded-2xl border border-dashed border-description"; const UPLOAD_BOX_CLASS = "w-[200px] h-[362px] rounded-2xl border border-dashed border-description";
const getPatternBgUrl = (item: BackgroundItemType) => item.url ?? item.bgUrl ?? "";
const DesignSettings: FC = () => { const DesignSettings: FC = () => {
const [menuColor, setMenuColor] = useState("#000000"); const [menuColor, setMenuColor] = useState("#000000");
const [appliedColor, setAppliedColor] = useState("#000000"); const [appliedColor, setAppliedColor] = useState("#000000");
@@ -22,7 +31,12 @@ const DesignSettings: FC = () => {
const [imageOpacity, setImageOpacity] = useState(100); const [imageOpacity, setImageOpacity] = useState(100);
const [imageBlur, setImageBlur] = useState(0); const [imageBlur, setImageBlur] = useState(0);
const [overlayDarkness, setOverlayDarkness] = useState(0); const [overlayDarkness, setOverlayDarkness] = useState(0);
const { data: backgrounds } = useGetBackgrounds();
const { mutate: setBackground, isPending: isSavingBackground } = useSetBackground();
const { mutate: singleUpload, isPending: isUploadingImage } = useSingleUpload();
const { patterns, isLoading: isPatternsLoading, isError: isPatternsError } = useBackgroundPatterns(appliedColor); const { patterns, isLoading: isPatternsLoading, isError: isPatternsError } = useBackgroundPatterns(appliedColor);
const backgroundItems = useMemo(() => backgrounds?.data ?? [], [backgrounds]);
const isSaving = isSavingBackground || isUploadingImage;
useEffect(() => { useEffect(() => {
if (patterns[0]?.id) { if (patterns[0]?.id) {
@@ -66,6 +80,65 @@ const DesignSettings: FC = () => {
setOverlayDarkness(0); setOverlayDarkness(0);
}; };
const handleApplyChanges = () => {
if (backgroundType === "pattern") {
const selectedItem = backgroundItems.find((item) => item.id === selectedPatternId);
const bgUrl = selectedItem ? getPatternBgUrl(selectedItem) : "";
if (!bgUrl) {
toast.error("لطفاً یک پترن انتخاب کنید");
return;
}
setBackground(
{ bgUrl },
{
onSuccess: () => toast.success("تنظیمات طراحی با موفقیت ذخیره شد"),
onError: (error: unknown) => {
toast.error(extractErrorMessage(error as ErrorType, "خطا در ذخیره تنظیمات طراحی"));
},
},
);
return;
}
if (!customImage) {
toast.error("لطفاً تصویر پس‌زمینه را آپلود کنید");
return;
}
singleUpload(customImage, {
onSuccess: (response) => {
const bgUrl = response?.data?.url ?? "";
if (!bgUrl) {
toast.error("خطا در آپلود تصویر");
return;
}
setBackground(
{
bgUrl,
bgOpacity: imageOpacity,
bgBlur: imageBlur,
bgOverlay: String(overlayDarkness),
},
{
onSuccess: () => toast.success("تنظیمات طراحی با موفقیت ذخیره شد"),
onError: (error: unknown) => {
toast.error(extractErrorMessage(error as ErrorType, "خطا در ذخیره تنظیمات طراحی"));
},
},
);
},
onError: (error: ErrorType) => {
toast.error(extractErrorMessage(error, "خطا در آپلود تصویر"));
},
});
};
const isApplyDisabled = isSaving || (backgroundType === "pattern" ? !selectedPatternId : !customImage);
return ( return (
<div className="bg-white rounded-4xl p-8"> <div className="bg-white rounded-4xl p-8">
<div className="text-lg font-light">تنظیمات طراحی</div> <div className="text-lg font-light">تنظیمات طراحی</div>
@@ -82,16 +155,9 @@ const DesignSettings: FC = () => {
{backgroundType === "pattern" && ( {backgroundType === "pattern" && (
<div className="mt-2 flex gap-3 flex-wrap"> <div className="mt-2 flex gap-3 flex-wrap">
{isPatternsLoading && {isPatternsLoading && Array.from({ length: 6 }).map((_, index) => <div key={index} className="w-16 h-[139px] rounded-lg bg-secondary animate-pulse" />)}
Array.from({ length: 6 }).map((_, index) => ( {!isPatternsLoading && isPatternsError && <div className="text-sm text-description">خطا در بارگذاری پترنها</div>}
<div key={index} className="w-16 h-[139px] rounded-lg bg-secondary animate-pulse" /> {!isPatternsLoading && !isPatternsError && patterns.length === 0 && <div className="text-sm text-description">پترنی یافت نشد</div>}
))}
{!isPatternsLoading && isPatternsError && (
<div className="text-sm text-description">خطا در بارگذاری پترنها</div>
)}
{!isPatternsLoading && !isPatternsError && patterns.length === 0 && (
<div className="text-sm text-description">پترنی یافت نشد</div>
)}
{patterns.map((pattern) => ( {patterns.map((pattern) => (
<button <button
key={pattern.id} key={pattern.id}
@@ -158,6 +224,14 @@ const DesignSettings: FC = () => {
</div> </div>
)} )}
</div> </div>
<div className="mt-8 flex justify-end">
<Button type="button" className="w-fit px-6" isloading={isSaving} disabled={isApplyDisabled} onClick={handleApplyChanges}>
<div className="flex gap-2 items-center">
<TickCircle size={20} color="#fff" />
<span>اعمال تغییرات</span>
</div>
</Button>
</div>
</div> </div>
</div> </div>
</div> </div>