get pattenrs from api
This commit is contained in:
@@ -2,7 +2,7 @@ import ColorPicker from "@/components/ColorPicker";
|
|||||||
import Radio from "@/components/Radio";
|
import Radio from "@/components/Radio";
|
||||||
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, usePatterns } from "@/pages/settings/hooks/usePatterns";
|
import { PATTERN_BACKGROUND_OPACITY, useBackgroundPatterns } from "@/pages/settings/hooks/usePatterns";
|
||||||
import { DocumentUpload, Gallery, Trash } from "iconsax-react";
|
import { DocumentUpload, Gallery, Trash } from "iconsax-react";
|
||||||
import { useCallback, useEffect, useState, type FC } from "react";
|
import { useCallback, useEffect, useState, type FC } from "react";
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
@@ -15,14 +15,20 @@ const UPLOAD_BOX_CLASS = "w-[200px] h-[362px] rounded-2xl border border-dashed b
|
|||||||
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");
|
||||||
const [selectedPatternId, setSelectedPatternId] = useState(1);
|
const [selectedPatternId, setSelectedPatternId] = useState<string | null>(null);
|
||||||
const [backgroundType, setBackgroundType] = useState<BackgroundType>("pattern");
|
const [backgroundType, setBackgroundType] = useState<BackgroundType>("pattern");
|
||||||
const [customImage, setCustomImage] = useState<File | null>(null);
|
const [customImage, setCustomImage] = useState<File | null>(null);
|
||||||
const [customImagePreview, setCustomImagePreview] = useState<string | null>(null);
|
const [customImagePreview, setCustomImagePreview] = useState<string | null>(null);
|
||||||
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 patterns = usePatterns(appliedColor);
|
const { patterns, isLoading: isPatternsLoading, isError: isPatternsError } = useBackgroundPatterns(appliedColor);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (patterns[0]?.id) {
|
||||||
|
setSelectedPatternId((current) => current ?? patterns[0].id);
|
||||||
|
}
|
||||||
|
}, [patterns]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => setAppliedColor(menuColor), COLOR_APPLY_DELAY_MS);
|
const timer = setTimeout(() => setAppliedColor(menuColor), COLOR_APPLY_DELAY_MS);
|
||||||
@@ -76,6 +82,16 @@ 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 &&
|
||||||
|
Array.from({ length: 6 }).map((_, index) => (
|
||||||
|
<div key={index} className="w-16 h-[139px] rounded-lg bg-secondary animate-pulse" />
|
||||||
|
))}
|
||||||
|
{!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}
|
||||||
@@ -84,7 +100,7 @@ const DesignSettings: FC = () => {
|
|||||||
className={clx("relative w-16 h-[139px] rounded-lg bg-white border overflow-hidden", selectedPatternId === pattern.id ? "border-black" : "border-border")}
|
className={clx("relative w-16 h-[139px] rounded-lg bg-white border overflow-hidden", selectedPatternId === pattern.id ? "border-black" : "border-border")}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0" style={{ backgroundColor: appliedColor, opacity: PATTERN_BACKGROUND_OPACITY }} />
|
<div className="absolute inset-0" style={{ backgroundColor: appliedColor, opacity: PATTERN_BACKGROUND_OPACITY }} />
|
||||||
<img src={pattern.image} alt={pattern.name} className="relative scale-200 w-full h-full object-cover" />
|
<img src={pattern.image} alt="" className="relative scale-200 w-full h-full object-cover" />
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { useMemo } from "react";
|
import type { BackgroundItemType } from "@/pages/settings/types/Types";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { useGetBackgrounds } from "./useSettingData";
|
||||||
import firstPattern from "@/assets/images/pattern/1.svg?raw";
|
import firstPattern from "@/assets/images/pattern/1.svg?raw";
|
||||||
import secondPattern from "@/assets/images/pattern/2.svg?raw";
|
import secondPattern from "@/assets/images/pattern/2.svg?raw";
|
||||||
import thirdPattern from "@/assets/images/pattern/3.svg?raw";
|
import thirdPattern from "@/assets/images/pattern/3.svg?raw";
|
||||||
@@ -18,7 +20,14 @@ const PATTERNS = [
|
|||||||
export const PATTERN_BACKGROUND_OPACITY = 0.05;
|
export const PATTERN_BACKGROUND_OPACITY = 0.05;
|
||||||
export const PATTERN_VECTOR_OPACITY = 0.1;
|
export const PATTERN_VECTOR_OPACITY = 0.1;
|
||||||
|
|
||||||
const colorizeSvg = (svg: string, color: string) => {
|
export const getBackgroundPatternUrl = (item: BackgroundItemType) => {
|
||||||
|
const url = item.url ?? item.bgUrl ?? "";
|
||||||
|
if (!url) return "";
|
||||||
|
if (url.startsWith("http")) return url;
|
||||||
|
return `${import.meta.env.VITE_BASE_URL}${url.startsWith("/") ? url : `/${url}`}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const colorizeSvg = (svg: string, color: string) => {
|
||||||
let result = svg.replace(/fill="(?!black|none)[^"]+"/g, `fill="${color}"`);
|
let result = svg.replace(/fill="(?!black|none)[^"]+"/g, `fill="${color}"`);
|
||||||
result = result.replace(/fill-opacity="[^"]+"/g, `fill-opacity="${PATTERN_VECTOR_OPACITY}"`);
|
result = result.replace(/fill-opacity="[^"]+"/g, `fill-opacity="${PATTERN_VECTOR_OPACITY}"`);
|
||||||
result = result.replace(
|
result = result.replace(
|
||||||
@@ -29,9 +38,84 @@ const colorizeSvg = (svg: string, color: string) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const svgToDataUrl = (svg: string) =>
|
export const svgToDataUrl = (svg: string) =>
|
||||||
`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
||||||
|
|
||||||
|
type CachedPatternSource = {
|
||||||
|
id: string;
|
||||||
|
svg?: string;
|
||||||
|
fallbackUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchPatternSource = async (item: BackgroundItemType): Promise<CachedPatternSource | null> => {
|
||||||
|
const url = getBackgroundPatternUrl(item);
|
||||||
|
if (!url) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
return { id: item.id, fallbackUrl: url };
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await response.text();
|
||||||
|
if (!content.includes("<svg")) {
|
||||||
|
return { id: item.id, fallbackUrl: url };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id: item.id, svg: content };
|
||||||
|
} catch {
|
||||||
|
return { id: item.id, fallbackUrl: url };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BackgroundPattern = {
|
||||||
|
id: string;
|
||||||
|
image: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBackgroundPatterns = (color: string) => {
|
||||||
|
const { data: backgrounds, isLoading, isError } = useGetBackgrounds();
|
||||||
|
const [sources, setSources] = useState<CachedPatternSource[]>([]);
|
||||||
|
const [isFetching, setIsFetching] = useState(false);
|
||||||
|
const backgroundItems = useMemo(() => backgrounds?.data ?? [], [backgrounds]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!backgroundItems.length) {
|
||||||
|
setSources([]);
|
||||||
|
setIsFetching(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
setIsFetching(true);
|
||||||
|
|
||||||
|
Promise.all(backgroundItems.map(fetchPatternSource)).then((results) => {
|
||||||
|
if (cancelled) return;
|
||||||
|
setSources(results.filter((item): item is CachedPatternSource => item !== null));
|
||||||
|
setIsFetching(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [backgroundItems]);
|
||||||
|
|
||||||
|
const patterns = useMemo(
|
||||||
|
() =>
|
||||||
|
sources.map((source) => ({
|
||||||
|
id: source.id,
|
||||||
|
image: source.svg ? svgToDataUrl(colorizeSvg(source.svg, color)) : (source.fallbackUrl ?? ""),
|
||||||
|
})),
|
||||||
|
[sources, color],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
patterns,
|
||||||
|
isLoading: isLoading || isFetching,
|
||||||
|
isError,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const usePatterns = (color: string) => {
|
export const usePatterns = (color: string) => {
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -13,3 +13,16 @@ export const useUpdateRestaurant = () => {
|
|||||||
mutationFn: api.updateRestaurant,
|
mutationFn: api.updateRestaurant,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetBackgrounds = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["backgrounds"],
|
||||||
|
queryFn: api.getBackgrounds,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetBackground = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.setBackground,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import type {
|
import type { GetBackgroundsResponse, GetRestaurantResponse, SetBackgroundType, UpdateRestaurantType } from "../types/Types";
|
||||||
GetRestaurantResponse,
|
|
||||||
UpdateRestaurantType,
|
|
||||||
} from "../types/Types";
|
|
||||||
|
|
||||||
export const getRestaurant = async (): Promise<GetRestaurantResponse> => {
|
export const getRestaurant = async (): Promise<GetRestaurantResponse> => {
|
||||||
const { data } = await axios.get<GetRestaurantResponse>(
|
const { data } = await axios.get<GetRestaurantResponse>("/admin/restaurants/my-restaurant");
|
||||||
"/admin/restaurants/my-restaurant"
|
|
||||||
);
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateRestaurant = async (params: UpdateRestaurantType) => {
|
export const updateRestaurant = async (params: UpdateRestaurantType) => {
|
||||||
const { data } = await axios.patch(
|
const { data } = await axios.patch("/admin/restaurants/my-restaurant", params);
|
||||||
"/admin/restaurants/my-restaurant",
|
return data;
|
||||||
params
|
};
|
||||||
);
|
|
||||||
|
export const getBackgrounds = async () => {
|
||||||
|
const { data } = await axios.get<GetBackgroundsResponse>(`/admin/restaurants/background`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setBackground = async (params: SetBackgroundType) => {
|
||||||
|
const { data } = await axios.patch(`/admin/restaurants/my-restaurant/background`, params);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -70,3 +70,21 @@ export type Restaurant = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type GetRestaurantResponse = IResponse<Restaurant>;
|
export type GetRestaurantResponse = IResponse<Restaurant>;
|
||||||
|
|
||||||
|
export type SetBackgroundType = {
|
||||||
|
bgUrl: string;
|
||||||
|
bgOpacity?: number;
|
||||||
|
bgBlur?: number;
|
||||||
|
bgOverlay?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BackgroundItemType = {
|
||||||
|
createdAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
id: string;
|
||||||
|
updatedAt: string;
|
||||||
|
url?: string;
|
||||||
|
bgUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetBackgroundsResponse = IResponse<BackgroundItemType[]>;
|
||||||
|
|||||||
Reference in New Issue
Block a user