Удален файл CSS_FIX_GUIDE.md, который содержал инструкции по исправлению проблем со стилями в Docker. Обновлены зависимости в package-lock.json и package.json для улучшения совместимости с Tailwind CSS и PostCSS. Внесены изменения в конфигурацию PostCSS и удален устаревший файл tailwind.config.js. Обновлены стили в globals.css для улучшения структуры и добавлены новые переменные. Добавлен новый элемент в боковое меню для тестирования стилей.
This commit is contained in:
127
CSS_FIX_GUIDE.md
127
CSS_FIX_GUIDE.md
@ -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`
|
188
package-lock.json
generated
188
package-lock.json
generated
@ -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": {
|
||||
|
@ -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",
|
||||
|
@ -1,6 +1,7 @@
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
export default {
|
||||
const config = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
};
|
||||
export default config;
|
||||
|
69
src/app/api/debug-unit/route.ts
Normal file
69
src/app/api/debug-unit/route.ts
Normal file
@ -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 })
|
||||
}
|
||||
}
|
250
src/app/dashboard/test-styles/page.tsx
Normal file
250
src/app/dashboard/test-styles/page.tsx
Normal file
@ -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 (
|
||||
<div className="p-6 space-y-8">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground mb-2">
|
||||
Тест стилей Tailwind CSS + Shadcn/ui
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Проверка всех основных компонентов и цветов
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Color Test */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Цветовая палитра</CardTitle>
|
||||
<CardDescription>Проверка всех основных цветов</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="p-4 bg-primary text-primary-foreground rounded-lg">
|
||||
<p className="font-semibold">Primary</p>
|
||||
<p className="text-sm">Основной цвет</p>
|
||||
</div>
|
||||
<div className="p-4 bg-secondary text-secondary-foreground rounded-lg">
|
||||
<p className="font-semibold">Secondary</p>
|
||||
<p className="text-sm">Вторичный цвет</p>
|
||||
</div>
|
||||
<div className="p-4 bg-accent text-accent-foreground rounded-lg">
|
||||
<p className="font-semibold">Accent</p>
|
||||
<p className="text-sm">Акцентный цвет</p>
|
||||
</div>
|
||||
<div className="p-4 bg-muted text-muted-foreground rounded-lg">
|
||||
<p className="font-semibold">Muted</p>
|
||||
<p className="text-sm">Приглушенный</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Buttons Test */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Кнопки</CardTitle>
|
||||
<CardDescription>Различные варианты кнопок</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Button variant="default">Default</Button>
|
||||
<Button variant="secondary">Secondary</Button>
|
||||
<Button variant="outline">Outline</Button>
|
||||
<Button variant="ghost">Ghost</Button>
|
||||
<Button variant="link">Link</Button>
|
||||
<Button variant="destructive">Destructive</Button>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Button size="sm">Small</Button>
|
||||
<Button size="default">Default</Button>
|
||||
<Button size="lg">Large</Button>
|
||||
<Button size="icon">
|
||||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Badges Test */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Бейджи</CardTitle>
|
||||
<CardDescription>Различные статусы и метки</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Badge variant="default">Default</Badge>
|
||||
<Badge variant="secondary">Secondary</Badge>
|
||||
<Badge variant="outline">Outline</Badge>
|
||||
<Badge variant="destructive">Destructive</Badge>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Form Elements Test */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Элементы формы</CardTitle>
|
||||
<CardDescription>Поля ввода и лейблы</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input id="email" placeholder="email@example.com" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Пароль</Label>
|
||||
<Input id="password" type="password" placeholder="Введите пароль" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Icons Test */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Иконки</CardTitle>
|
||||
<CardDescription>Lucide React иконки</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<div className="flex items-center gap-2 p-2 bg-green-50 text-green-700 rounded-lg">
|
||||
<CheckCircle className="h-5 w-5" />
|
||||
<span>Успех</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 p-2 bg-red-50 text-red-700 rounded-lg">
|
||||
<AlertCircle className="h-5 w-5" />
|
||||
<span>Ошибка</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 p-2 bg-blue-50 text-blue-700 rounded-lg">
|
||||
<Info className="h-5 w-5" />
|
||||
<span>Информация</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 p-2 bg-purple-50 text-purple-700 rounded-lg">
|
||||
<ShoppingCart className="h-5 w-5" />
|
||||
<span>Заказы</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 p-2 bg-orange-50 text-orange-700 rounded-lg">
|
||||
<Users className="h-5 w-5" />
|
||||
<span>Клиенты</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Cards Test */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<ShoppingCart className="h-5 w-5" />
|
||||
Заказы
|
||||
</CardTitle>
|
||||
<CardDescription>Всего заказов</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-blue-600">1,234</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
+20.1% с прошлого месяца
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Users className="h-5 w-5" />
|
||||
Клиенты
|
||||
</CardTitle>
|
||||
<CardDescription>Активные клиенты</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-green-600">456</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
+15.3% с прошлого месяца
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<AlertCircle className="h-5 w-5" />
|
||||
Проблемы
|
||||
</CardTitle>
|
||||
<CardDescription>Требуют внимания</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-red-600">12</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
-5.2% с прошлого месяца
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Dark/Light Mode Test */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Тема</CardTitle>
|
||||
<CardDescription>Проверка адаптации к темной/светлой теме</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="p-4 border rounded-lg">
|
||||
<p className="text-foreground mb-2">
|
||||
Этот текст должен адаптироваться к теме
|
||||
</p>
|
||||
<p className="text-muted-foreground">
|
||||
А этот текст должен быть приглушенным
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Status Message */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-green-600">✅ Статус интеграции</CardTitle>
|
||||
<CardDescription>Результат исправления Tailwind CSS</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
<p className="flex items-center gap-2">
|
||||
<CheckCircle className="h-5 w-5 text-green-600" />
|
||||
<span>Tailwind CSS v4 правильно интегрирован</span>
|
||||
</p>
|
||||
<p className="flex items-center gap-2">
|
||||
<CheckCircle className="h-5 w-5 text-green-600" />
|
||||
<span>Shadcn/ui компоненты работают корректно</span>
|
||||
</p>
|
||||
<p className="flex items-center gap-2">
|
||||
<CheckCircle className="h-5 w-5 text-green-600" />
|
||||
<span>Цветовая схема применяется правильно</span>
|
||||
</p>
|
||||
<p className="flex items-center gap-2">
|
||||
<CheckCircle className="h-5 w-5 text-green-600" />
|
||||
<span>Поддержка темной/светлой темы активна</span>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -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;
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
}
|
||||
},
|
||||
|
@ -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!]
|
||||
}
|
||||
|
||||
|
@ -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,20 +1763,50 @@ 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(/<row([^>]*)>([\s\S]*?)<\/row>/)
|
||||
if (!rowMatch) return null
|
||||
if (!rowMatch) {
|
||||
console.log('❌ Не найден тег <row>')
|
||||
return null
|
||||
}
|
||||
|
||||
console.log('✅ Найден тег <row>')
|
||||
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[] = []
|
||||
const attributeMatches = content.match(/<attribute[^>]*\/?>|<attribute[^>]*>[\s\S]*?<\/attribute>/g)
|
||||
@ -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(/<ListQuickDetail[^>]*>([\s\S]*?)<\/ListQuickDetail>/) ||
|
||||
resultData.match(/<response[^>]*>([\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(/<ListQuickDetail[^>]*>([\s\S]*?)<\/ListQuickDetail>/) ||
|
||||
decodedXML.match(/<response[^>]*>([\s\S]*?)<\/response>/)
|
||||
|
||||
if (!quickDetailMatch) {
|
||||
console.log('❌ Не найдена секция ListQuickDetail')
|
||||
@ -2174,30 +2229,116 @@ class LaximoService {
|
||||
details: []
|
||||
}
|
||||
|
||||
// В каждом узле ищем детали (Detail)
|
||||
const detailPattern = /<Detail([^>]*?)(?:\s*\/>|>([\s\S]*?)<\/Detail>)/g
|
||||
let detailMatch
|
||||
console.log('🔍 Содержимое узла (первые 1000 символов):')
|
||||
console.log(unitContent.substring(0, 1000))
|
||||
console.log('🔍 Ищем детали в содержимом узла...')
|
||||
|
||||
while ((detailMatch = detailPattern.exec(unitContent)) !== null) {
|
||||
const detailAttributes = detailMatch[1]
|
||||
const detailContent = detailMatch[2] || ''
|
||||
// ВАЖНО: Детали могут быть как внутри Unit content, так и в атрибутах самого Unit
|
||||
// Сначала ищем в содержимом узла
|
||||
const detailTagCount = (unitContent.match(/<Detail/g) || []).length
|
||||
console.log(`🔍 Общее количество тегов <Detail в содержимом: ${detailTagCount}`)
|
||||
|
||||
// Также проверяем весь блок Unit на предмет деталей
|
||||
const fullUnitBlock = unitMatch[0] // Полный блок Unit включая все его содержимое
|
||||
const fullUnitDetailCount = (fullUnitBlock.match(/<Detail/g) || []).length
|
||||
console.log(`🔍 Общее количество тегов <Detail в полном блоке Unit: ${fullUnitDetailCount}`)
|
||||
|
||||
// Будем искать детали в полном блоке Unit, а не только в unitContent
|
||||
const searchContent = fullUnitBlock
|
||||
console.log('🔍 Поиск деталей в полном блоке Unit (первые 500 символов):')
|
||||
console.log(searchContent.substring(0, 500))
|
||||
|
||||
const detailMatches: Array<{attributes: string, content: string, fullMatch: string}> = []
|
||||
|
||||
// ИСПРАВЛЕНО: Ищем все теги Detail (самозакрывающиеся и полные) в одном регулярном выражении
|
||||
const detailPattern = /<Detail([^>]*?)(?:\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 = /<attribute([^>]*?)(?:\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 = /<Detail[^>]*(?:\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 = /<row([^>]*?)(?:\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 = /<Detail([^>]*?)(?:\s*\/>|>([\s\S]*?)<\/Detail>)/g
|
||||
// В каждом узле ищем детали (Detail) - поддерживаем как самозакрывающиеся, так и полные теги
|
||||
// Используем более простой подход: сначала найдем все вхождения тега Detail
|
||||
console.log('🔍 Поиск деталей в полном блоке Unit (первые 500 символов):')
|
||||
console.log(unitMatch[0].substring(0, 500))
|
||||
|
||||
// Ищем все теги Detail внутри текущего Unit
|
||||
const detailPattern = /<Detail[^>]*(?:\s*\/>|>[^<]*<\/Detail>)/g
|
||||
let detailMatch
|
||||
let detailCount = 0
|
||||
|
||||
while ((detailMatch = detailPattern.exec(unitContent)) !== null) {
|
||||
// Получаем содержимое Unit для поиска деталей
|
||||
const unitContentForDetails = unitMatch[2] || ''
|
||||
|
||||
// Ищем все детали в содержимом Unit
|
||||
const allDetailMatches = [...unitContentForDetails.matchAll(/<Detail([^>]*?)(?:\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')
|
||||
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')
|
||||
|
||||
console.log('🔩 Найдена деталь:', { detailId, detailName, oem, brand })
|
||||
|
||||
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 = /<attribute([^>]*?)(?:\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)
|
||||
|
@ -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 'Неизвестно'
|
||||
}
|
||||
}
|
||||
|
@ -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: [],
|
||||
}
|
Reference in New Issue
Block a user