diff --git a/client/package-lock.json b/client/package-lock.json index 3efb283..ac1555d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -119,7 +119,6 @@ "version": "7.29.7", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -419,7 +418,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -441,7 +439,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -499,7 +496,6 @@ "node_modules/@dicebear/core": { "version": "9.4.2", "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -617,6 +613,29 @@ "@dicebear/core": "^9.0.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -674,7 +693,6 @@ "node_modules/@emotion/react": { "version": "11.14.0", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -712,7 +730,6 @@ "node_modules/@emotion/styled": { "version": "11.14.1", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1333,6 +1350,17 @@ "@floating-ui/utils": "^0.2.11" } }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, "node_modules/@floating-ui/utils": { "version": "0.2.11", "license": "MIT", @@ -1551,7 +1579,6 @@ "resolved": "https://registry.npmjs.org/@mui/material/-/material-9.0.1.tgz", "integrity": "sha512-voyCpeUxcSWLN7KPZuq0pGCIt726T9K6kiVM3XUcywZDAlZSarLHaUxJVQpospbjjOzN53hwyjo8s6KoWl6utw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.29.2", "@mui/core-downloads-tracker": "^9.0.1", @@ -2440,6 +2467,7 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -2520,7 +2548,6 @@ "node_modules/@tiptap/core": { "version": "3.23.6", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2711,7 +2738,6 @@ "node_modules/@tiptap/extension-list": { "version": "3.23.6", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2812,7 +2838,6 @@ "node_modules/@tiptap/extensions": { "version": "3.23.6", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2825,7 +2850,6 @@ "node_modules/@tiptap/pm": { "version": "3.23.6", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-commands": "^1.6.2", @@ -2918,7 +2942,8 @@ "node_modules/@types/aria-query": { "version": "5.0.4", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/chai": { "version": "5.2.3", @@ -2951,7 +2976,6 @@ "version": "24.12.4", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2967,7 +2991,6 @@ "node_modules/@types/react": { "version": "19.2.15", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2975,7 +2998,6 @@ "node_modules/@types/react-dom": { "version": "19.2.3", "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3002,7 +3024,6 @@ "version": "8.59.4", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.59.4", "@typescript-eslint/types": "8.59.4", @@ -3171,7 +3192,6 @@ "version": "8.59.4", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.4", @@ -3684,7 +3704,6 @@ "version": "8.16.0", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3729,6 +3748,7 @@ "version": "5.0.1", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -4839,6 +4859,7 @@ "version": "2.0.3", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -4865,7 +4886,8 @@ "node_modules/dom-accessibility-api": { "version": "0.5.16", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dom-helpers": { "version": "5.2.1", @@ -4904,7 +4926,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=11.0.0" } @@ -5130,7 +5151,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -5190,7 +5210,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5249,7 +5268,6 @@ "version": "10.1.8", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -5381,7 +5399,6 @@ "version": "4.16.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@package-json/types": "^0.0.12", "@typescript-eslint/types": "^8.56.0", @@ -7167,6 +7184,7 @@ "version": "1.5.0", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -7182,7 +7200,6 @@ "node_modules/maplibre-gl": { "version": "5.24.0", "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/point-geometry": "^1.1.0", @@ -7740,7 +7757,6 @@ "version": "3.8.3", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7766,6 +7782,7 @@ "version": "27.5.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -7779,6 +7796,7 @@ "version": "5.2.0", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -7789,7 +7807,8 @@ "node_modules/pretty-format/node_modules/react-is": { "version": "17.0.2", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/prop-types": { "version": "15.8.1", @@ -7935,7 +7954,6 @@ "node_modules/react": { "version": "19.2.6", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7943,7 +7961,6 @@ "node_modules/react-dom": { "version": "19.2.6", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -9127,7 +9144,6 @@ "version": "4.0.4", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9318,7 +9334,6 @@ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9530,7 +9545,6 @@ "version": "8.0.14", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -9644,7 +9658,6 @@ "version": "4.0.4", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9884,7 +9897,6 @@ "version": "4.0.4", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9896,7 +9908,6 @@ "version": "7.3.3", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -10198,7 +10209,6 @@ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/client/src/features/product-form/ui/GalleryImagePicker.tsx b/client/src/features/product-form/ui/GalleryImagePicker.tsx index 572a089..9fee2fe 100644 --- a/client/src/features/product-form/ui/GalleryImagePicker.tsx +++ b/client/src/features/product-form/ui/GalleryImagePicker.tsx @@ -59,7 +59,7 @@ export function GalleryImagePicker({ return ( Изображения из галереи - + {galleryQuery.isLoading && Загрузка списка…} {galleryQuery.isError && Не удалось загрузить галерею. Попробуйте ещё раз.} {galleryQuery.data?.items.length === 0 && !galleryQuery.isLoading && ( diff --git a/client/src/pages/admin-gallery/ui/AdminGalleryPage.tsx b/client/src/pages/admin-gallery/ui/AdminGalleryPage.tsx index b77ebac..1c7ffe1 100644 --- a/client/src/pages/admin-gallery/ui/AdminGalleryPage.tsx +++ b/client/src/pages/admin-gallery/ui/AdminGalleryPage.tsx @@ -13,6 +13,7 @@ import ToggleButton from '@mui/material/ToggleButton' import ToggleButtonGroup from '@mui/material/ToggleButtonGroup' import Typography from '@mui/material/Typography' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { Grid3x3, List } from 'lucide-react' import { deleteGalleryImage, fetchAdminGallery, @@ -24,7 +25,6 @@ import type { GalleryImageItem } from '@/entities/gallery' import { formatAdminImageMaxSizeHint } from '@/shared/constants/upload-limits' import { invalidateQueryKeys } from '@/shared/lib/invalidate-query-keys' import type { AxiosError } from 'axios' -import { Grid3x3, List } from 'lucide-react' function getApiErrorMessage(error: unknown): string | null { const e = error as AxiosError<{ error?: string }> @@ -197,12 +197,7 @@ export function AdminGalleryPage() { }} /> - v && setViewMode(v)} - size="small" - > + v && setViewMode(v)} size="small"> diff --git a/client/src/pages/admin-slider/ui/AdminSliderPage.tsx b/client/src/pages/admin-slider/ui/AdminSliderPage.tsx index 1ac2b83..b6d4163 100644 --- a/client/src/pages/admin-slider/ui/AdminSliderPage.tsx +++ b/client/src/pages/admin-slider/ui/AdminSliderPage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useMemo, useState } from 'react' import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward' import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward' import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined' @@ -21,36 +21,17 @@ import { invalidateQueryKeys } from '@/shared/lib/invalidate-query-keys' type SlideDraft = { galleryImageId: string; caption: string; textColor: string } -export function AdminSliderPage() { +function SliderEditor({ + initialSlides, + galleryItems, +}: { + initialSlides: SlideDraft[] + galleryItems: GalleryImageItem[] +}) { const queryClient = useQueryClient() - - const sliderQuery = useQuery({ - queryKey: ['admin', 'catalog-slider'], - queryFn: fetchAdminCatalogSlider, - }) - - const galleryQuery = useQuery({ - queryKey: ['admin', 'gallery'], - queryFn: fetchAdminGallery, - }) - - const [sliderDraft, setSliderDraft] = useState([]) + const [sliderDraft, setSliderDraft] = useState(initialSlides) const [pickOpen, setPickOpen] = useState(false) - useEffect(() => { - if (sliderQuery.isSuccess && sliderDraft.length === 0) { - setSliderDraft( - sliderQuery.data.slides.map((s) => ({ - galleryImageId: s.galleryImageId, - caption: s.caption, - textColor: s.textColor || '#ffffff', - })), - ) - } - }, [sliderQuery.isSuccess, sliderQuery.dataUpdatedAt]) - - const galleryItems: GalleryImageItem[] = galleryQuery.data?.items ?? [] - const usedIds = new Set(sliderDraft.map((s) => s.galleryImageId)) const pickCandidates = galleryItems.filter((i) => !usedIds.has(i.id) && i.isResized) @@ -88,24 +69,8 @@ export function AdminSliderPage() { }) } - if (sliderQuery.isLoading || galleryQuery.isLoading) { - return Загрузка… - } - - if (sliderQuery.isError) { - return Не удалось загрузить слайдер. - } - return ( - - - Слайдер - - - Изображения для карусели на главной странице. Сначала загрузите фото в Галерею и обработайте их (Resize), затем - добавьте в слайдер. Порядок строк = порядок показа. - - + <> {sliderDraft.length === 0 && ( @@ -236,6 +201,51 @@ export function AdminSliderPage() { + + ) +} + +export function AdminSliderPage() { + const sliderQuery = useQuery({ + queryKey: ['admin', 'catalog-slider'], + queryFn: fetchAdminCatalogSlider, + }) + + const galleryQuery = useQuery({ + queryKey: ['admin', 'gallery'], + queryFn: fetchAdminGallery, + }) + + const galleryItems: GalleryImageItem[] = galleryQuery.data?.items ?? [] + + const initialSlides = useMemo(() => { + if (!sliderQuery.isSuccess) return [] + return sliderQuery.data.slides.map((s) => ({ + galleryImageId: s.galleryImageId, + caption: s.caption, + textColor: s.textColor || '#ffffff', + })) + }, [sliderQuery.isSuccess, sliderQuery.data?.slides]) + + if (sliderQuery.isLoading || galleryQuery.isLoading) { + return Загрузка… + } + + if (sliderQuery.isError) { + return Не удалось загрузить слайдер. + } + + return ( + + + Слайдер + + + Изображения для карусели на главной странице. Сначала загрузите фото в Галерею и обработайте их (Resize), затем + добавьте в слайдер. Порядок строк = порядок показа. + + + ) } diff --git a/client/src/pages/auth/ui/AuthPage.tsx b/client/src/pages/auth/ui/AuthPage.tsx index 73d2221..d7b679a 100644 --- a/client/src/pages/auth/ui/AuthPage.tsx +++ b/client/src/pages/auth/ui/AuthPage.tsx @@ -13,12 +13,24 @@ import { AuthForgotForm } from '@/features/auth-forgot' import { OAuthButtons } from '@/features/auth-oauth' import { AuthPasswordForm } from '@/features/auth-password' import { $user } from '@/shared/model/auth' -import { useThemeController } from '@/app/providers/theme-controller' +import type { ColorScheme } from '@/shared/model/theme' import { BearLogo } from '@/shared/ui/BearLogo' +function readStoredScheme(): ColorScheme { + try { + const raw = localStorage.getItem('craftshop_theme') + if (!raw) return 'craft' + const parsed = JSON.parse(raw) + const scheme = parsed?.scheme + return scheme === 'forest' || scheme === 'ocean' || scheme === 'berry' ? scheme : 'craft' + } catch { + return 'craft' + } +} + export function AuthPage() { const theme = useTheme() - const { scheme } = useThemeController() + const scheme = readStoredScheme() const [message, setMessage] = useState(null) const [oauthError, setOauthError] = useState(null) const [tab, setTab] = useState(0) @@ -40,7 +52,7 @@ export function AuthPage() { setSearchParams({}, { replace: true }) }, 0) return () => clearTimeout(timeoutId) - }, []) + }, [searchParams, setSearchParams]) if (showForgot) { return (