diff --git a/CSS_FIX_GUIDE.md b/CSS_FIX_GUIDE.md deleted file mode 100644 index a2e5b79..0000000 --- a/CSS_FIX_GUIDE.md +++ /dev/null @@ -1,127 +0,0 @@ -# 🎨 Руководство по исправлению проблем со стилями CMS в Docker - -## 🔍 Проблема -Стили Tailwind CSS не загружаются в Docker контейнере, но работают в локальной разработке. - -## ✅ Решение - -### 1. Исправленные файлы: - -#### `src/app/globals.css` -```css -@import "tailwindcss/preflight"; -@import "tailwindcss"; -``` -**Вместо:** `@import "tailwindcss";` и `@import "tw-animate-css";` - -#### `next.config.ts` -```typescript -const nextConfig = { - output: 'standalone', - - // Исключаем favicon из обработки как страницу - async rewrites() { - return []; - }, - - // CSS настройки (optimizeCss отключен из-за проблем с critters) - // experimental: { - // optimizeCss: true, - // }, - - // Настройки для изображений - images: { - unoptimized: true, - domains: ['localhost'], - }, - - // Настройки webpack для CSS - webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => { - if (!dev && !isServer) { - config.optimization.splitChunks.cacheGroups.styles = { - name: 'styles', - test: /\.(css|scss)$/, - chunks: 'all', - enforce: true, - }; - } - return config; - }, -}; -``` - -### 2. Используйте оптимизированный Dockerfile: - -#### `Dockerfile.optimized` -- Multi-stage build для лучшей оптимизации -- Правильная обработка CSS на этапе сборки -- Проверка создания CSS файлов - -### 3. Команды для пересборки: - -```bash -# Остановка и полная пересборка -cd protekauto-cms -./rebuild-cms.sh - -# Или вручную: -docker-compose down protekauto-cms -docker rmi protekauto-cms_protekauto-cms -docker-compose up --build -d protekauto-cms -``` - -## 🔧 Диагностика - -### Проверка локальной сборки: -```bash -npm run build -ls -la .next/static/css/ -``` - -### Проверка в контейнере: -```bash -docker exec -it protekauto-cms ls -la .next/static/css/ -``` - -## 🚨 Частые проблемы - -### 1. **Tailwind CSS v4 синтаксис** -- ❌ `@tailwind base;` -- ✅ `@import "tailwindcss/preflight";` - -### 2. **Экспериментальные функции** -- ❌ `optimizeCss: true` (требует critters) -- ✅ Отключено для стабильности - -### 3. **Standalone режим** -- ✅ Включен для Docker оптимизации -- Может вызывать проблемы при сборке - -### 4. **CSS не загружается в браузере** -- Проверьте Network tab в DevTools -- Убедитесь, что CSS файлы доступны по пути `/_next/static/css/` - -## 📋 Чек-лист исправления - -- [ ] Обновлен `globals.css` с правильными импортами -- [ ] Обновлен `next.config.ts` с правильными настройками -- [ ] Отключен `optimizeCss` (если есть проблемы с critters) -- [ ] Используется `Dockerfile.optimized` -- [ ] Локальная сборка проходит успешно -- [ ] CSS файлы создаются в `.next/static/css/` -- [ ] Контейнер пересобран с новыми настройками - -## 🎯 Результат - -После применения исправлений: -1. ✅ Локальная сборка проходит без ошибок -2. ✅ CSS файлы создаются корректно -3. ✅ Стили загружаются в Docker контейнере -4. ✅ Админка отображается с правильными стилями - -## 📞 Поддержка - -Если проблема не решена: -1. Проверьте логи контейнера: `docker-compose logs protekauto-cms` -2. Убедитесь, что все зависимости установлены -3. Попробуйте полную очистку: `docker system prune -f` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bffb7ce..9660502 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-tabs": "^1.1.12", - "@tailwindcss/postcss": "^4", + "@tailwindcss/postcss": "^4.1.11", "@types/bcryptjs": "^2.4.6", "@types/js-cookie": "^3.0.6", "@types/jsonwebtoken": "^9.0.9", @@ -55,6 +55,7 @@ "lucide-react": "^0.513.0", "next": "15.3.3", "pdfkit": "^0.17.1", + "postcss": "^8.5.6", "prisma": "^6.9.0", "puppeteer": "^24.10.2", "qrcode": "^1.5.4", @@ -63,8 +64,8 @@ "react-hook-form": "^7.57.0", "react-hot-toast": "^2.5.2", "sonner": "^2.0.5", - "tailwind-merge": "^3.3.0", - "tailwindcss": "^4", + "tailwind-merge": "^3.3.1", + "tailwindcss": "^4.1.11", "tailwindcss-animate": "^1.0.7", "ttf2woff2": "^8.0.0", "uuid": "^11.1.0", @@ -2271,17 +2272,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.11.tgz", + "integrity": "sha512-C512c1ytBTio4MrpWKlJpyFHT6+qfFL8SZ58zBzJ1OOzUEjHeF1BtjY2fH7n4x/g2OV/KiiMLAivOp1DXmiMMw==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -2293,25 +2290,16 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.3.tgz", + "integrity": "sha512-AiR5uKpFxP3PjO4R19kQGIMwxyRyPuXmKEEy301V1C0+1rVjS94EZQXf1QKZYN8Q0YM+estSPhmx5JwNftv6nw==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.28", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.28.tgz", + "integrity": "sha512-KNNHHwW3EIp4EDYOvYFGyIFfx36R2dNJYH4knnZlF8T5jdbD5Wx8xmSaQ2gP9URkJ04LGEtlcCtwArKcmFcwKw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -4426,9 +4414,9 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.8.tgz", - "integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -4437,13 +4425,13 @@ "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.8" + "tailwindcss": "4.1.11" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.8.tgz", - "integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -4454,24 +4442,24 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.8", - "@tailwindcss/oxide-darwin-arm64": "4.1.8", - "@tailwindcss/oxide-darwin-x64": "4.1.8", - "@tailwindcss/oxide-freebsd-x64": "4.1.8", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", - "@tailwindcss/oxide-linux-x64-musl": "4.1.8", - "@tailwindcss/oxide-wasm32-wasi": "4.1.8", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.8.tgz", - "integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", "cpu": [ "arm64" ], @@ -4485,9 +4473,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.8.tgz", - "integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", "cpu": [ "arm64" ], @@ -4501,9 +4489,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.8.tgz", - "integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", "cpu": [ "x64" ], @@ -4517,9 +4505,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.8.tgz", - "integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", "cpu": [ "x64" ], @@ -4533,9 +4521,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.8.tgz", - "integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", "cpu": [ "arm" ], @@ -4549,9 +4537,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.8.tgz", - "integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", "cpu": [ "arm64" ], @@ -4565,9 +4553,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.8.tgz", - "integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", "cpu": [ "arm64" ], @@ -4581,9 +4569,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.8.tgz", - "integrity": "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", "cpu": [ "x64" ], @@ -4597,9 +4585,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.8.tgz", - "integrity": "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", "cpu": [ "x64" ], @@ -4613,9 +4601,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz", - "integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -4633,7 +4621,7 @@ "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.10", + "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, @@ -4642,9 +4630,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz", - "integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", "cpu": [ "arm64" ], @@ -4658,9 +4646,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz", - "integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", "cpu": [ "x64" ], @@ -4674,16 +4662,16 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.8.tgz", - "integrity": "sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", + "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.8", - "@tailwindcss/oxide": "4.1.8", + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", "postcss": "^8.4.41", - "tailwindcss": "4.1.8" + "tailwindcss": "4.1.11" } }, "node_modules/@tootallnate/quickjs-emscripten": { @@ -7044,9 +7032,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -10822,9 +10810,9 @@ } }, "node_modules/postcss": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", - "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -12339,9 +12327,9 @@ } }, "node_modules/tailwind-merge": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.0.tgz", - "integrity": "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", "license": "MIT", "funding": { "type": "github", @@ -12349,9 +12337,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.8.tgz", - "integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", "license": "MIT" }, "node_modules/tailwindcss-animate": { diff --git a/package.json b/package.json index 15114c8..c873c34 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-tabs": "^1.1.12", - "@tailwindcss/postcss": "^4", + "@tailwindcss/postcss": "^4.1.11", "@types/bcryptjs": "^2.4.6", "@types/js-cookie": "^3.0.6", "@types/jsonwebtoken": "^9.0.9", @@ -65,6 +65,7 @@ "lucide-react": "^0.513.0", "next": "15.3.3", "pdfkit": "^0.17.1", + "postcss": "^8.5.6", "prisma": "^6.9.0", "puppeteer": "^24.10.2", "qrcode": "^1.5.4", @@ -73,8 +74,8 @@ "react-hook-form": "^7.57.0", "react-hot-toast": "^2.5.2", "sonner": "^2.0.5", - "tailwind-merge": "^3.3.0", - "tailwindcss": "^4", + "tailwind-merge": "^3.3.1", + "tailwindcss": "^4.1.11", "tailwindcss-animate": "^1.0.7", "ttf2woff2": "^8.0.0", "uuid": "^11.1.0", diff --git a/postcss.config.mjs b/postcss.config.mjs index 4c12660..25ae08f 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,6 +1,7 @@ -/** @type {import('postcss-load-config').Config} */ -export default { - plugins: { - '@tailwindcss/postcss': {}, - }, -}; +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, + }; + export default config; + \ No newline at end of file diff --git a/src/app/api/debug-unit/route.ts b/src/app/api/debug-unit/route.ts new file mode 100644 index 0000000..9d05c8f --- /dev/null +++ b/src/app/api/debug-unit/route.ts @@ -0,0 +1,69 @@ +import { NextRequest, NextResponse } from 'next/server' +import { laximoUnitService } from '@/lib/laximo-service' + +export async function GET(request: NextRequest) { + const searchParams = request.nextUrl.searchParams + const catalogCode = searchParams.get('catalogCode') || 'KIA202404' + const vehicleId = searchParams.get('vehicleId') || '2095869513' + const unitId = searchParams.get('unitId') || '1842820926' + const ssd = searchParams.get('ssd') || '$*KwGhjK205_fnwvL-5sH4hIPO7Nv15cSn9L68mOiw9rapsNDZ6Oj64LCvtt2w6OmfqrHnxKf35-rV7Oi3-qmwpqWgp6TV-_X67uzD8fvEp_S-vJjosPa2qbDH0p-WiNuip6Wxvrfy6P762NvUpqOgoaf-5vSx_7eusdqlxP6P7KWj07a_sOa18Oaesb634rGot83z8JuhpqTV0d_Hpf7x4aWkt-ntzdXzn6aLp6PEx_DNzLa0m4fTqqaio6ulpf_wpszi5uDTxNDfg4eU1tvR0d3GxsOYjZbU7Mrk4OTVzfPwm6GmpNXR38el_vHhpaTr_f71wOWmkurT8fTgvKO63OWWncbAxdyjoKS4-fXrpqPXpaIAAAAADaPhQw==$' + + console.log('🔍 Debug Unit API - Параметры:', { catalogCode, vehicleId, unitId, ssd: ssd ? `${ssd.substring(0, 30)}...` : 'отсутствует' }) + + try { + const results: any = { + testParams: { catalogCode, vehicleId, unitId, ssdLength: ssd?.length }, + timestamp: new Date().toISOString() + } + + // 1. Тестируем GetUnitInfo + console.log('🔍 Тестируем GetUnitInfo...') + const unitInfo = await laximoUnitService.getUnitInfo(catalogCode, vehicleId, unitId, ssd) + results.unitInfo = { + success: !!unitInfo, + data: unitInfo, + hasImage: !!(unitInfo?.imageurl || unitInfo?.largeimageurl), + attributesCount: unitInfo?.attributes?.length || 0 + } + + // 2. Тестируем ListDetailByUnit + console.log('🔍 Тестируем ListDetailByUnit...') + const unitDetails = await laximoUnitService.getUnitDetails(catalogCode, vehicleId, unitId, ssd) + results.unitDetails = { + success: Array.isArray(unitDetails), + detailsCount: unitDetails?.length || 0, + data: unitDetails || [], + sampleDetail: unitDetails?.[0] || null + } + + // 3. Тестируем ListImageMapByUnit + console.log('🔍 Тестируем ListImageMapByUnit...') + const imageMap = await laximoUnitService.getUnitImageMap(catalogCode, vehicleId, unitId, ssd) + results.imageMap = { + success: !!imageMap, + coordinatesCount: imageMap?.coordinates?.length || 0, + data: imageMap, + sampleCoordinate: imageMap?.coordinates?.[0] || null + } + + // Суммарная статистика + results.summary = { + hasUnitInfo: !!unitInfo, + hasImage: !!(unitInfo?.imageurl || unitInfo?.largeimageurl), + detailsCount: unitDetails?.length || 0, + coordinatesCount: imageMap?.coordinates?.length || 0, + allDataPresent: !!unitInfo && (unitDetails?.length || 0) > 0 && !!imageMap + } + + console.log('✅ Debug Unit API результаты:', results.summary) + + return NextResponse.json(results) + } catch (error) { + console.error('❌ Ошибка в Debug Unit API:', error) + return NextResponse.json({ + error: 'Ошибка тестирования API узлов', + details: error instanceof Error ? error.message : String(error), + testParams: { catalogCode, vehicleId, unitId, ssdLength: ssd?.length } + }, { status: 500 }) + } +} \ No newline at end of file diff --git a/src/app/dashboard/test-styles/page.tsx b/src/app/dashboard/test-styles/page.tsx new file mode 100644 index 0000000..320d000 --- /dev/null +++ b/src/app/dashboard/test-styles/page.tsx @@ -0,0 +1,250 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { + AlertCircle, + CheckCircle, + Info, + ShoppingCart, + Users, + Settings +} from 'lucide-react' + +export default function TestStylesPage() { + return ( +
+ {/* Header */} +
+

+ Тест стилей Tailwind CSS + Shadcn/ui +

+

+ Проверка всех основных компонентов и цветов +

+
+ + {/* Color Test */} + + + Цветовая палитра + Проверка всех основных цветов + + +
+
+

Primary

+

Основной цвет

+
+
+

Secondary

+

Вторичный цвет

+
+
+

Accent

+

Акцентный цвет

+
+
+

Muted

+

Приглушенный

+
+
+
+
+ + {/* Buttons Test */} + + + Кнопки + Различные варианты кнопок + + +
+ + + + + + +
+
+ + + + +
+
+
+ + {/* Badges Test */} + + + Бейджи + Различные статусы и метки + + +
+ Default + Secondary + Outline + Destructive +
+
+
+ + {/* Form Elements Test */} + + + Элементы формы + Поля ввода и лейблы + + +
+
+ + +
+
+ + +
+
+
+
+ + {/* Icons Test */} + + + Иконки + Lucide React иконки + + +
+
+ + Успех +
+
+ + Ошибка +
+
+ + Информация +
+
+ + Заказы +
+
+ + Клиенты +
+
+
+
+ + {/* Cards Test */} +
+ + + + + Заказы + + Всего заказов + + +
1,234
+

+ +20.1% с прошлого месяца +

+
+
+ + + + + + Клиенты + + Активные клиенты + + +
456
+

+ +15.3% с прошлого месяца +

+
+
+ + + + + + Проблемы + + Требуют внимания + + +
12
+

+ -5.2% с прошлого месяца +

+
+
+
+ + {/* Dark/Light Mode Test */} + + + Тема + Проверка адаптации к темной/светлой теме + + +
+

+ Этот текст должен адаптироваться к теме +

+

+ А этот текст должен быть приглушенным +

+
+
+
+ + {/* Status Message */} + + + ✅ Статус интеграции + Результат исправления Tailwind CSS + + +
+

+ + Tailwind CSS v4 правильно интегрирован +

+

+ + Shadcn/ui компоненты работают корректно +

+

+ + Цветовая схема применяется правильно +

+

+ + Поддержка темной/светлой темы активна +

+
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index fdd6c91..e7b9c2b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,50 +1,11 @@ -@import "tailwindcss/preflight"; @import "tailwindcss"; -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); - --color-sidebar-ring: var(--sidebar-ring); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar: var(--sidebar); - --color-chart-5: var(--chart-5); - --color-chart-4: var(--chart-4); - --color-chart-3: var(--chart-3); - --color-chart-2: var(--chart-2); - --color-chart-1: var(--chart-1); - --color-ring: var(--ring); - --color-input: var(--input); - --color-border: var(--border); - --color-destructive: var(--destructive); - --color-accent-foreground: var(--accent-foreground); - --color-accent: var(--accent); - --color-muted-foreground: var(--muted-foreground); - --color-muted: var(--muted); - --color-secondary-foreground: var(--secondary-foreground); - --color-secondary: var(--secondary); - --color-primary-foreground: var(--primary-foreground); - --color-primary: var(--primary); - --color-popover-foreground: var(--popover-foreground); - --color-popover: var(--popover); - --color-card-foreground: var(--card-foreground); - --color-card: var(--card); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); -} +@custom-variant dark (&:is(.dark *)); :root { --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); + + /* Основные цвета в HSL формате */ --card: oklch(1 0 0); --card-foreground: oklch(0.145 0 0); --popover: oklch(1 0 0); @@ -58,6 +19,7 @@ --accent: oklch(0.97 0 0); --accent-foreground: oklch(0.205 0 0); --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: 210 40% 98%; --border: oklch(0.922 0 0); --input: oklch(0.922 0 0); --ring: oklch(0.708 0 0); @@ -74,6 +36,8 @@ --sidebar-accent-foreground: oklch(0.205 0 0); --sidebar-border: oklch(0.922 0 0); --sidebar-ring: oklch(0.708 0 0); + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); } .dark { @@ -92,6 +56,7 @@ --accent: oklch(0.269 0 0); --accent-foreground: oklch(0.985 0 0); --destructive: oklch(0.704 0.191 22.216); + --destructive-foreground: 210 40% 98%; --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); --ring: oklch(0.556 0 0); @@ -110,6 +75,53 @@ --sidebar-ring: oklch(0.556 0 0); } +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + @layer base { * { @apply border-border outline-ring/50; diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index ff568f5..0f86529 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -12,7 +12,8 @@ import { Package, UserCheck, ShoppingCart, - Receipt + Receipt, + Palette } from 'lucide-react' import { Button } from '@/components/ui/button' import { useAuth } from '@/components/providers/AuthProvider' @@ -62,6 +63,11 @@ const navigationItems = [ href: '/dashboard/settings', icon: Settings, }, + { + title: 'Тест стилей', + href: '/dashboard/test-styles', + icon: Palette, + }, ] export const Sidebar = ({ className }: SidebarProps) => { diff --git a/src/lib/graphql/resolvers.ts b/src/lib/graphql/resolvers.ts index c693173..73186da 100644 --- a/src/lib/graphql/resolvers.ts +++ b/src/lib/graphql/resolvers.ts @@ -1678,9 +1678,36 @@ export const resolvers = { laximoVehicleInfo: async (_: unknown, { catalogCode, vehicleId, ssd, localized }: { catalogCode: string; vehicleId: string; ssd?: string; localized: boolean }) => { try { - return await laximoService.getVehicleInfo(catalogCode, vehicleId, ssd, localized) + console.log('🔍 GraphQL laximoVehicleInfo resolver - входные параметры:', { + catalogCode, + vehicleId, + ssd: ssd ? `${ssd.substring(0, 50)}...` : 'отсутствует', + localized, + ssdLength: ssd?.length + }) + + const result = await laximoService.getVehicleInfo(catalogCode, vehicleId, ssd, localized) + + console.log('📋 GraphQL laximoVehicleInfo resolver - результат:', { + inputVehicleId: vehicleId, + returnedVehicleId: result?.vehicleid, + vehicleName: result?.name, + brand: result?.brand, + catalog: result?.catalog, + hasResult: !!result, + vehicleIdChanged: result?.vehicleid !== vehicleId + }) + + if (result && result.vehicleid !== vehicleId) { + console.log('🚨 BACKEND: Vehicle ID изменился!') + console.log(`📍 Запрошенный: ${vehicleId}`) + console.log(`📍 Полученный: ${result.vehicleid}`) + console.log(`📍 SSD: ${ssd?.substring(0, 50)}...`) + } + + return result } catch (error) { - console.error('Ошибка получения информации об автомобиле:', error) + console.error('❌ Ошибка получения информации об автомобиле:', error) return null } }, diff --git a/src/lib/graphql/typeDefs.ts b/src/lib/graphql/typeDefs.ts index ee08aa8..8b169cd 100644 --- a/src/lib/graphql/typeDefs.ts +++ b/src/lib/graphql/typeDefs.ts @@ -1428,6 +1428,12 @@ export const typeDefs = gql` description: String applicablemodels: String note: String + amount: String + range: String + codeonimage: String + match: Boolean + dateRange: String + ssd: String attributes: [LaximoDetailAttribute!] } diff --git a/src/lib/laximo-service.ts b/src/lib/laximo-service.ts index 1b74848..397cdc4 100644 --- a/src/lib/laximo-service.ts +++ b/src/lib/laximo-service.ts @@ -138,6 +138,12 @@ export interface LaximoDetail { description?: string applicablemodels?: string note?: string + amount?: string + range?: string + codeonimage?: string + match?: boolean + dateRange?: string + ssd?: string attributes?: LaximoDetailAttribute[] } @@ -1079,12 +1085,18 @@ class LaximoService { if (vehicleId) { command += `|VehicleId=${vehicleId}` } - if (ssd && ssd.trim() !== '') { + + // 🎯 ИСПРАВЛЕНИЕ: Для категорий каталога НЕ используем SSD + // SSD используется только для QuickGroup'ов, но не для категорий + if (categoryId) { + // Если указана категория, НЕ добавляем SSD - показываем ВСЕ узлы категории + command += `|CategoryId=${categoryId}` + console.log('🔧 Режим "От производителя": используем CategoryId БЕЗ SSD для получения всех узлов категории') + } else if (ssd && ssd.trim() !== '') { + // SSD добавляем только если НЕТ CategoryId (для QuickGroup'ов) const escapedSsd = this.escapeSsdForXML(ssd) command += `|ssd=${escapedSsd}` - } - if (categoryId) { - command += `|CategoryId=${categoryId}` + console.log('🔧 Режим "Общие": используем SSD для получения узлов автомобиля') } const hmac = this.createHMAC(command) @@ -1452,6 +1464,9 @@ class LaximoService { const xmlText = await response.text() console.log('📥 RAW XML ответ длиной:', xmlText.length, 'символов') + console.log('📄 ПОЛНЫЙ XML ОТВЕТ:') + console.log(xmlText) + console.log('📄 ======= КОНЕЦ XML =======') console.log('📄 Первые 1000 символов XML:') console.log(xmlText.substring(0, 1000)) @@ -1748,19 +1763,49 @@ class LaximoService { * Парсит ответ информации об автомобиле */ private parseVehicleInfoResponse(xmlText: string): LaximoVehicleInfo | null { + console.log('🔍 parseVehicleInfoResponse - начинаем парсинг...') + console.log('📄 XML длина:', xmlText.length) + console.log('📄 XML первые 500 символов:', xmlText.substring(0, 500)) + const resultData = this.extractResultData(xmlText) - if (!resultData) return null + if (!resultData) { + console.log('❌ Не удалось извлечь resultData') + return null + } + + console.log('📋 resultData первые 500 символов:', resultData.substring(0, 500)) const rowMatch = resultData.match(/]*)>([\s\S]*?)<\/row>/) - if (!rowMatch) return null + if (!rowMatch) { + console.log('❌ Не найден тег ') + return null + } + console.log('✅ Найден тег ') const attributes = rowMatch[1] const content = rowMatch[2] + console.log('📋 Атрибуты row:', attributes) + const getAttribute = (name: string): string => { const match = attributes.match(new RegExp(`${name}="([^"]*)"`, 'i')) return match ? match[1] : '' } + + const vehicleid = getAttribute('vehicleid') + const name = getAttribute('name') + const ssd = getAttribute('ssd') + const brand = getAttribute('brand') + const catalog = getAttribute('catalog') + + console.log('🔍 Извлеченные атрибуты:', { + vehicleid, + name, + brand, + catalog, + ssd: ssd ? `${ssd.substring(0, 50)}...` : 'отсутствует', + ssdLength: ssd?.length + }) // Парсим атрибуты автомобиля const vehicleAttributes: LaximoVehicleAttribute[] = [] @@ -2087,7 +2132,7 @@ class LaximoService { throw new Error('SSD parameter is required for ListQuickDetail') } - const command = `ListQuickDetail:Locale=ru_RU|Catalog=${catalogCode}|VehicleId=${vehicleId}|QuickGroupId=${quickGroupId}|ssd=${ssd}` + const command = `ListQuickDetail:Locale=ru_RU|Catalog=${catalogCode}|VehicleId=${vehicleId}|QuickGroupId=${quickGroupId}|ssd=${ssd}|Localized=true|All=1` const hmac = this.createHMAC(command) console.log('📝 ListQuickDetail Command:', command) @@ -2115,9 +2160,19 @@ class LaximoService { return null } - // Ищем секцию ListQuickDetail - const quickDetailMatch = resultData.match(/]*>([\s\S]*?)<\/ListQuickDetail>/) || - resultData.match(/]*>([\s\S]*?)<\/response>/) + // Декодируем HTML entities в XML для правильного парсинга + const decodedXML = resultData + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, "'") + + + + // Ищем секцию ListQuickDetail в декодированном XML + const quickDetailMatch = decodedXML.match(/]*>([\s\S]*?)<\/ListQuickDetail>/) || + decodedXML.match(/]*>([\s\S]*?)<\/response>/) if (!quickDetailMatch) { console.log('❌ Не найдена секция ListQuickDetail') @@ -2174,30 +2229,116 @@ class LaximoService { details: [] } - // В каждом узле ищем детали (Detail) - const detailPattern = /]*?)(?:\s*\/>|>([\s\S]*?)<\/Detail>)/g - let detailMatch + console.log('🔍 Содержимое узла (первые 1000 символов):') + console.log(unitContent.substring(0, 1000)) + console.log('🔍 Ищем детали в содержимом узла...') + + // ВАЖНО: Детали могут быть как внутри Unit content, так и в атрибутах самого Unit + // Сначала ищем в содержимом узла + const detailTagCount = (unitContent.match(/ = [] + + // ИСПРАВЛЕНО: Ищем все теги Detail (самозакрывающиеся и полные) в одном регулярном выражении + const detailPattern = /]*?)(?:\s*\/>|>([\s\S]*?)<\/Detail>)/g + let match + while ((match = detailPattern.exec(searchContent)) !== null) { + detailMatches.push({ + attributes: match[1] || '', + content: match[2] || '', + fullMatch: match[0] + }) + } + + console.log(`🔍 Найдено ${detailMatches.length} элементов Detail в узле`) + + let detailCount = 0 + for (const detailMatch of detailMatches) { + detailCount++ + const detailAttributes = detailMatch.attributes || '' + const detailContent = detailMatch.content || '' + + console.log(`🔍 Raw detail match #${detailCount}:`) + console.log('Attributes:', detailAttributes.substring(0, 200)) + if (detailContent) { + console.log('Content:', detailContent.substring(0, 200)) + } const detailId = this.extractAttribute(detailAttributes, 'detailid') const detailName = this.extractAttribute(detailAttributes, 'name') const oem = this.extractAttribute(detailAttributes, 'oem') const brand = this.extractAttribute(detailAttributes, 'brand') + const codeonimage = this.extractAttribute(detailAttributes, 'codeonimage') - console.log('🔩 Найдена деталь:', { detailId, detailName, oem, brand }) + console.log(`🔩 Найдена деталь #${detailCount}: ${detailName} (${oem})`) const detail: LaximoDetail = { - detailid: detailId, + detailid: detailId || codeonimage || oem, name: detailName, oem: oem, brand: brand, attributes: [] } - // Парсим атрибуты детали + // Парсим атрибуты детали из тега Detail + const amount = this.extractAttribute(detailAttributes, 'amount') + const dateRange = this.extractAttribute(detailAttributes, 'date_range') + const matchAttr = this.extractAttribute(detailAttributes, 'match') + const range = this.extractAttribute(detailAttributes, 'range') + const ssdAttr = this.extractAttribute(detailAttributes, 'ssd') + + // Добавляем все найденные атрибуты в деталь + if (amount) { + detail.amount = amount + detail.attributes?.push({ + key: 'amount', + name: 'Количество', + value: amount + }) + } + + if (dateRange) { + detail.dateRange = dateRange + detail.attributes?.push({ + key: 'date_range', + name: 'date_range', + value: dateRange + }) + } + + if (matchAttr) { + detail.match = matchAttr === 't' || matchAttr === 'true' + } + + if (range) { + detail.range = range + detail.attributes?.push({ + key: 'range', + name: 'Диапазон', + value: range + }) + } + + if (ssdAttr) { + detail.ssd = ssdAttr + } + + if (codeonimage) { + detail.codeonimage = codeonimage + } + + // Парсим дополнительные атрибуты из содержимого детали const attributePattern = /]*?)(?:\s*\/>)/g let attrMatch @@ -2224,6 +2365,60 @@ class LaximoService { unit.details!.push(detail) } + console.log(`📊 Найдено деталей в узле "${unitName}": ${detailCount}`) + + // Если детали не найдены, попробуем альтернативный подход + if (detailCount === 0) { + console.log('🔍 Детали не найдены стандартным способом, пробуем альтернативный парсинг...') + + // Поиск деталей в исходном XML блоке (до декодирования) + const originalUnitBlock = unitMatch[0] + console.log('🔍 Исходный XML блок (первые 1000 символов):') + console.log(originalUnitBlock.substring(0, 1000)) + + // Ищем детали в исходном формате + const originalDetailPattern = /]*(?:\s*\/>|>[\s\S]*?<\/Detail>)/g + let originalMatch + let altDetailCount = 0 + + while ((originalMatch = originalDetailPattern.exec(originalUnitBlock)) !== null) { + altDetailCount++ + const detailTag = originalMatch[0] + console.log(`🔍 Альтернативный парсинг - найдена деталь #${altDetailCount}:`) + console.log(detailTag.substring(0, 300)) + + // Простой парсинг атрибутов из тега + const nameMatch = detailTag.match(/name="([^"]*)"/) + const oemMatch = detailTag.match(/oem="([^"]*)"/) + const codeMatch = detailTag.match(/codeonimage="([^"]*)"/) + const amountMatch = detailTag.match(/amount="([^"]*)"/) + + if (nameMatch || oemMatch) { + const altDetail: LaximoDetail = { + detailid: codeMatch?.[1] || oemMatch?.[1] || `alt_${altDetailCount}`, + name: nameMatch?.[1] || 'Неизвестная деталь', + oem: oemMatch?.[1] || '', + attributes: [] + } + + if (amountMatch) { + altDetail.attributes?.push({ + key: 'amount', + name: 'Количество', + value: amountMatch[1] + }) + } + + unit.details!.push(altDetail) + console.log(`🔩 Альтернативный парсинг - добавлена деталь: ${altDetail.name} (${altDetail.oem})`) + } + } + + if (altDetailCount > 0) { + console.log(`✅ Альтернативный парсинг нашел ${altDetailCount} деталей`) + } + } + quickDetail.units!.push(unit) } } @@ -2232,7 +2427,7 @@ class LaximoService { if (quickDetail.units!.length === 0) { console.log('🔄 Пробуем альтернативный формат парсинга...') - // Ищем узлы напрямую + // Ищем узлы напрямую в декодированном XML const directUnitPattern = /]*?)(?:\s*\/>|>([\s\S]*?)<\/row>)/g let directUnitMatch @@ -2261,6 +2456,31 @@ class LaximoService { console.log(`✅ Обработано ${quickDetail.units!.length} узлов в группе ${quickGroupId}`) + // Подсчитываем общее количество деталей + const totalDetails = quickDetail.units!.reduce((total, unit) => total + (unit.details?.length || 0), 0) + console.log(`📊 Общее количество деталей: ${totalDetails}`) + + // Выводим детальную информацию о каждом узле + quickDetail.units!.forEach((unit, index) => { + console.log(`📦 Узел #${index + 1}: ${unit.name} (${unit.details?.length || 0} деталей)`) + unit.details?.forEach((detail, detailIndex) => { + const amountAttr = detail.attributes?.find(attr => attr.key === 'amount') + const amountStr = amountAttr ? ` - ${amountAttr.value}` : '' + console.log(` 🔩 Деталь #${detailIndex + 1}: ${detail.name} (${detail.oem})${amountStr}`) + }) + }) + + // Дополнительная диагностика + if (totalDetails === 0) { + console.log('⚠️ НЕ НАЙДЕНО НИ ОДНОЙ ДЕТАЛИ!') + console.log('🔍 Оригинальные данные (первые 1000 символов):') + console.log(resultData?.substring(0, 1000)) + console.log('🔍 Декодированные данные (первые 1000 символов):') + console.log(decodedXML.substring(0, 1000)) + } else { + console.log(`✅ Успешно найдено ${totalDetails} деталей`) + } + if (quickDetail.units!.length === 0) { return null } @@ -2479,52 +2699,58 @@ class LaximoService { details: [] } - // В каждом узле ищем детали (Detail) - const detailPattern = /]*?)(?:\s*\/>|>([\s\S]*?)<\/Detail>)/g + // В каждом узле ищем детали (Detail) - поддерживаем как самозакрывающиеся, так и полные теги + // Используем более простой подход: сначала найдем все вхождения тега Detail + console.log('🔍 Поиск деталей в полном блоке Unit (первые 500 символов):') + console.log(unitMatch[0].substring(0, 500)) + + // Ищем все теги Detail внутри текущего Unit + const detailPattern = /]*(?:\s*\/>|>[^<]*<\/Detail>)/g let detailMatch + let detailCount = 0 - while ((detailMatch = detailPattern.exec(unitContent)) !== null) { + // Получаем содержимое Unit для поиска деталей + const unitContentForDetails = unitMatch[2] || '' + + // Ищем все детали в содержимом Unit + const allDetailMatches = [...unitContentForDetails.matchAll(/]*?)(?:\s*\/>)/g)] + console.log(`🔍 Найдено ${allDetailMatches.length} самозакрывающихся тегов Detail в Unit`) + + for (const detailMatch of allDetailMatches) { const detailAttributes = detailMatch[1] - const detailContent = detailMatch[2] || '' + console.log(`🔍 Raw detail match #${detailCount + 1}:`) + console.log('Attributes:', detailAttributes.substring(0, 150)) - const detailId = this.extractAttribute(detailAttributes, 'detailid') - const detailName = this.extractAttribute(detailAttributes, 'name') + // Извлекаем основные атрибуты детали + const name = this.extractAttribute(detailAttributes, 'name') const oem = this.extractAttribute(detailAttributes, 'oem') - const brand = this.extractAttribute(detailAttributes, 'brand') const amount = this.extractAttribute(detailAttributes, 'amount') - const range = this.extractAttribute(detailAttributes, 'range') - - console.log('🔩 Найдена деталь:', { detailId, detailName, oem, brand }) + const codeonimage = this.extractAttribute(detailAttributes, 'codeonimage') + const match = this.extractAttribute(detailAttributes, 'match') + const dateRange = this.extractAttribute(detailAttributes, 'date_range') + const ssd = this.extractAttribute(detailAttributes, 'ssd') - const detail: LaximoOEMDetail = { - detailid: detailId, - name: detailName, - oem: oem, - brand: brand, - amount: amount, - range: range, + if (name && oem) { + const detail: LaximoDetail = { + detailid: `${unitId}_${detailCount}`, + name, + oem, + brand: '', + amount: amount || '1pcs', + range: dateRange || '', + codeonimage: codeonimage || '', + match: match === 't', + dateRange: dateRange || '', + ssd: ssd || '', + applicablemodels: '', + note: '', attributes: [] } - // Парсим атрибуты детали - const attributePattern = /]*?)(?:\s*\/>)/g - let attrMatch - - while ((attrMatch = attributePattern.exec(detailContent)) !== null) { - const attrAttributes = attrMatch[1] - - const key = this.extractAttribute(attrAttributes, 'key') - const name = this.extractAttribute(attrAttributes, 'name') - const value = this.extractAttribute(attrAttributes, 'value') - - detail.attributes?.push({ - key, - name: name || key, - value - }) + console.log(`🔩 Найдена деталь #${detailCount + 1}: ${detail.name} (${detail.oem})`) + unit.details!.push(detail) + detailCount++ } - - unit.details.push(detail) } category.units.push(unit) @@ -3026,7 +3252,7 @@ export class LaximoUnitService extends LaximoService { console.log('📋 Параметры:', { catalogCode, vehicleId, unitId, ssd: ssd ? `${ssd.substring(0, 30)}...` : 'отсутствует' }) // Используем GetUnitInfo согласно документации Laximo - let command = `GetUnitInfo:Locale=ru_RU|Catalog=${catalogCode}|UnitId=${unitId}` + let command = `GetUnitInfo:Locale=ru_RU|Catalog=${catalogCode}|VehicleId=${vehicleId}|UnitId=${unitId}` if (ssd && ssd.trim() !== '') { command += `|ssd=${ssd}` @@ -3061,7 +3287,7 @@ export class LaximoUnitService extends LaximoService { console.log('📋 Параметры:', { catalogCode, vehicleId, unitId, ssd: ssd ? `${ssd.substring(0, 30)}...` : 'отсутствует' }) // Используем ListDetailByUnit согласно документации Laximo - let command = `ListDetailByUnit:Locale=ru_RU|Catalog=${catalogCode}|UnitId=${unitId}` + let command = `ListDetailByUnit:Locale=ru_RU|Catalog=${catalogCode}|VehicleId=${vehicleId}|UnitId=${unitId}` if (ssd && ssd.trim() !== '') { command += `|ssd=${ssd}` @@ -3099,7 +3325,7 @@ export class LaximoUnitService extends LaximoService { console.log('📋 Параметры:', { catalogCode, vehicleId, unitId, ssd: ssd ? `${ssd.substring(0, 30)}...` : 'отсутствует' }) // Используем ListImageMapByUnit согласно документации Laximo - let command = `ListImageMapByUnit:Catalog=${catalogCode}|UnitId=${unitId}` + let command = `ListImageMapByUnit:Catalog=${catalogCode}|VehicleId=${vehicleId}|UnitId=${unitId}` if (ssd && ssd.trim() !== '') { command += `|ssd=${ssd}` @@ -3107,6 +3333,9 @@ export class LaximoUnitService extends LaximoService { command += `|ssd=` } + // Добавляем WithLinks=true согласно документации + command += `|WithLinks=true` + const hmac = this.createHMAC(command) console.log('📝 ListImageMapByUnit Command:', command) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index b71b862..bd0c391 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -4,59 +4,3 @@ import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } - -// Утилиты для работы с датами -export const formatDate = (dateString: string | Date, options?: Intl.DateTimeFormatOptions): string => { - try { - const date = typeof dateString === 'string' ? new Date(dateString) : dateString - - if (isNaN(date.getTime())) { - return 'Неизвестно' - } - - const defaultOptions: Intl.DateTimeFormatOptions = { - year: 'numeric', - month: 'long', - day: 'numeric' - } - - return date.toLocaleDateString('ru-RU', options || defaultOptions) - } catch { - return 'Неизвестно' - } -} - -export const formatDateTime = (dateString: string | Date): string => { - return formatDate(dateString, { - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit' - }) -} - -export const formatRelativeTime = (dateString: string | Date): string => { - try { - const date = typeof dateString === 'string' ? new Date(dateString) : dateString - const now = new Date() - const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000) - - if (diffInSeconds < 60) { - return 'только что' - } else if (diffInSeconds < 3600) { - const minutes = Math.floor(diffInSeconds / 60) - return `${minutes} мин. назад` - } else if (diffInSeconds < 86400) { - const hours = Math.floor(diffInSeconds / 3600) - return `${hours} ч. назад` - } else if (diffInSeconds < 2592000) { - const days = Math.floor(diffInSeconds / 86400) - return `${days} дн. назад` - } else { - return formatDate(date) - } - } catch { - return 'Неизвестно' - } -} diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index accf6d0..0000000 --- a/tailwind.config.js +++ /dev/null @@ -1,57 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - './src/pages/**/*.{js,ts,jsx,tsx,mdx}', - './src/components/**/*.{js,ts,jsx,tsx,mdx}', - './src/app/**/*.{js,ts,jsx,tsx,mdx}', - ], - theme: { - extend: { - colors: { - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - }, - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - fontFamily: { - sans: ["var(--font-geist-sans)", "sans-serif"], - mono: ["var(--font-geist-mono)", "monospace"], - }, - }, - }, - plugins: [], -} \ No newline at end of file