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
+
Акцентный цвет
+
+
+
+
+
+
+ {/* 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