Объединены файлы правил системы в единую базу знаний v3.0 с устранением противоречий и дублирования. Создан rules-unified.md на основе rules.md, rules1.md и rules2.md с добавлением всех уникальных разделов. Обновлена терминология системы с соответствием реальной схеме БД (ТОВАР→PRODUCT, РАСХОДНИКИ→CONSUMABLE). Архивированы старые файлы правил в папку archive. Обновлены ссылки в CLAUDE.md и development-checklist.md на новый единый источник истины.
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
58
CLAUDE.md
Normal file
58
CLAUDE.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# СИСТЕМНЫЕ ПРАВИЛА ДЛЯ CLAUDE CODE
|
||||||
|
|
||||||
|
## 🚨 ОБЯЗАТЕЛЬНЫЕ ФАЙЛЫ ДЛЯ ПРОВЕРКИ
|
||||||
|
|
||||||
|
**ПЕРЕД ЛЮБЫМ ИЗМЕНЕНИЕМ КОДА ВСЕГДА ЧИТАТЬ:**
|
||||||
|
|
||||||
|
1. **`development-checklist.md`** - Критические проверки и запреты
|
||||||
|
2. **`rules-unified.md`** - Полная база знаний системы (v4.0)
|
||||||
|
3. **`fulfillment-cabinet-rules.md`** - Правила кабинета фулфилмента
|
||||||
|
4. **`visual-design-rules.md`** - Правила визуального дизайна
|
||||||
|
|
||||||
|
> **Архивированы**: `rules.md`, `rules1.md`, `rules2.md` → `archive/`
|
||||||
|
|
||||||
|
## 🔄 WORKFLOW РАЗРАБОТКИ
|
||||||
|
|
||||||
|
### Шаг 1: Анализ задачи
|
||||||
|
- [ ] Прочитать `development-checklist.md`
|
||||||
|
- [ ] Проверить применимые правила из всех файлов правил
|
||||||
|
- [ ] Убедиться что задача не нарушает критические запреты
|
||||||
|
|
||||||
|
### Шаг 2: Планирование
|
||||||
|
- [ ] Использовать TodoWrite для планирования задач
|
||||||
|
- [ ] Проверить соответствие архитектуре системы
|
||||||
|
- [ ] Учесть все обязательные проверки
|
||||||
|
|
||||||
|
### Шаг 3: Реализация
|
||||||
|
- [ ] Следовать техническим правилам (GraphQL, TypeScript)
|
||||||
|
- [ ] Соблюдать систему партнерства и типизацию
|
||||||
|
- [ ] Добавлять необходимое логирование
|
||||||
|
|
||||||
|
### Шаг 4: Проверка
|
||||||
|
- [ ] Проверить все пункты checklist'а
|
||||||
|
- [ ] Протестировать функциональность
|
||||||
|
- [ ] Убедиться в отсутствии нарушений запретов
|
||||||
|
|
||||||
|
## 🎯 КЛЮЧЕВЫЕ ПРИНЦИПЫ
|
||||||
|
|
||||||
|
1. **НЕ ПРЕДПОЛАГАТЬ** - всегда уточнять при сомнениях
|
||||||
|
2. **ПРОВЕРЯТЬ СХЕМЫ** - GraphQL и Prisma должны соответствовать коду
|
||||||
|
3. **СЛЕДОВАТЬ WORKFLOW** - не нарушать последовательность статусов
|
||||||
|
4. **ДОКУМЕНТИРОВАТЬ** - обновлять правила при решении проблем
|
||||||
|
|
||||||
|
## 📋 КРИТИЧЕСКИЕ ФАЙЛЫ
|
||||||
|
|
||||||
|
### Файлы с правилами (ВСЕГДА ПРОВЕРЯТЬ):
|
||||||
|
- `development-checklist.md` - ОБЯЗАТЕЛЬНЫЕ проверки
|
||||||
|
- `rules-unified.md` - Полная база знаний системы (v4.0)
|
||||||
|
- `fulfillment-cabinet-rules.md` - Правила кабинета фулфилмента
|
||||||
|
- `visual-design-rules.md` - Правила визуального дизайна
|
||||||
|
|
||||||
|
### Технические файлы:
|
||||||
|
- `prisma/schema.prisma` - Схема базы данных
|
||||||
|
- `src/graphql/typedefs.ts` - GraphQL схема
|
||||||
|
- `src/graphql/resolvers.ts` - GraphQL резолверы
|
||||||
|
|
||||||
|
## 🚨 НАПОМИНАНИЕ
|
||||||
|
|
||||||
|
**ЭТОТ ФАЙЛ СЛУЖИТ ПОСТОЯННЫМ НАПОМИНАНИЕМ О НЕОБХОДИМОСТИ ПРОВЕРКИ ВСЕХ ФАЙЛОВ С ПРАВИЛАМИ ПЕРЕД КАЖДЫМ ИЗМЕНЕНИЕМ КОДА!**
|
@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
> ⚠️ **ВАЖНОЕ ПРИМЕЧАНИЕ**: Данные из этого файла могут быть удалены только с разрешения пользователя. Все изменения должны согласовываться.
|
> ⚠️ **ВАЖНОЕ ПРИМЕЧАНИЕ**: Данные из этого файла могут быть удалены только с разрешения пользователя. Все изменения должны согласовываться.
|
||||||
|
|
||||||
|
## 🔤 ТЕРМИНЫ СИСТЕМЫ
|
||||||
|
> Для людей → `В коде`
|
||||||
|
- ТОВАР → `PRODUCT`
|
||||||
|
- РАСХОДНИКИ → `CONSUMABLE`
|
||||||
|
- БРАК → `DEFECT` *(планируется)*
|
||||||
|
- ПРОДУКТ → `FINISHED_PRODUCT` *(планируется)*
|
||||||
|
|
||||||
## 1. 🎯 ОСНОВНЫЕ ПРИНЦИПЫ
|
## 1. 🎯 ОСНОВНЫЕ ПРИНЦИПЫ
|
||||||
|
|
||||||
### 1.0 Структура системы по кабинетам
|
### 1.0 Структура системы по кабинетам
|
||||||
@ -27,14 +34,14 @@
|
|||||||
|
|
||||||
**🏢 КАБИНЕТ ПОСТАВЩИКА:**
|
**🏢 КАБИНЕТ ПОСТАВЩИКА:**
|
||||||
|
|
||||||
1. **ТОВАР** - базовый тип товара от поставщика
|
1. **ТОВАР** (`PRODUCT`) - базовый тип товара от поставщика
|
||||||
2. **РАСХОДНИКИ** - материалы и вспомогательные товары от поставщика
|
2. **РАСХОДНИКИ** (`CONSUMABLE`) - материалы и вспомогательные товары от поставщика
|
||||||
|
|
||||||
**🏭 КАБИНЕТ ФУЛФИЛМЕНТА:**
|
**🏭 КАБИНЕТ ФУЛФИЛМЕНТА:**
|
||||||
|
|
||||||
1. **ТОВАР** - базовые товары от поставщиков (принятые на склад)
|
1. **ТОВАР** (`PRODUCT`) - базовые товары от поставщиков (принятые на склад)
|
||||||
2. **БРАК** - производная от товара (товар с дефектами)
|
2. **БРАК** (`DEFECT` - планируется) - производная от товара (товар с дефектами)
|
||||||
3. **ПРОДУКТ** - производная от товара (готовый к продаже товар)
|
3. **ПРОДУКТ** (`FINISHED_PRODUCT` - планируется) - готовый к продаже товар
|
||||||
4. **РАСХОДНИКИ ФУЛФИЛМЕНТА** - операционные материалы фулфилмента
|
4. **РАСХОДНИКИ ФУЛФИЛМЕНТА** - операционные материалы фулфилмента
|
||||||
5. **РАСХОДНИКИ СЕЛЛЕРОВ** - материалы для товаров селлеров
|
5. **РАСХОДНИКИ СЕЛЛЕРОВ** - материалы для товаров селлеров
|
||||||
|
|
@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
> ⚠️ **ВАЖНОЕ ПРИМЕЧАНИЕ**: Данные из этого файла могут быть удалены только с разрешения пользователя. Все изменения должны согласовываться.
|
> ⚠️ **ВАЖНОЕ ПРИМЕЧАНИЕ**: Данные из этого файла могут быть удалены только с разрешения пользователя. Все изменения должны согласовываться.
|
||||||
|
|
||||||
|
## 🔤 ТЕРМИНЫ СИСТЕМЫ
|
||||||
|
> Для людей → `В коде`
|
||||||
|
- ТОВАР → `PRODUCT`
|
||||||
|
- РАСХОДНИКИ → `CONSUMABLE`
|
||||||
|
- БРАК → `DEFECT` *(планируется)*
|
||||||
|
- ПРОДУКТ → `FINISHED_PRODUCT` *(планируется)*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📑 ОГЛАВЛЕНИЕ
|
## 📑 ОГЛАВЛЕНИЕ
|
||||||
@ -30,14 +37,14 @@
|
|||||||
|
|
||||||
**🏢 КАБИНЕТ ПОСТАВЩИКА** - создает и управляет:
|
**🏢 КАБИНЕТ ПОСТАВЩИКА** - создает и управляет:
|
||||||
|
|
||||||
- **ТОВАР** - базовые товары от поставщика
|
- **ТОВАР** (`PRODUCT`) - базовые товары от поставщика
|
||||||
- **РАСХОДНИКИ** - материалы и вспомогательные товары от поставщика
|
- **РАСХОДНИКИ** (`CONSUMABLE`) - материалы и вспомогательные товары от поставщика
|
||||||
|
|
||||||
**🏭 КАБИНЕТ ФУЛФИЛМЕНТА** - принимает, обрабатывает и управляет всеми типами:
|
**🏭 КАБИНЕТ ФУЛФИЛМЕНТА** - принимает, обрабатывает и управляет всеми типами:
|
||||||
|
|
||||||
- **ТОВАР** - базовые товары от поставщиков (принятые на склад)
|
- **ТОВАР** (`PRODUCT`) - базовые товары от поставщиков (принятые на склад)
|
||||||
- **БРАК** - производная от товара (товар с дефектами)
|
- **БРАК** (`DEFECT` - планируется) - производная от товара (товар с дефектами)
|
||||||
- **ПРОДУКТ** - производная от товара (готовый к продаже товар)
|
- **ПРОДУКТ** (`FINISHED_PRODUCT` - планируется) - готовый к продаже товар
|
||||||
- **РАСХОДНИКИ ФУЛФИЛМЕНТА** - операционные материалы фулфилмента
|
- **РАСХОДНИКИ ФУЛФИЛМЕНТА** - операционные материалы фулфилмента
|
||||||
- **РАСХОДНИКИ СЕЛЛЕРОВ** - материалы для товаров селлеров
|
- **РАСХОДНИКИ СЕЛЛЕРОВ** - материалы для товаров селлеров
|
||||||
|
|
@ -2,6 +2,17 @@
|
|||||||
|
|
||||||
> ⚠️ **ВАЖНОЕ ПРИМЕЧАНИЕ**: Данный файл является объединенной базой знаний системы на основе анализа rules.md, rules1.md и description.md. Все изменения должны согласовываться.
|
> ⚠️ **ВАЖНОЕ ПРИМЕЧАНИЕ**: Данный файл является объединенной базой знаний системы на основе анализа rules.md, rules1.md и description.md. Все изменения должны согласовываться.
|
||||||
|
|
||||||
|
## 🔤 ТЕРМИНЫ СИСТЕМЫ
|
||||||
|
> Для людей → `В коде`
|
||||||
|
- ТОВАР → `PRODUCT`
|
||||||
|
- РАСХОДНИКИ → `CONSUMABLE`
|
||||||
|
- БРАК → `DEFECT` *(планируется)*
|
||||||
|
- ПРОДУКТ → `FINISHED_PRODUCT` *(планируется)*
|
||||||
|
- ПОСТАВЩИК → `WHOLESALE`
|
||||||
|
- СЕЛЛЕР → `SELLER`
|
||||||
|
- ФУЛФИЛМЕНТ → `FULFILLMENT`
|
||||||
|
- ЛОГИСТИКА → `LOGIST`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📑 ОГЛАВЛЕНИЕ
|
## 📑 ОГЛАВЛЕНИЕ
|
||||||
@ -27,23 +38,27 @@
|
|||||||
11. [🏭 Кабинет фулфилмента](#11--кабинет-фулфилмента)
|
11. [🏭 Кабинет фулфилмента](#11--кабинет-фулфилмента)
|
||||||
12. [🚚 Кабинет логистики](#12--кабинет-логистики)
|
12. [🚚 Кабинет логистики](#12--кабинет-логистики)
|
||||||
|
|
||||||
|
### 🤝 **СИСТЕМА ПАРТНЕРСТВА**
|
||||||
|
|
||||||
|
13. [🤝 Система партнерства и контрагентов](#13--система-партнерства-и-контрагентов)
|
||||||
|
|
||||||
### 🌐 **ИНТЕГРАЦИИ И ФУНКЦИИ**
|
### 🌐 **ИНТЕГРАЦИИ И ФУНКЦИИ**
|
||||||
|
|
||||||
13. [🌐 Интеграции с системой](#13--интеграции-с-системой)
|
14. [🌐 Интеграции с системой](#14--интеграции-с-системой)
|
||||||
14. [📊 Статистика и аналитика](#14--статистика-и-аналитика)
|
15. [📊 Статистика и аналитика](#15--статистика-и-аналитика)
|
||||||
15. [⚠️ Критические запреты](#15--критические-запреты)
|
16. [⚠️ Критические запреты](#16--критические-запреты)
|
||||||
|
|
||||||
### 💻 **ТЕХНИЧЕСКИЕ ПРАВИЛА**
|
### 💻 **ТЕХНИЧЕСКИЕ ПРАВИЛА**
|
||||||
|
|
||||||
16. [📱 Правила пользовательского интерфейса](#16--правила-пользовательского-интерфейса)
|
17. [📱 Правила пользовательского интерфейса](#17--правила-пользовательского-интерфейса)
|
||||||
17. [🚨 Правила обработки ошибок](#17--правила-обработки-ошибок)
|
18. [🚨 Правила обработки ошибок](#18--правила-обработки-ошибок)
|
||||||
18. [📈 Правила производительности](#18--правила-производительности)
|
19. [📈 Правила производительности](#19--правила-производительности)
|
||||||
19. [🔐 Правила безопасности данных](#19--правила-безопасности-данных)
|
20. [🔐 Правила безопасности данных](#20--правила-безопасности-данных)
|
||||||
20. [🎯 Правила качества кода](#20--правила-качества-кода)
|
21. [🎯 Правила качества кода](#21--правила-качества-кода)
|
||||||
|
|
||||||
### 📋 **ДОПОЛНИТЕЛЬНО**
|
### 📋 **ДОПОЛНИТЕЛЬНО**
|
||||||
|
|
||||||
21. [📋 Приложение: Дополнительные возможности и планы](#-приложение-дополнительные-возможности-и-планы)
|
22. [📋 Приложение: Дополнительные возможности и планы](#22-приложение-дополнительные-возможности-и-планы)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -53,10 +68,12 @@
|
|||||||
|
|
||||||
| Сущность | Название в системе | Кабинет создания | Описание | Статус |
|
| Сущность | Название в системе | Кабинет создания | Описание | Статус |
|
||||||
| ---------- | ---------------------------------- | ---------------- | ----------------------------------------------- | --------------- |
|
| ---------- | ---------------------------------- | ---------------- | ----------------------------------------------- | --------------- |
|
||||||
| Товар | `Product` (type: PRODUCT) | Поставщик | Базовый тип товара от поставщика | ✅ Реализовано |
|
| Товар | `Product` (type: `PRODUCT`) | Поставщик | Базовый тип товара от поставщика | ✅ Реализовано |
|
||||||
| Брак | `Product` (type: DEFECT) | Фулфилмент | Производная от товара с дефектами | 🔄 В разработке |
|
| Расходники | `Product` (type: `CONSUMABLE`) | Поставщик | Материалы и вспомогательные товары | ✅ Реализовано |
|
||||||
| Расходники | `Product` (type: CONSUMABLE) | Поставщик | Материалы и вспомогательные товары | ✅ Реализовано |
|
| Брак | `Product` (type: `DEFECT`)* | Фулфилмент | Производная от товара с дефектами | 📋 Планируется |
|
||||||
| Продукт | `Product` (type: FINISHED_PRODUCT) | Фулфилмент | Готовый к продаже товар (производная от товара) | 🔄 В разработке |
|
| Продукт | `Product` (type: `FINISHED_PRODUCT`)* | Фулфилмент | Готовый к продаже товар (производная от товара) | 📋 Планируется |
|
||||||
|
|
||||||
|
> **\* Планируется**: Типы `DEFECT` и `FINISHED_PRODUCT` еще не добавлены в Prisma схему
|
||||||
|
|
||||||
### 🏢 **ОРГАНИЗАЦИИ И РОЛИ**
|
### 🏢 **ОРГАНИЗАЦИИ И РОЛИ**
|
||||||
|
|
||||||
@ -96,9 +113,9 @@
|
|||||||
|
|
||||||
## 2. 📦 ТИПИЗАЦИЯ ПРЕДМЕТОВ
|
## 2. 📦 ТИПИЗАЦИЯ ПРЕДМЕТОВ
|
||||||
|
|
||||||
### 2.1 Четыре основных типа предметов
|
### 2.1 Два реализованных и два планируемых типа предметов
|
||||||
|
|
||||||
#### **1. ТОВАР** (базовый тип)
|
#### **1. ТОВАР** (`PRODUCT` - базовый тип)
|
||||||
|
|
||||||
- **СОЗДАЕТСЯ**: Поставщиком
|
- **СОЗДАЕТСЯ**: Поставщиком
|
||||||
- **СТАТУС**: Может быть активным/неактивным
|
- **СТАТУС**: Может быть активным/неактивным
|
||||||
@ -106,9 +123,17 @@
|
|||||||
- **ТРАНСФОРМАЦИЯ**: Может стать ПРОДУКТОМ или БРАКОМ
|
- **ТРАНСФОРМАЦИЯ**: Может стать ПРОДУКТОМ или БРАКОМ
|
||||||
- **ЦЕНА**: Обязательна, больше 0
|
- **ЦЕНА**: Обязательна, больше 0
|
||||||
|
|
||||||
#### **2. БРАК** (производная от товара)
|
#### **2. РАСХОДНИКИ** (`CONSUMABLE` - базовый тип)
|
||||||
|
|
||||||
- **СОЗДАЕТСЯ**: Фулфилментом на основе существующего ТОВАРА при обнаружении дефектов
|
- **СОЗДАЕТСЯ**: Поставщиком как универсальный тип
|
||||||
|
- **КЛАССИФИКАЦИЯ ПРИ ЗАКАЗЕ**:
|
||||||
|
- Фулфилмент заказывает → "Расходники фулфилмента"
|
||||||
|
- Селлер заказывает → "Расходники селлеров"
|
||||||
|
- **ДОСТУП**: Видны всем типам организаций в маркете
|
||||||
|
|
||||||
|
#### **3. БРАК** (`DEFECT` - планируется, производная от товара)
|
||||||
|
|
||||||
|
- **БУДЕТ СОЗДАВАТЬСЯ**: Фулфилментом на основе существующего ТОВАРА при обнаружении дефектов
|
||||||
- **МОМЕНТ СОЗДАНИЯ**: В процессе "Создание продукта" / "В работе" после подсчета факта
|
- **МОМЕНТ СОЗДАНИЯ**: В процессе "Создание продукта" / "В работе" после подсчета факта
|
||||||
- **СВЯЗЬ**: Обязательная связь с родительским товаром (parentId)
|
- **СВЯЗЬ**: Обязательная связь с родительским товаром (parentId)
|
||||||
- **ЗАКАЗ**: ЗАПРЕЩЕН заказ брака
|
- **ЗАКАЗ**: ЗАПРЕЩЕН заказ брака
|
||||||
@ -117,23 +142,17 @@
|
|||||||
- **WORKFLOW**: Особый процесс списания и утилизации
|
- **WORKFLOW**: Особый процесс списания и утилизации
|
||||||
- **УЧЕТ**: Отдельный учет в статистике потерь
|
- **УЧЕТ**: Отдельный учет в статистике потерь
|
||||||
- **ОТОБРАЖЕНИЕ**: Виден только для учета потерь
|
- **ОТОБРАЖЕНИЕ**: Виден только для учета потерь
|
||||||
|
- **⚠️ СТАТУС РАЗРАБОТКИ**: Тип `DEFECT` еще не добавлен в схему БД
|
||||||
|
|
||||||
#### **3. РАСХОДНИКИ** (базовый тип)
|
#### **4. ПРОДУКТ** (`FINISHED_PRODUCT` - планируется, производная от товара)
|
||||||
|
|
||||||
- **СОЗДАЕТСЯ**: Поставщиком как универсальный тип
|
- **БУДЕТ СОЗДАВАТЬСЯ**: Фулфилментом на основе ТОВАРА по заказу селлера
|
||||||
- **КЛАССИФИКАЦИЯ ПРИ ЗАКАЗЕ**:
|
|
||||||
- Фулфилмент заказывает → "Расходники фулфилмента"
|
|
||||||
- Селлер заказывает → "Расходники селлеров"
|
|
||||||
- **ДОСТУП**: Видны всем типам организаций в маркете
|
|
||||||
|
|
||||||
#### **4. ПРОДУКТ** (производная от товара)
|
|
||||||
|
|
||||||
- **СОЗДАЕТСЯ**: Фулфилментом на основе ТОВАРА по заказу селлера
|
|
||||||
- **ИНИЦИАТОР**: Селлер создает заказ с рецептурой, фулфилмент исполняет
|
- **ИНИЦИАТОР**: Селлер создает заказ с рецептурой, фулфилмент исполняет
|
||||||
- **СВЯЗЬ**: Обязательная связь с родительским товаром (parentId)
|
- **СВЯЗЬ**: Обязательная связь с родительским товаром (parentId)
|
||||||
- **РЕЦЕПТУРА**: Задается селлером при создании заказа (Товар + Услуга + Расходники)
|
- **РЕЦЕПТУРА**: Задается селлером при создании заказа (Товар + Услуга + Расходники)
|
||||||
- **СТАТУСЫ**: "в работе" → "готов к отправке"
|
- **СТАТУСЫ**: "в работе" → "готов к отправке"
|
||||||
- **ЗАКАЗ**: Доступен только в статусе "готов к отправке"
|
- **ЗАКАЗ**: Доступен только в статусе "готов к отправке"
|
||||||
|
- **⚠️ СТАТУС РАЗРАБОТКИ**: Тип `FINISHED_PRODUCT` еще не добавлен в схему БД
|
||||||
|
|
||||||
### 2.2 Обязательные поля карточки
|
### 2.2 Обязательные поля карточки
|
||||||
|
|
||||||
@ -595,7 +614,419 @@ transition-all duration-150
|
|||||||
- **ПРИЧИНА**: Заменяется контекстными кнопками в табах
|
- **ПРИЧИНА**: Заменяется контекстными кнопками в табах
|
||||||
- **СОХРАНИТЬ**: Стили и логику навигации, но адаптировать под новые роуты
|
- **СОХРАНИТЬ**: Стили и логику навигации, но адаптировать под новые роуты
|
||||||
|
|
||||||
### 9.4 Экономика
|
### 9.4 Структура страницы "Мои поставки" - Трёхблочная архитектура
|
||||||
|
|
||||||
|
#### **9.4.1 Обязательная структура страницы**
|
||||||
|
|
||||||
|
**ПРИНЦИП**: Страница состоит из трёх визуально разделённых блоков
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ 1. БЛОК ТАБОВ (навигация) │
|
||||||
|
│ - Фиксированная высота │
|
||||||
|
│ - Glass-эффект │
|
||||||
|
│ - Иерархическая структура │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ 2. БЛОК СТАТИСТИКИ (метрики) │
|
||||||
|
│ - Контекстные данные │
|
||||||
|
│ - 4 карточки в ряд (desktop) │
|
||||||
|
│ - Динамическое обновление │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ 3. ОСНОВНОЙ БЛОК (контент) │
|
||||||
|
│ - Сохраняет весь функционал │
|
||||||
|
│ - Таблицы, фильтры, действия │
|
||||||
|
│ - Высота до низа sidebar │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **9.4.2 Блок статистики - контекстные метрики**
|
||||||
|
|
||||||
|
**ПРАВИЛО**: Статистика меняется в зависимости от выбранных табов
|
||||||
|
|
||||||
|
**Для путей "Фулфилмент → Товар → Карточки/Поставщики":**
|
||||||
|
- Всего поставок
|
||||||
|
- Активных поставок
|
||||||
|
- Сумма активных поставок
|
||||||
|
- В пути
|
||||||
|
|
||||||
|
**Для пути "Фулфилмент → Расходники селлера":**
|
||||||
|
- Всего поставок
|
||||||
|
- Активных поставок
|
||||||
|
- Видов расходников
|
||||||
|
- Критические остатки
|
||||||
|
|
||||||
|
**Для путей "Маркетплейсы → Wildberries/Ozon":**
|
||||||
|
- Поставок на маркетплейс
|
||||||
|
- Товаров отправлено
|
||||||
|
- Возвраты за неделю
|
||||||
|
- Эффективность поставок
|
||||||
|
|
||||||
|
#### **9.4.3 Высота основного блока**
|
||||||
|
|
||||||
|
**ФОРМУЛА РАСЧЕТА**:
|
||||||
|
```css
|
||||||
|
height: calc(100vh - headerHeight - tabsHeight - statsHeight - margins)
|
||||||
|
```
|
||||||
|
|
||||||
|
**ПРАВИЛО ВЫРАВНИВАНИЯ**:
|
||||||
|
- Нижняя граница основного блока должна быть на одном уровне с нижней границей sidebar
|
||||||
|
- При изменении размера окна высота пересчитывается
|
||||||
|
- Внутренний скролл: `overflow-y-auto`
|
||||||
|
|
||||||
|
#### **9.4.4 Сохранение функционала**
|
||||||
|
|
||||||
|
**КРИТИЧЕСКИ ВАЖНО**: При добавлении блока статистики весь существующий функционал сохраняется:
|
||||||
|
- Таблицы с данными поставок
|
||||||
|
- Фильтры и сортировка
|
||||||
|
- Кнопки действий
|
||||||
|
- Детализация при клике
|
||||||
|
- Пагинация
|
||||||
|
- Поиск
|
||||||
|
|
||||||
|
**ЗАПРЕЩЕНО**:
|
||||||
|
- Удалять существующие компоненты
|
||||||
|
- Изменять логику работы таблиц
|
||||||
|
- Нарушать существующие API вызовы
|
||||||
|
|
||||||
|
#### **9.4.5 Проверка остатков при создании поставки**
|
||||||
|
|
||||||
|
**ОБЯЗАТЕЛЬНАЯ ВАЛИДАЦИЯ**:
|
||||||
|
|
||||||
|
1. **Проверка на клиенте**:
|
||||||
|
```typescript
|
||||||
|
// При изменении количества
|
||||||
|
if (quantity > product.stock) {
|
||||||
|
toast.error(`Недостаточно товара. Доступно: ${product.stock}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Проверка на сервере**:
|
||||||
|
```typescript
|
||||||
|
// Перед созданием заказа
|
||||||
|
const stockAvailable = await checkStockAvailability(productId, quantity);
|
||||||
|
if (!stockAvailable) {
|
||||||
|
throw new Error("Insufficient stock");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Логирование проверок**:
|
||||||
|
- Все попытки добавления в корзину
|
||||||
|
- Результаты проверки остатков
|
||||||
|
- ID пользователя и timestamp
|
||||||
|
|
||||||
|
#### **9.4.6 Адаптивность блоков**
|
||||||
|
|
||||||
|
**Desktop (>1024px)**:
|
||||||
|
- Все три блока вертикально
|
||||||
|
- Статистика: 4 карточки в ряд
|
||||||
|
|
||||||
|
**Tablet (768-1024px)**:
|
||||||
|
- Все три блока вертикально
|
||||||
|
- Статистика: 2 карточки в ряд
|
||||||
|
|
||||||
|
**Mobile (<768px)**:
|
||||||
|
- Блоки в колонку
|
||||||
|
- Статистика: 1 карточка в ряд
|
||||||
|
- Сворачиваемая статистика
|
||||||
|
|
||||||
|
### 9.5 Табы "Карточки" и "Поставщики" - объединённая логика
|
||||||
|
|
||||||
|
#### **9.5.1 Принцип единого типа предмета**
|
||||||
|
|
||||||
|
**КЛЮЧЕВОЕ ПРАВИЛО**: Табы "Карточки" и "Поставщики" - это два способа создания поставок одного типа предмета (ТОВАР)
|
||||||
|
|
||||||
|
**СПОСОБЫ СОЗДАНИЯ**:
|
||||||
|
- **Карточки** - импорт товаров через WB API с автоматическим созданием поставки
|
||||||
|
- **Поставщики** - прямой заказ товаров у поставщика с указанием рецептуры
|
||||||
|
|
||||||
|
**РЕЗУЛЬТАТ**: Оба способа создают `SupplyOrder` с товарами типа `PRODUCT`
|
||||||
|
|
||||||
|
#### **9.5.2 Общая статистика**
|
||||||
|
|
||||||
|
**ПРАВИЛО**: Блок статистики показывает ОДИНАКОВЫЕ данные для обоих табов
|
||||||
|
|
||||||
|
**МЕТРИКИ ДЛЯ ТАБОВ "КАРТОЧКИ" И "ПОСТАВЩИКИ"**:
|
||||||
|
- Всего поставок товаров (из всех источников)
|
||||||
|
- Активных поставок товаров (в работе)
|
||||||
|
- Сумма активных поставок товаров
|
||||||
|
- Товаров в пути (все способы доставки)
|
||||||
|
|
||||||
|
**ЗАПРЕЩЕНО**: Разделять статистику по способу создания
|
||||||
|
|
||||||
|
#### **9.5.3 Общий основной блок**
|
||||||
|
|
||||||
|
**СОДЕРЖИМОЕ**: Единая таблица всех поставок товаров
|
||||||
|
|
||||||
|
**ИСТОЧНИКИ ДАННЫХ**:
|
||||||
|
- Поставки, созданные через импорт карточек WB
|
||||||
|
- Поставки, созданные через заказ у поставщиков
|
||||||
|
- Все промежуточные и завершённые поставки
|
||||||
|
|
||||||
|
**РАЗЛИЧИЯ ТАБОВ**:
|
||||||
|
- Только кнопки создания ведут на разные страницы
|
||||||
|
- Таб "Карточки": `/supplies/create-cards`
|
||||||
|
- Таб "Поставщики": `/supplies/create-suppliers`
|
||||||
|
|
||||||
|
#### **9.5.4 Структура таблицы поставок товаров**
|
||||||
|
|
||||||
|
**ОБЯЗАТЕЛЬНЫЕ КОЛОНКИ**:
|
||||||
|
- Номер поставки
|
||||||
|
- Способ создания (иконка: 📱 карточки / 🏢 поставщик)
|
||||||
|
- Количество товаров
|
||||||
|
- Общая стоимость
|
||||||
|
- Поставщик/Источник
|
||||||
|
- Дата поставки (планируемая дата доставки)
|
||||||
|
- Статус
|
||||||
|
- Дата создания
|
||||||
|
- Действия
|
||||||
|
|
||||||
|
**ФИЛЬТРЫ**:
|
||||||
|
- По статусу workflow
|
||||||
|
- По способу создания
|
||||||
|
- По поставщику
|
||||||
|
- По периоду
|
||||||
|
- По дате поставки
|
||||||
|
- По сумме заказа
|
||||||
|
|
||||||
|
#### **9.5.5 Детализация поставки**
|
||||||
|
|
||||||
|
**ПРИ КЛИКЕ НА ПОСТАВКУ**:
|
||||||
|
- Раскрывается детализация товаров в поставке
|
||||||
|
- Информация о рецептуре (если применимо)
|
||||||
|
- История изменения статусов
|
||||||
|
- Логистическая информация
|
||||||
|
- Связанные документы
|
||||||
|
|
||||||
|
**ДЕЙСТВИЯ В ДЕТАЛИЗАЦИИ**:
|
||||||
|
- Отслеживание статуса
|
||||||
|
- Связь с поставщиком/логистикой
|
||||||
|
- Отмена (если статус позволяет)
|
||||||
|
- Экспорт данных
|
||||||
|
|
||||||
|
#### **9.5.6 Принципы реализации**
|
||||||
|
|
||||||
|
**ОБЯЗАТЕЛЬНО**:
|
||||||
|
- Использовать единый компонент для отображения таблицы
|
||||||
|
- Агрегировать данные из всех источников в статистике
|
||||||
|
- Сохранять фильтрацию при переключении между табами
|
||||||
|
|
||||||
|
**ЗАПРЕЩЕНО**:
|
||||||
|
- Создавать отдельные таблицы для разных способов создания
|
||||||
|
- Разделять статистику по источникам
|
||||||
|
- Дублировать логику отображения поставок
|
||||||
|
|
||||||
|
### 9.7 Форма создания поставки товаров через поставщиков
|
||||||
|
|
||||||
|
#### **9.7.1 Маршрут и навигация**
|
||||||
|
**URL**: `/supplies/create-suppliers`
|
||||||
|
**Доступ**: Только для селлеров и администраторов
|
||||||
|
**Возврат**: Кнопка "Назад" ведет к табу "Поставщики" в разделе товаров
|
||||||
|
|
||||||
|
#### **9.7.2 Структура страницы - 3 блока (аналогично create-consumables)**
|
||||||
|
|
||||||
|
**БЛОК 1: ЗАГОЛОВОК И НАВИГАЦИЯ**
|
||||||
|
- Заголовок: "Создание поставки товаров через поставщиков"
|
||||||
|
- Подзаголовок: "Прямой заказ товаров у поставщика с указанием рецептуры"
|
||||||
|
- Кнопка "Назад" (возврат к табу "Поставщики")
|
||||||
|
- Индикатор прогресса: "Шаг 1 из 3" (Товары → Логистика → Подтверждение)
|
||||||
|
|
||||||
|
**БЛОК 2: ФОРМА ТОВАРОВ**
|
||||||
|
- **Выбор поставщика**: Работает как на create-consumables (выбор из существующих)
|
||||||
|
- **Каталог товаров поставщика**: Отображение доступных товаров выбранного поставщика
|
||||||
|
- **Корзина товаров**: Система добавления товаров в корзину как на create-consumables
|
||||||
|
- **Поля товара при добавлении в корзину**:
|
||||||
|
- Название товара (из каталога поставщика)
|
||||||
|
- Артикул поставщика (из каталога)
|
||||||
|
- Категория (из каталога)
|
||||||
|
- Количество (вводит пользователь, > 0)
|
||||||
|
- Цена за единицу (из каталога или договорная)
|
||||||
|
- Комплектность (если есть) - описание состава комплекта
|
||||||
|
- Рецептура/состав (текстовое поле для дополнений)
|
||||||
|
- Параметры товара (цвет, размер, материал - если применимо)
|
||||||
|
|
||||||
|
**БЛОК 3: КОРЗИНА И ИТОГИ**
|
||||||
|
- Таблица товаров в корзине
|
||||||
|
- **Желаемая дата поставки** (обязательное поле с календарем)
|
||||||
|
- **Выбор логистики**:
|
||||||
|
- Dropdown "Логистическая компания" (опционально)
|
||||||
|
- Если не выбрано: "Логистику выберет фulfilment"
|
||||||
|
- Ориентировочная стоимость логистики (расчетная)
|
||||||
|
- Общее количество товаров в корзине
|
||||||
|
- Общая стоимость товаров
|
||||||
|
- Ориентировочная стоимость фулфилмента (расчетная)
|
||||||
|
- **Итого к оплате** (сумма всех составляющих)
|
||||||
|
- Кнопка "Продолжить" (переход к подтверждению заказа)
|
||||||
|
|
||||||
|
#### **9.7.3 Принцип работы с поставщиками-партнерами**
|
||||||
|
|
||||||
|
**ИСТОЧНИК ПОСТАВЩИКОВ**:
|
||||||
|
- Показываются только поставщики-партнеры из таблицы `Counterparty`
|
||||||
|
- Фильтрация: `counterparty.type === "WHOLESALE"`
|
||||||
|
- Партнерство может быть создано двумя способами:
|
||||||
|
1. Через заказ в маркете → автоматическое партнерство после одобрения
|
||||||
|
2. Через раздел "Партнеры" → отправка и принятие заявки `CounterpartyRequest`
|
||||||
|
|
||||||
|
**ВЫБОР ПОСТАВЩИКА**:
|
||||||
|
- Dropdown с поиском партнеров-поставщиков
|
||||||
|
- Отображение: Название поставщика, ИНН, статус партнерства
|
||||||
|
- Только активные партнеры с типом WHOLESALE
|
||||||
|
- При выборе загружается каталог товаров поставщика из `Product` таблицы
|
||||||
|
|
||||||
|
**КАТАЛОГ ТОВАРОВ ПАРТНЕРА**:
|
||||||
|
- Товары из `Product` where `organizationId = поставщик.id`
|
||||||
|
- Отображение в виде карточек с полями:
|
||||||
|
- Картинка товара
|
||||||
|
- Название, артикул
|
||||||
|
- Цена за единицу
|
||||||
|
- Доступное количество
|
||||||
|
- Поле ввода количества (минимум 5 цифр) с кнопками +/-
|
||||||
|
- Фильтрация по категориям
|
||||||
|
- Поиск по названию/артикулу
|
||||||
|
|
||||||
|
#### **9.7.4 Принцип работы с товарами и корзиной (как на create-consumables)**
|
||||||
|
|
||||||
|
**ДОБАВЛЕНИЕ В КОРЗИНУ**:
|
||||||
|
- Клик по товару → модальное окно с деталями
|
||||||
|
- Указание количества, комплектности, дополнительных параметров
|
||||||
|
- Кнопка "Добавить в корзину"
|
||||||
|
|
||||||
|
**КОРЗИНА**:
|
||||||
|
- Отображение добавленных товаров в таблице
|
||||||
|
- Колонки: Товар, Артикул, Количество, Цена, Комплектность, Сумма
|
||||||
|
- Возможность изменения количества
|
||||||
|
- Кнопка удаления товара из корзины
|
||||||
|
- Автоматический пересчет итогов
|
||||||
|
|
||||||
|
#### **9.7.5 Поля формы товара**
|
||||||
|
|
||||||
|
**ОСНОВНЫЕ ПОЛЯ** (из каталога поставщика):
|
||||||
|
- Название товара
|
||||||
|
- Артикул поставщика
|
||||||
|
- Категория
|
||||||
|
- Базовая цена
|
||||||
|
|
||||||
|
**ПОЛЯ ДЛЯ ЗАПОЛНЕНИЯ**:
|
||||||
|
- Количество (целое число > 0)
|
||||||
|
- Комплектность (если есть) - текстовое описание состава
|
||||||
|
- Цена за единицу (может отличаться от базовой по договоренности)
|
||||||
|
- Описание/рецептура (дополнительные требования)
|
||||||
|
- Особые требования к товару
|
||||||
|
|
||||||
|
**ПАРАМЕТРЫ ТОВАРА** (если применимо):
|
||||||
|
- Цвет, Размер, Материал, Бренд
|
||||||
|
- Динамические параметры для конкретной категории
|
||||||
|
|
||||||
|
#### **9.7.6 Валидация и проверки**
|
||||||
|
|
||||||
|
**ОБЯЗАТЕЛЬНАЯ ВАЛИДАЦИЯ**:
|
||||||
|
- Выбран поставщик
|
||||||
|
- В корзине минимум 1 товар
|
||||||
|
- Указана желаемая дата поставки
|
||||||
|
- У всех товаров указано количество > 0
|
||||||
|
- Все товары проверены на наличие у поставщика
|
||||||
|
- Количество каждого товара ≤ доступному остатку
|
||||||
|
|
||||||
|
**ПРЕДУПРЕЖДЕНИЯ**:
|
||||||
|
- "Товар заканчивается на складе поставщика (осталось X шт)"
|
||||||
|
- "Товар недоступен, выберите другое количество"
|
||||||
|
- "Выбранная дата может не соответствовать возможностям логистики"
|
||||||
|
- "Габариты не указаны - стоимость логистики ориентировочная"
|
||||||
|
- Превышение лимитов заказа
|
||||||
|
- Значительное отклонение цены от каталожной
|
||||||
|
|
||||||
|
**АВТОМАТИЧЕСКИЕ РАСЧЕТЫ**:
|
||||||
|
- Стоимость по каждому товару = количество × цена
|
||||||
|
- Общая стоимость товаров в корзине
|
||||||
|
- Комиссии фулфилмента (% от стоимости)
|
||||||
|
- Логистика (по весу/габаритам или фиксированная ставка)
|
||||||
|
|
||||||
|
#### **9.7.7 Выбор логистики**
|
||||||
|
|
||||||
|
**ОПЦИИ ЛОГИСТИКИ**:
|
||||||
|
- "Автоматический выбор" (фулфилмент выберет оптимальную)
|
||||||
|
- Список доступных логистических компаний
|
||||||
|
- Отображение ориентировочной стоимости по каждой опции
|
||||||
|
- Сроки доставки по каждой опции
|
||||||
|
|
||||||
|
**ЛОГИКА ВЫБОРА**:
|
||||||
|
- Если селлер не выбрал → фулфилмент выбирает оптимальную
|
||||||
|
- Если селлер выбрал → используется выбранная компания
|
||||||
|
- Финальная стоимость может отличаться от ориентировочной
|
||||||
|
|
||||||
|
#### **9.7.8 Желаемая дата поставки**
|
||||||
|
|
||||||
|
**РАСПОЛОЖЕНИЕ**: В блоке корзины и итогов
|
||||||
|
|
||||||
|
**ПОЛЕ "ЖЕЛАЕМАЯ ДАТА ПОСТАВКИ"**:
|
||||||
|
- **Обязательное поле** для планирования
|
||||||
|
- **Календарь** с ограничениями:
|
||||||
|
- Минимум: завтра (нельзя выбрать прошедшие даты)
|
||||||
|
- Максимум: +90 дней от текущей даты
|
||||||
|
- **Проверка на рабочие дни** (опционально)
|
||||||
|
- **Подсказки** по срокам доставки от выбранной логистической компании
|
||||||
|
|
||||||
|
**ЛОГИКА РАБОТЫ С ДАТОЙ**:
|
||||||
|
- При выборе логистики → автоматическое обновление возможных дат
|
||||||
|
- При изменении даты → пересчет стоимости логистики (если зависит от срочности)
|
||||||
|
- Отображение: "Ориентировочная дата доставки: 15-17 января 2024"
|
||||||
|
|
||||||
|
#### **9.7.9 Ограничения и проверки товаров**
|
||||||
|
|
||||||
|
**КОЛИЧЕСТВЕННЫЕ ОГРАНИЧЕНИЯ**:
|
||||||
|
- **НЕТ ограничений** на максимальное количество товаров в поставке
|
||||||
|
- **ОБЯЗАТЕЛЬНАЯ проверка** доступности на складах поставщиков
|
||||||
|
- **В реальном времени** проверка остатков при добавлении в корзину
|
||||||
|
- **Предупреждения** если запрашиваемое количество превышает доступное
|
||||||
|
- **Блокировка** добавления товара если его нет в наличии у поставщика
|
||||||
|
|
||||||
|
**ЛОГИКА ПРОВЕРКИ ОСТАТКОВ**:
|
||||||
|
- При добавлении товара в корзину → запрос к API поставщика
|
||||||
|
- При изменении количества → повторная проверка
|
||||||
|
- Отображение доступного количества рядом с полем ввода
|
||||||
|
- Сообщение: "Доступно: 150 шт" или "Нет в наличии"
|
||||||
|
|
||||||
|
#### **9.7.10 Габариты и логистические данные**
|
||||||
|
|
||||||
|
**НА ЭТАПЕ СОЗДАНИЯ ПОСТАВКИ**:
|
||||||
|
- **НЕТ обязательных** полей для габаритов/веса
|
||||||
|
- **Ориентировочный расчет** логистики по средним показателям категории
|
||||||
|
- **Предупреждение** что финальная стоимость может отличаться
|
||||||
|
|
||||||
|
**В КАБИНЕТЕ ПОСТАВЩИКА** (при одобрении поставки):
|
||||||
|
- **Возможность ввести** точные логистические данные:
|
||||||
|
- Габариты каждого товара (Д×Ш×В в см)
|
||||||
|
- Объем упаковки (в куб.м)
|
||||||
|
- Количество мест/коробок
|
||||||
|
- Общий вес поставки
|
||||||
|
- **НЕ обязательные** для заполнения (можно оставить пустыми)
|
||||||
|
- **Уточнение стоимости** логистики после заполнения данных
|
||||||
|
|
||||||
|
#### **9.7.11 Навигация и сохранение**
|
||||||
|
|
||||||
|
**УПРАВЛЕНИЕ СЕССИЕЙ**:
|
||||||
|
- Автосохранение корзины каждые 30 секунд
|
||||||
|
- Сохранение состояния при смене поставщика
|
||||||
|
- Предупреждение при попытке покинуть страницу
|
||||||
|
|
||||||
|
**ПЕРЕХОДЫ**:
|
||||||
|
- "Назад" → возврат к табу "Поставщики" (с предупреждением о потере данных)
|
||||||
|
- "Очистить корзину" → очистка всех товаров с подтверждением
|
||||||
|
- "Продолжить" → переход к подтверждению и оформлению заказа
|
||||||
|
|
||||||
|
#### **9.7.12 Интеграция с системой**
|
||||||
|
|
||||||
|
**СВЯЗЬ С ДАННЫМИ**:
|
||||||
|
- Работа с каталогом товаров поставщиков
|
||||||
|
- Проверка остатков в реальном времени
|
||||||
|
- Создание черновика `SupplyOrder` типа `ТОВАР` способом `suppliers`
|
||||||
|
|
||||||
|
**ОСОБЕННОСТИ**:
|
||||||
|
- Отличие от "Карточки": здесь товары выбираются из каталога поставщика
|
||||||
|
- Отличие от "Расходники": здесь товары предназначены для перепродажи
|
||||||
|
- Возможность указания комплектности для наборов товаров
|
||||||
|
|
||||||
|
### 9.8 Экономика
|
||||||
|
|
||||||
_Раздел находится в разработке. Будет добавлен позже._
|
_Раздел находится в разработке. Будет добавлен позже._
|
||||||
|
|
||||||
@ -801,9 +1232,169 @@ _Раздел находится в разработке. Будет добав
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 13. 🌐 ИНТЕГРАЦИИ С СИСТЕМОЙ
|
## 13. 🤝 СИСТЕМА ПАРТНЕРСТВА И КОНТРАГЕНТОВ
|
||||||
|
|
||||||
### 13.1 Глобальная интеграция
|
### 13.1 Основы системы партнерства
|
||||||
|
|
||||||
|
**ПРИНЦИП РАБОТЫ**:
|
||||||
|
- Все типы кабинетов могут создавать партнерские отношения
|
||||||
|
- Партнерство реализовано через таблицы `Counterparty` и `CounterpartyRequest`
|
||||||
|
- Двустороннее партнерство: каждая организация видит другую в разделе "Партнеры"
|
||||||
|
|
||||||
|
**ТИПЫ ОРГАНИЗАЦИЙ-ПАРТНЕРОВ**:
|
||||||
|
- `WHOLESALE` - Поставщики товаров и расходников
|
||||||
|
- `FULFILLMENT` - Фулфилмент-центры
|
||||||
|
- `LOGIST` - Логистические компании
|
||||||
|
- `SELLER` - Селлеры (торговые организации)
|
||||||
|
|
||||||
|
### 13.2 Способы создания партнерства
|
||||||
|
|
||||||
|
#### **СПОСОБ 1: Через заказ в маркете (автоматическое партнерство)**
|
||||||
|
|
||||||
|
**WORKFLOW**:
|
||||||
|
1. Поставщик создает товар → товар попадает в глобальный маркет
|
||||||
|
2. Селлер/Фулфилмент находит товар в маркете
|
||||||
|
3. Создает заказ (`SupplyOrder`) → статус `PENDING`
|
||||||
|
4. Поставщик получает уведомление в разделе "Заявки"
|
||||||
|
5. Поставщик одобряет заявку → статус `SUPPLIER_APPROVED`
|
||||||
|
6. **Автоматически создается двустороннее партнерство**:
|
||||||
|
- Запись в `Counterparty` для заказчика (`organizationId` → `counterpartyId`)
|
||||||
|
- Обратная запись в `Counterparty` для поставщика
|
||||||
|
7. Обе организации видят друг друга в разделе "Партнеры"
|
||||||
|
|
||||||
|
#### **СПОСОБ 2: Через раздел "Партнеры" (заявочная система)**
|
||||||
|
|
||||||
|
**WORKFLOW**:
|
||||||
|
1. Любая организация идет в раздел "Партнеры"
|
||||||
|
2. Использует поиск для нахождения нужной организации
|
||||||
|
3. Отправляет заявку на партнерство → создается `CounterpartyRequest`:
|
||||||
|
- `senderId` - отправитель заявки
|
||||||
|
- `receiverId` - получатель заявки
|
||||||
|
- `status: PENDING`
|
||||||
|
- `message` - опциональное сообщение
|
||||||
|
4. Получатель видит заявку в разделе "Партнеры" → "Входящие заявки"
|
||||||
|
5. Получатель принимает заявку → статус меняется на `ACCEPTED`
|
||||||
|
6. **Автоматически создается двустороннее партнерство** (аналогично способу 1)
|
||||||
|
|
||||||
|
**СТАТУСЫ ЗАЯВОК**:
|
||||||
|
- `PENDING` - Ожидает рассмотрения
|
||||||
|
- `ACCEPTED` - Принята (партнерство создано)
|
||||||
|
- `REJECTED` - Отклонена
|
||||||
|
- `CANCELLED` - Отменена отправителем
|
||||||
|
|
||||||
|
### 13.3 Использование партнерства в системе
|
||||||
|
|
||||||
|
#### **В форме создания поставки товаров через поставщиков**
|
||||||
|
|
||||||
|
**ПРАВИЛО ОТОБРАЖЕНИЯ ПОСТАВЩИКОВ**:
|
||||||
|
- Показываются только партнеры с типом `WHOLESALE`
|
||||||
|
- Источник: таблица `Counterparty` where `counterparty.type === "WHOLESALE"`
|
||||||
|
- Фильтрация по `organizationId` текущего пользователя
|
||||||
|
|
||||||
|
**ЛОГИКА РАБОТЫ**:
|
||||||
|
1. Пользователь выбирает поставщика из dropdown партнеров-поставщиков
|
||||||
|
2. Загружается каталог товаров поставщика из `Product` таблицы
|
||||||
|
3. Товары фильтруются по `organizationId = поставщик.id`
|
||||||
|
4. Пользователь может добавлять товары в корзину и создавать заказ
|
||||||
|
|
||||||
|
#### **В других разделах системы**
|
||||||
|
|
||||||
|
**ВЫБОР ФУЛФИЛМЕНТ-ЦЕНТРА**:
|
||||||
|
- Партнеры с типом `FULFILLMENT`
|
||||||
|
- Используется при создании поставок расходников
|
||||||
|
|
||||||
|
**ВЫБОР ЛОГИСТИКИ**:
|
||||||
|
- Партнеры с типом `LOGIST`
|
||||||
|
- Используется при планировании доставок
|
||||||
|
|
||||||
|
**МЕССЕНДЖЕР**:
|
||||||
|
- Общение доступно только между партнерами
|
||||||
|
- Список чатов формируется из таблицы `Counterparty`
|
||||||
|
|
||||||
|
### 13.4 Технические правила
|
||||||
|
|
||||||
|
**СОЗДАНИЕ ЗАПИСЕЙ В COUNTERPARTY**:
|
||||||
|
```sql
|
||||||
|
-- При создании партнерства создаются ДВЕ записи
|
||||||
|
INSERT INTO counterparties (organizationId, counterpartyId) VALUES (org1_id, org2_id);
|
||||||
|
INSERT INTO counterparties (organizationId, counterpartyId) VALUES (org2_id, org1_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**ПРОВЕРКА ПАРТНЕРСТВА**:
|
||||||
|
```typescript
|
||||||
|
const isPartner = await prisma.counterparty.findFirst({
|
||||||
|
where: {
|
||||||
|
organizationId: currentOrgId,
|
||||||
|
counterpartyId: targetOrgId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**ПОЛУЧЕНИЕ ПАРТНЕРОВ ПО ТИПУ**:
|
||||||
|
```typescript
|
||||||
|
const wholesalePartners = await prisma.counterparty.findMany({
|
||||||
|
where: {
|
||||||
|
organizationId: currentOrgId,
|
||||||
|
counterparty: {
|
||||||
|
type: "WHOLESALE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
counterparty: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13.5 Решение распространенных проблем
|
||||||
|
|
||||||
|
#### **ПРОБЛЕМА: GraphQL запрос не возвращает данные партнеров**
|
||||||
|
|
||||||
|
**Симптомы**:
|
||||||
|
- В консоли браузера: `All counterparties: 0`, `All counterparties data: []`
|
||||||
|
- GraphQL запрос отправляется успешно, но возвращает пустой массив
|
||||||
|
- В базе данных партнеры существуют
|
||||||
|
|
||||||
|
**Возможные причины и решения**:
|
||||||
|
|
||||||
|
1. **НЕПРАВИЛЬНОЕ ИМЯ ПОЛЯ В КОДЕ** (наиболее частая ошибка):
|
||||||
|
```typescript
|
||||||
|
// ❌ НЕПРАВИЛЬНО
|
||||||
|
const allCounterparties = counterpartiesData?.getMyCounterparties || [];
|
||||||
|
|
||||||
|
// ✅ ПРАВИЛЬНО
|
||||||
|
const allCounterparties = counterpartiesData?.myCounterparties || [];
|
||||||
|
```
|
||||||
|
**Объяснение**: В GraphQL схеме поле называется `myCounterparties`, а не `getMyCounterparties`
|
||||||
|
|
||||||
|
2. **НЕСООТВЕТСТВИЕ ID ПОЛЬЗОВАТЕЛЯ**:
|
||||||
|
- Проверить что пользователь авторизован под правильным аккаунтом
|
||||||
|
- Убедиться что `context.user.id` соответствует ожидаемому пользователю
|
||||||
|
|
||||||
|
3. **ПРОБЛЕМЫ С КЕШИРОВАНИЕМ APOLLO CLIENT**:
|
||||||
|
```typescript
|
||||||
|
const { data, loading, error } = useQuery(GET_MY_COUNTERPARTIES, {
|
||||||
|
fetchPolicy: 'network-only', // Обходим кеш
|
||||||
|
errorPolicy: 'all'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **ОТСУТСТВИЕ ЛОГИРОВАНИЯ В РЕЗОЛВЕРЕ**:
|
||||||
|
- Добавить console.log в GraphQL резолвер для отладки
|
||||||
|
- Проверить что резолвер вызывается
|
||||||
|
|
||||||
|
**Чек-лист для диагностики**:
|
||||||
|
- [ ] Проверить правильность имени поля в коде (`myCounterparties`)
|
||||||
|
- [ ] Убедиться что пользователь авторизован
|
||||||
|
- [ ] Проверить логи сервера на вызов резолвера
|
||||||
|
- [ ] Добавить отладочное логирование в браузере
|
||||||
|
- [ ] Проверить данные в базе через Prisma Studio
|
||||||
|
- [ ] Использовать `fetchPolicy: 'network-only'` для обхода кеша
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. 🌐 ИНТЕГРАЦИИ С СИСТЕМОЙ
|
||||||
|
|
||||||
|
### 14.1 Глобальная интеграция
|
||||||
|
|
||||||
- **МАРКЕТ**: Товары поставщиков отображаются в глобальном маркете
|
- **МАРКЕТ**: Товары поставщиков отображаются в глобальном маркете
|
||||||
- **СИНХРОНИЗАЦИЯ**: Данные склада синхронизируются с модулем аналитики
|
- **СИНХРОНИЗАЦИЯ**: Данные склада синхронизируются с модулем аналитики
|
||||||
@ -1137,6 +1728,89 @@ _Раздел находится в разработке. Будет добав
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 21. 🎯 ПРАВИЛА КАЧЕСТВА КОДА
|
||||||
|
|
||||||
|
### 21.1 GraphQL Rules
|
||||||
|
|
||||||
|
#### **Правила именования полей**
|
||||||
|
|
||||||
|
**ВАЖНО**: Имена полей в GraphQL запросах должны точно соответствовать схеме!
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ ПРАВИЛЬНО - соответствует схеме
|
||||||
|
export const GET_MY_COUNTERPARTIES = gql`
|
||||||
|
query GetMyCounterparties {
|
||||||
|
myCounterparties { // <- имя поля в схеме
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Использование в компоненте
|
||||||
|
const allCounterparties = counterpartiesData?.myCounterparties || [];
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ❌ НЕПРАВИЛЬНО - не соответствует схеме
|
||||||
|
const allCounterparties = counterpartiesData?.getMyCounterparties || []; // Ошибка!
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **Правила отладки GraphQL**
|
||||||
|
|
||||||
|
**При проблемах с GraphQL запросами следовать чек-листу**:
|
||||||
|
|
||||||
|
1. **Проверить соответствие имен полей схеме**
|
||||||
|
2. **Добавить fetchPolicy: 'network-only' для обхода кеша**
|
||||||
|
3. **Логировать данные в браузере и сервере**
|
||||||
|
4. **Проверить авторизацию пользователя**
|
||||||
|
5. **Убедиться что резолвер вызывается**
|
||||||
|
|
||||||
|
#### **Обязательные поля для отладки**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { data, loading, error } = useQuery(QUERY_NAME, {
|
||||||
|
fetchPolicy: 'network-only', // Обходим кеш при отладке
|
||||||
|
errorPolicy: 'all' // Показываем все ошибки
|
||||||
|
});
|
||||||
|
|
||||||
|
// Логирование для отладки
|
||||||
|
console.log("Data:", data);
|
||||||
|
console.log("Loading:", loading);
|
||||||
|
console.log("Error:", error);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 21.2 TypeScript Rules
|
||||||
|
|
||||||
|
#### **Интерфейсы данных**
|
||||||
|
|
||||||
|
**Поля интерфейсов должны соответствовать GraphQL схеме**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ ПРАВИЛЬНО - соответствует schema.prisma
|
||||||
|
interface GoodsProduct {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
article: string; // <- соответствует полю в schema
|
||||||
|
quantity?: number; // <- соответствует полю в schema
|
||||||
|
organization: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ❌ НЕПРАВИЛЬНО - не соответствует schema
|
||||||
|
interface GoodsProduct {
|
||||||
|
sku: string; // <- в schema поле называется 'article'
|
||||||
|
stock?: number; // <- в schema поле называется 'quantity'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
_Эта база знаний создана на основе анализа rules.md, rules1.md и description.md и является единым источником понимания структуры и логики проекта._
|
_Эта база знаний создана на основе анализа rules.md, rules1.md и description.md и является единым источником понимания структуры и логики проекта._
|
||||||
|
|
||||||
_Версия: 2.0_
|
_Версия: 2.0_
|
@ -2,14 +2,24 @@
|
|||||||
|
|
||||||
> ⚠️ **КРИТИЧЕСКИ ВАЖНО**: Этот чеклист ОБЯЗАТЕЛЕН к проверке перед любым изменением кода!
|
> ⚠️ **КРИТИЧЕСКИ ВАЖНО**: Этот чеклист ОБЯЗАТЕЛЕН к проверке перед любым изменением кода!
|
||||||
|
|
||||||
|
## 🔤 ТЕРМИНЫ СИСТЕМЫ
|
||||||
|
> Для людей → `В коде`
|
||||||
|
- ТОВАР → `PRODUCT`
|
||||||
|
- РАСХОДНИКИ → `CONSUMABLE`
|
||||||
|
- БРАК → `DEFECT` *(планируется)*
|
||||||
|
- ПРОДУКТ → `FINISHED_PRODUCT` *(планируется)*
|
||||||
|
|
||||||
## 🔴 КРИТИЧЕСКИЕ ПРОВЕРКИ (НЕЛЬЗЯ НАРУШАТЬ)
|
## 🔴 КРИТИЧЕСКИЕ ПРОВЕРКИ (НЕЛЬЗЯ НАРУШАТЬ)
|
||||||
|
|
||||||
### ✅ Типизация предметов
|
### ✅ Типизация предметов
|
||||||
|
|
||||||
- [ ] Каждый предмет имеет один из 4 типов: ТОВАР, БРАК, РАСХОДНИКИ, ПРОДУКТ
|
- [ ] Каждый предмет имеет один из типов: ТОВАР (`PRODUCT`), РАСХОДНИКИ (`CONSUMABLE`), БРАК и ПРОДУКТ (планируются)
|
||||||
- [ ] БРАК и ПРОДУКТ имеют обязательную связь с родительским товаром (parentId)
|
- [ ] БРАК и ПРОДУКТ имеют обязательную связь с родительским товаром (parentId)
|
||||||
- [ ] Расходники создаются как универсальный тип, классифицируются при заказе
|
- [ ] Расходники создаются как универсальный тип, классифицируются при заказе
|
||||||
- [ ] ТОВАР ≠ ПРОДУКТ (разные сущности в системе)
|
- [ ] ТОВАР ≠ ПРОДУКТ (разные сущности в системе)
|
||||||
|
- [ ] **В формах создания поставок товаров показываются ТОЛЬКО предметы типа ТОВАР** (`PRODUCT`)
|
||||||
|
- [ ] **В формах создания поставок расходников показываются ТОЛЬКО предметы типа РАСХОДНИКИ** (`CONSUMABLE`)
|
||||||
|
- [ ] **Фильтрация по типу предмета происходит на уровне GraphQL резолвера**, не на фронтенде
|
||||||
|
|
||||||
### ✅ Workflow статусов
|
### ✅ Workflow статусов
|
||||||
|
|
||||||
@ -121,6 +131,35 @@
|
|||||||
- [ ] Документирование всех API методов
|
- [ ] Документирование всех API методов
|
||||||
- [ ] Автоматическое тестирование при развертывании
|
- [ ] Автоматическое тестирование при развертывании
|
||||||
|
|
||||||
|
### ✅ GraphQL и TypeScript
|
||||||
|
|
||||||
|
- [ ] Имена полей в коде соответствуют GraphQL схеме (`myCounterparties`, не `getMyCounterparties`)
|
||||||
|
- [ ] Интерфейсы TypeScript соответствуют schema.prisma (`article`, не `sku`; `quantity`, не `stock`)
|
||||||
|
- [ ] При проблемах с GraphQL использовать `fetchPolicy: 'network-only'` для обхода кеша
|
||||||
|
- [ ] Добавлять логирование при отладке GraphQL запросов
|
||||||
|
- [ ] Проверка что резолверы вызываются (логи сервера)
|
||||||
|
- [ ] **Использовать специализированные запросы вместо общих** (`GET_ORGANIZATION_PRODUCTS` вместо `GET_ALL_PRODUCTS` для конкретного поставщика)
|
||||||
|
- [ ] **Обязательная фильтрация по типу предмета** в резолверах (`PRODUCT`/`CONSUMABLE`)
|
||||||
|
- [ ] **Параметр `organizationId` обязателен** для запросов товаров конкретной организации
|
||||||
|
|
||||||
|
### ✅ Система партнерства
|
||||||
|
|
||||||
|
- [ ] Поставщики в формах берутся только из партнеров с типом `WHOLESALE`
|
||||||
|
- [ ] Используется запрос `GET_MY_COUNTERPARTIES` с фильтрацией по типу
|
||||||
|
- [ ] НЕ используется `GET_SUPPLY_SUPPLIERS` для отображения поставщиков в формах
|
||||||
|
- [ ] Партнерство создается двумя способами: через заказ в маркете или через раздел "Партнеры"
|
||||||
|
- [ ] Двусторонние записи в таблице `Counterparty` при создании партнерства
|
||||||
|
|
||||||
|
### ✅ Архитектурные принципы GraphQL
|
||||||
|
|
||||||
|
- [ ] **Создавать специализированные резолверы** для каждого контекста использования
|
||||||
|
- [ ] **Использовать параметризованные запросы** (`organizationId`, `type`, `search`) вместо фильтрации на фронтенде
|
||||||
|
- [ ] **Добавлять подробное логирование** в резолверы для отладки (входные параметры, результаты фильтрации)
|
||||||
|
- [ ] **Типы запросов должны отражать бизнес-логику**: `organizationProducts` для товаров конкретной организации
|
||||||
|
- [ ] **Значения по умолчанию в резолверах** для критических параметров (`type: args.type || "PRODUCT"`)
|
||||||
|
- [ ] **Валидация обязательных параметров** на уровне схемы (`organizationId: ID!`)
|
||||||
|
- [ ] **Кеширование обходить при проблемах** через `fetchPolicy: 'network-only'`
|
||||||
|
|
||||||
## 🟢 РЕКОМЕНДУЕМЫЕ ПРОВЕРКИ
|
## 🟢 РЕКОМЕНДУЕМЫЕ ПРОВЕРКИ
|
||||||
|
|
||||||
### ✅ Пользовательский опыт
|
### ✅ Пользовательский опыт
|
||||||
@ -147,6 +186,12 @@
|
|||||||
8. Нарушать последовательность статусов
|
8. Нарушать последовательность статусов
|
||||||
9. Игнорировать валидацию
|
9. Игнорировать валидацию
|
||||||
10. Нарушать последовательность модулей в статистике фулфилмента
|
10. Нарушать последовательность модулей в статистике фулфилмента
|
||||||
|
11. **Использовать неправильные имена полей GraphQL** (`getMyCounterparties` вместо `myCounterparties`)
|
||||||
|
12. **Использовать GET_SUPPLY_SUPPLIERS для отображения поставщиков в формах** (только партнеры WHOLESALE)
|
||||||
|
13. **Создавать интерфейсы TypeScript не соответствующие schema.prisma** (`sku`/`stock` вместо `article`/`quantity`)
|
||||||
|
14. **Использовать общие запросы вместо специализированных** (`GET_ALL_PRODUCTS` вместо `GET_ORGANIZATION_PRODUCTS` для конкретного поставщика)
|
||||||
|
15. **Показывать расходники в формах создания поставок товаров** (строгая типизация `PRODUCT`/`CONSUMABLE`)
|
||||||
|
16. **Фильтровать предметы по типу на фронтенде** (фильтрация должна быть в GraphQL резолвере)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -174,5 +219,5 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
**ПРАВИЛО**: Перед каждым изменением кода проверить этот чеклист!
|
**ПРАВИЛО**: Перед каждым изменением кода проверить этот чеклист!
|
||||||
**ИСТОЧНИК ИСТИНЫ**: rules2.md
|
**ИСТОЧНИК ИСТИНЫ**: rules-unified.md (v4.0)
|
||||||
**СТАТУС**: ОБЯЗАТЕЛЬНЫЙ К ВЫПОЛНЕНИЮ
|
**СТАТУС**: ОБЯЗАТЕЛЬНЫЙ К ВЫПОЛНЕНИЮ
|
||||||
|
1626
rules-unified.md
Normal file
1626
rules-unified.md
Normal file
@ -0,0 +1,1626 @@
|
|||||||
|
# ПРАВИЛА СИСТЕМЫ УПРАВЛЕНИЯ СКЛАДАМИ И ПОСТАВКАМИ - ПОЛНАЯ БАЗА ЗНАНИЙ v3.0
|
||||||
|
|
||||||
|
> ⚠️ **ВАЖНОЕ ПРИМЕЧАНИЕ**: Данный файл является объединенной полной базой знаний системы, созданной на основе rules.md, rules1.md и rules2.md. Все изменения должны согласовываться.
|
||||||
|
|
||||||
|
## 🔤 ТЕРМИНЫ СИСТЕМЫ
|
||||||
|
> Для людей → `В коде`
|
||||||
|
- ТОВАР → `PRODUCT`
|
||||||
|
- РАСХОДНИКИ → `CONSUMABLE`
|
||||||
|
- БРАК → `DEFECT` *(планируется)*
|
||||||
|
- ПРОДУКТ → `FINISHED_PRODUCT` *(планируется)*
|
||||||
|
- ПОСТАВЩИК → `WHOLESALE`
|
||||||
|
- СЕЛЛЕР → `SELLER`
|
||||||
|
- ФУЛФИЛМЕНТ → `FULFILLMENT`
|
||||||
|
- ЛОГИСТИКА → `LOGIST`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📑 ОГЛАВЛЕНИЕ
|
||||||
|
|
||||||
|
### 🏗️ **АРХИТЕКТУРА И ОСНОВЫ**
|
||||||
|
|
||||||
|
1. [🎯 Основные принципы системы](#1--основные-принципы-системы)
|
||||||
|
2. [📦 Типизация предметов](#2--типизация-предметов)
|
||||||
|
3. [🏢 Структура кабинетов](#3--структура-кабинетов)
|
||||||
|
4. [🔐 Система ролей и доступов](#4--система-ролей-и-доступов)
|
||||||
|
|
||||||
|
### 🚚 **WORKFLOW И ПРОЦЕССЫ**
|
||||||
|
|
||||||
|
5. [🚚 Workflow поставок](#5--workflow-поставок)
|
||||||
|
6. [🔄 Процесс создания продукта](#6--процесс-создания-продукта)
|
||||||
|
7. [📊 Система учета движения товаров](#7--система-учета-движения-товаров)
|
||||||
|
|
||||||
|
### 🏢 **КАБИНЕТЫ СИСТЕМЫ**
|
||||||
|
|
||||||
|
8. [🏠 Общие правила кабинетов](#8--общие-правила-кабинетов)
|
||||||
|
9. [🏠 Кабинет селлера (детальные правила)](#9--кабинет-селлера-детальные-правила)
|
||||||
|
10. [🏪 Кабинет поставщика](#10--кабинет-поставщика)
|
||||||
|
11. [🏭 Кабинет фулфилмента](#11--кабинет-фулфилмента)
|
||||||
|
12. [🚚 Кабинет логистики](#12--кабинет-логистики)
|
||||||
|
|
||||||
|
### 🤝 **СИСТЕМА ПАРТНЕРСТВА**
|
||||||
|
|
||||||
|
13. [🤝 Система партнерства и контрагентов](#13--система-партнерства-и-контрагентов)
|
||||||
|
|
||||||
|
### 🌐 **ИНТЕГРАЦИИ И ФУНКЦИИ**
|
||||||
|
|
||||||
|
14. [🌐 Интеграции с системой](#14--интеграции-с-системой)
|
||||||
|
15. [📊 Статистика и аналитика](#15--статистика-и-аналитика)
|
||||||
|
16. [📱 Правила UI/UX и интерфейса](#16--правила-uiux-и-интерфейса)
|
||||||
|
17. [⚠️ Критические запреты](#17--критические-запреты)
|
||||||
|
|
||||||
|
### 🛠️ **ТЕХНИЧЕСКИЕ ПРАВИЛА**
|
||||||
|
|
||||||
|
18. [🛠️ GraphQL и TypeScript правила](#18--graphql-и-typescript-правила)
|
||||||
|
19. [🔧 Архитектурные принципы](#19--архитектурные-принципы)
|
||||||
|
20. [🎯 Правила качества кода](#20--правила-качества-кода)
|
||||||
|
21. [🔍 Диагностика и решение проблем](#21--диагностика-и-решение-проблем)
|
||||||
|
|
||||||
|
### 📋 **ПРИЛОЖЕНИЯ**
|
||||||
|
|
||||||
|
22. [📋 Категории товаров и расходников](#22--категории-товаров-и-расходников)
|
||||||
|
23. [🎖️ Приоритеты разработки](#23--приоритеты-разработки)
|
||||||
|
|
||||||
|
### 🌐 **ИНТЕГРАЦИИ И ФУНКЦИИ**
|
||||||
|
|
||||||
|
14. [🌐 Интеграции с системой](#14--интеграции-с-системой)
|
||||||
|
15. [📊 Статистика и аналитика](#15--статистика-и-аналитика)
|
||||||
|
16. [📱 Правила UI/UX и интерфейса](#16--правила-uiux-и-интерфейса)
|
||||||
|
17. [⚠️ Критические запреты](#17--критические-запреты)
|
||||||
|
|
||||||
|
### 🛠️ **ТЕХНИЧЕСКИЕ ПРАВИЛА**
|
||||||
|
|
||||||
|
18. [🛠️ GraphQL и TypeScript правила](#18--graphql-и-typescript-правила)
|
||||||
|
19. [🔧 Архитектурные принципы](#19--архитектурные-принципы)
|
||||||
|
20. [🎯 Правила качества кода](#20--правила-качества-кода)
|
||||||
|
21. [🔍 Диагностика и решение проблем](#21--диагностика-и-решение-проблем)
|
||||||
|
|
||||||
|
### 📋 **ПРИЛОЖЕНИЯ**
|
||||||
|
|
||||||
|
22. [📋 Категории товаров и расходников](#22--категории-товаров-и-расходников)
|
||||||
|
23. [🎖️ Приоритеты разработки](#23--приоритеты-разработки)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏷️ РЕЕСТР СУЩНОСТЕЙ СИСТЕМЫ
|
||||||
|
|
||||||
|
### 📦 **ОСНОВНЫЕ ПРЕДМЕТЫ**
|
||||||
|
|
||||||
|
| Сущность | Название в системе | Кабинет создания | Описание | Статус |
|
||||||
|
| ---------- | ---------------------------------- | ---------------- | ----------------------------------------------- | --------------- |
|
||||||
|
| Товар | `Product` (type: `PRODUCT`) | Поставщик | Базовый тип товара от поставщика | ✅ Реализовано |
|
||||||
|
| Расходники | `Product` (type: `CONSUMABLE`) | Поставщик | Материалы и вспомогательные товары | ✅ Реализовано |
|
||||||
|
| Брак | `Product` (type: `DEFECT`)* | Фулфилмент | Производная от товара с дефектами | 📋 Планируется |
|
||||||
|
| Продукт | `Product` (type: `FINISHED_PRODUCT`)* | Фулфилмент | Готовый к продаже товар (производная от товара) | 📋 Планируется |
|
||||||
|
|
||||||
|
> **\* Планируется**: Типы `DEFECT` и `FINISHED_PRODUCT` еще не добавлены в Prisma схему
|
||||||
|
|
||||||
|
### 🏢 **ОРГАНИЗАЦИИ И РОЛИ**
|
||||||
|
|
||||||
|
| Сущность | Название в системе | Основные функции | Статус |
|
||||||
|
| ---------- | ---------------------------------- | --------------------------------------- | -------------- |
|
||||||
|
| Поставщик | `Organization` (type: `WHOLESALE`) | Создание товаров, управление поставками | ✅ Реализовано |
|
||||||
|
| Селлер | `Organization` (type: `SELLER`) | Заказ товаров, управление поставками | ✅ Реализовано |
|
||||||
|
| Фулфилмент | `Organization` (type: `FULFILLMENT`) | Обработка товаров, управление складом | ✅ Реализовано |
|
||||||
|
| Логистика | `Organization` (type: `LOGIST`) | Управление доставками | ✅ Реализовано |
|
||||||
|
|
||||||
|
### 🤝 **СИСТЕМА ПАРТНЕРСТВА**
|
||||||
|
|
||||||
|
| Сущность | Название в системе | Описание | Статус |
|
||||||
|
| ------------ | ------------------ | ---------------------------------- | -------------- |
|
||||||
|
| Контрагент | `Counterparty` | Связь между организациями | ✅ Реализовано |
|
||||||
|
| Заявка | `CounterpartyRequest` | Запрос на сотрудничество | ✅ Реализовано |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 🎯 ОСНОВНЫЕ ПРИНЦИПЫ СИСТЕМЫ
|
||||||
|
|
||||||
|
### 1.1 Архитектура системы
|
||||||
|
|
||||||
|
**СТРУКТУРА СИСТЕМЫ ПО КАБИНЕТАМ:**
|
||||||
|
|
||||||
|
**🏢 КАБИНЕТ ПОСТАВЩИКА** - создает и управляет:
|
||||||
|
- **ТОВАР** (`PRODUCT`) - базовые товары от поставщика
|
||||||
|
- **РАСХОДНИКИ** (`CONSUMABLE`) - материалы и вспомогательные товары от поставщика
|
||||||
|
|
||||||
|
**🏭 КАБИНЕТ ФУЛФИЛМЕНТА** - принимает, обрабатывает и управляет всеми типами:
|
||||||
|
- **ТОВАР** (`PRODUCT`) - базовые товары от поставщиков (принятые на склад)
|
||||||
|
- **БРАК** (`DEFECT` - планируется) - производная от товара (товар с дефектами)
|
||||||
|
- **ПРОДУКТ** (`FINISHED_PRODUCT` - планируется) - готовый к продаже товар
|
||||||
|
- **РАСХОДНИКИ ФУЛФИЛМЕНТА** - операционные материалы фулфилмента
|
||||||
|
- **РАСХОДНИКИ СЕЛЛЕРОВ** - материалы для товаров селлеров
|
||||||
|
|
||||||
|
**🛍️ КАБИНЕТ СЕЛЛЕРА** - заказывает и управляет поставками:
|
||||||
|
- Создает заказы товаров и расходников
|
||||||
|
- Управляет поставками на фулфилмент и маркетплейсы
|
||||||
|
- Отслеживает статусы поставок
|
||||||
|
|
||||||
|
### 1.2 Основные принципы разработки
|
||||||
|
|
||||||
|
**КРИТИЧЕСКИ ВАЖНЫЕ ПРИНЦИПЫ:**
|
||||||
|
|
||||||
|
1. **Строгая типизация**: Каждый предмет имеет один из типов: ТОВАР (`PRODUCT`), РАСХОДНИКИ (`CONSUMABLE`), БРАК и ПРОДУКТ (планируется)
|
||||||
|
2. **Партнерская система**: Все связи между организациями через модель `Counterparty`
|
||||||
|
3. **Workflow статусов**: Строгая последовательность статусов поставок
|
||||||
|
4. **Безопасность доступа**: Контроль доступа на уровне GraphQL резолверов
|
||||||
|
5. **Единый источник истины**: Централизованное управление данными
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 📦 ТИПИЗАЦИЯ ПРЕДМЕТОВ
|
||||||
|
|
||||||
|
### 2.1 Два реализованных и два планируемых типа предметов
|
||||||
|
|
||||||
|
#### **1. ТОВАР** (`PRODUCT` - базовый тип)
|
||||||
|
|
||||||
|
- **СОЗДАЕТСЯ**: Поставщиком
|
||||||
|
- **СТАТУС**: Может быть активным/неактивным
|
||||||
|
- **ЗАКАЗ**: Доступен для заказа всеми типами организаций
|
||||||
|
- **ТРАНСФОРМАЦИЯ**: Может стать ПРОДУКТОМ или БРАКОМ
|
||||||
|
- **ЦЕНА**: Обязательна, больше 0
|
||||||
|
|
||||||
|
#### **2. РАСХОДНИКИ** (`CONSUMABLE` - базовый тип)
|
||||||
|
|
||||||
|
- **СОЗДАЕТСЯ**: Поставщиком как универсальный тип
|
||||||
|
- **КЛАССИФИКАЦИЯ ПРИ ЗАКАЗЕ**:
|
||||||
|
- Фулфилмент заказывает → "Расходники фулфилмента"
|
||||||
|
- Селлер заказывает → "Расходники селлеров"
|
||||||
|
- **ДОСТУП**: Видны всем типам организаций в маркете
|
||||||
|
|
||||||
|
#### **3. БРАК** (`DEFECT` - планируется, производная от товара)
|
||||||
|
|
||||||
|
- **БУДЕТ СОЗДАВАТЬСЯ**: Фулфилментом на основе существующего ТОВАРА при обнаружении дефектов
|
||||||
|
- **МОМЕНТ СОЗДАНИЯ**: В процессе "Создание продукта" / "В работе" после подсчета факта
|
||||||
|
- **СВЯЗЬ**: Обязательная связь с родительским товаром (parentId)
|
||||||
|
- **ЗАКАЗ**: ЗАПРЕЩЕН заказ брака
|
||||||
|
- **СТАТУС**: Всегда неактивен для заказа
|
||||||
|
- **ЦЕНА**: Для селлера - себестоимость дефектного товара, для фулфилмента - 0
|
||||||
|
- **WORKFLOW**: Особый процесс списания и утилизации
|
||||||
|
- **УЧЕТ**: Отдельный учет в статистике потерь
|
||||||
|
- **ОТОБРАЖЕНИЕ**: Виден только для учета потерь
|
||||||
|
- **⚠️ СТАТУС РАЗРАБОТКИ**: Тип `DEFECT` еще не добавлен в схему БД
|
||||||
|
|
||||||
|
#### **4. ПРОДУКТ** (`FINISHED_PRODUCT` - планируется, производная от товара)
|
||||||
|
|
||||||
|
- **БУДЕТ СОЗДАВАТЬСЯ**: Фулфилментом на основе ТОВАРА по заказу селлера
|
||||||
|
- **ИНИЦИАТОР**: Селлер создает заказ с рецептурой, фулфилмент исполняет
|
||||||
|
- **СВЯЗЬ**: Обязательная связь с родительским товаром (parentId)
|
||||||
|
- **РЕЦЕПТУРА**: Задается селлером при создании заказа (Товар + Услуга + Расходники)
|
||||||
|
- **СТАТУСЫ**: "в работе" → "готов к отправке"
|
||||||
|
- **ЗАКАЗ**: Доступен только в статусе "готов к отправке"
|
||||||
|
- **⚠️ СТАТУС РАЗРАБОТКИ**: Тип `FINISHED_PRODUCT` еще не добавлен в схему БД
|
||||||
|
|
||||||
|
### 2.2 Обязательные поля карточки
|
||||||
|
|
||||||
|
**КРИТИЧЕСКИ ВАЖНО**: Название, артикул, цена > 0, тип предмета
|
||||||
|
**ИСКЛЮЧЕНИЕ ДЛЯ БРАКА**: Цена может быть 0 для фулфилмента (себестоимость для селлера)
|
||||||
|
**ОБЯЗАТЕЛЬНО**: Количество (может быть 0 для предзаказа)
|
||||||
|
**ДЛЯ ПРОИЗВОДНЫХ ТИПОВ**: Обязательная связь с родительским предметом
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 🏢 СТРУКТУРА КАБИНЕТОВ
|
||||||
|
|
||||||
|
### 3.1 Общие принципы кабинетов
|
||||||
|
|
||||||
|
**КАЖДЫЙ КАБИНЕТ ИМЕЕТ:**
|
||||||
|
|
||||||
|
1. **Главная страница** (`/dashboard`) - общая информация и статистика
|
||||||
|
2. **Экономика** (`/economics`) - финансовая аналитика
|
||||||
|
3. **Универсальные разделы**:
|
||||||
|
- Маркет (`/market`) - просмотр и заказ товаров
|
||||||
|
- Партнеры (`/partners`) - управление контрагентами
|
||||||
|
- Мессенджер (`/messenger`) - связь между организациями
|
||||||
|
- Настройки (`/settings`) - профиль и API ключи
|
||||||
|
|
||||||
|
### 3.2 Специфические разделы по типам организаций
|
||||||
|
|
||||||
|
**🏪 ПОСТАВЩИК (`WHOLESALE`):**
|
||||||
|
- Склад (`/warehouse`) - управление товарами и расходниками
|
||||||
|
- Поставки (`/supplies`) - обработка заказов от селлеров
|
||||||
|
|
||||||
|
**🛍️ СЕЛЛЕР (`SELLER`):**
|
||||||
|
- Мои поставки (`/supplies`) - управление заказами товаров
|
||||||
|
- WB Интеграция (`/wb-integration`) - связь с Wildberries
|
||||||
|
|
||||||
|
**🏭 ФУЛФИЛМЕНТ (`FULFILLMENT`):**
|
||||||
|
- Склад фулфилмента (`/fulfillment-warehouse`) - управление всеми типами товаров
|
||||||
|
- Поставки фулфилмента (`/fulfillment-supplies`) - обработка поставок
|
||||||
|
- Услуги (`/services`) - управление услугами, логистикой, расходниками
|
||||||
|
- Сотрудники (`/employees`) - управление персоналом
|
||||||
|
- Статистика фулфилмента (`/fulfillment-statistics`) - детальная аналитика
|
||||||
|
|
||||||
|
**🚚 ЛОГИСТИКА (`LOGIST`):**
|
||||||
|
- Заявки (`/logistics-requests`) - управление заявками на доставку
|
||||||
|
- Маршруты (`/routes`) - планирование маршрутов
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 🔐 СИСТЕМА РОЛЕЙ И ДОСТУПОВ
|
||||||
|
|
||||||
|
### 4.1 Контроль доступа к разделам
|
||||||
|
|
||||||
|
**ПРОВЕРКА НА УРОВНЕ КОМПОНЕНТОВ:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{user?.organization?.type === "FULFILLMENT" && (
|
||||||
|
// Компоненты доступны только фулфилменту
|
||||||
|
)}
|
||||||
|
```
|
||||||
|
|
||||||
|
**СПЕЦИАЛЬНАЯ ЛОГИКА РОУТИНГА:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const handleSuppliesClick = () => {
|
||||||
|
switch (user?.organization?.type) {
|
||||||
|
case "FULFILLMENT":
|
||||||
|
router.push("/fulfillment-supplies");
|
||||||
|
break;
|
||||||
|
case "SELLER":
|
||||||
|
router.push("/supplies");
|
||||||
|
break;
|
||||||
|
// ... другие типы
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 GraphQL проверки доступа
|
||||||
|
|
||||||
|
**В Apollo Client запросах:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { data } = useQuery(GET_MY_SERVICES, {
|
||||||
|
skip: user?.organization?.type !== "FULFILLMENT",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**В GraphQL резолверах:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
if (currentUser.organization.type !== "FULFILLMENT") {
|
||||||
|
throw new GraphQLError("Доступно только для фулфилмент центров");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 Контроль доступа к заказам
|
||||||
|
|
||||||
|
- **Создатель заказа** - полный доступ к своим заказам
|
||||||
|
- **Поставщик** - доступ к заказам, где он является поставщиком
|
||||||
|
- **Фулфилмент-центр** - доступ к заказам, направленным в его центр
|
||||||
|
- **Логистическая компания** - доступ к заказам для доставки
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 🚚 WORKFLOW ПОСТАВОК
|
||||||
|
|
||||||
|
### 5.1 Детализированная система статусов
|
||||||
|
|
||||||
|
**Статусы SupplyOrder (Заказ поставки):**
|
||||||
|
|
||||||
|
1. **PENDING** - Ожидает подтверждения поставщиком
|
||||||
|
2. **SUPPLIER_APPROVED** - Одобрено поставщиком
|
||||||
|
3. **CONFIRMED** - Подтвержден (готов к обработке)
|
||||||
|
4. **LOGISTICS_CONFIRMED** - Подтверждено логистикой
|
||||||
|
5. **SHIPPED** - Отгружено поставщиком
|
||||||
|
6. **IN_TRANSIT** - В пути (логистика доставляет)
|
||||||
|
7. **DELIVERED** - Доставлен на фулфилмент
|
||||||
|
8. **CANCELLED** - Отменен
|
||||||
|
|
||||||
|
### 5.2 Пошаговый процесс поставки
|
||||||
|
|
||||||
|
**ЭТАП 1: Создание заказа**
|
||||||
|
1. Селлер заказывает товар/расходники у поставщика
|
||||||
|
2. Система создает SupplyOrder со статусом `PENDING`
|
||||||
|
3. Автоматическое уведомление поставщику
|
||||||
|
|
||||||
|
**ЭТАП 2: Обработка поставщиком**
|
||||||
|
4. Поставщик получает оповещение
|
||||||
|
5. Поставщик нажимает "Одобрить"
|
||||||
|
6. Статус меняется на `SUPPLIER_APPROVED`
|
||||||
|
|
||||||
|
**ЭТАП 3: Передача в фулфилмент**
|
||||||
|
7. Поставка отображается в кабинете фулфилмента
|
||||||
|
8. Фулфилмент выбирает ответственного и логистику
|
||||||
|
9. Статус меняется на `CONFIRMED`
|
||||||
|
|
||||||
|
**ЭТАП 4: Логистическое подтверждение**
|
||||||
|
10. Логистика подтверждает доставку
|
||||||
|
11. Статус меняется на `LOGISTICS_CONFIRMED`
|
||||||
|
|
||||||
|
**ЭТАП 5: Отгрузка**
|
||||||
|
12. Поставщик отгружает товар
|
||||||
|
13. Статус меняется на `SHIPPED`, затем `IN_TRANSIT`
|
||||||
|
|
||||||
|
**ЭТАП 6: Доставка и приемка**
|
||||||
|
14. Логистика доставляет на фулфилмент
|
||||||
|
15. Фулфилмент принимает товар
|
||||||
|
16. Статус меняется на `DELIVERED`
|
||||||
|
|
||||||
|
### 5.3 Система уведомлений
|
||||||
|
|
||||||
|
**Обязательные уведомления:**
|
||||||
|
- Поставщику: о новом заказе
|
||||||
|
- Фулфилменту: о подтвержденной поставке
|
||||||
|
- Логистике: о назначении на заявку
|
||||||
|
- Селлеру: об изменении каждого статуса
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 🔄 ПРОЦЕСС СОЗДАНИЯ ПРОДУКТА
|
||||||
|
|
||||||
|
### 6.1 Workflow создания продукта
|
||||||
|
|
||||||
|
**ЭТАПЫ ПРОЦЕССА:**
|
||||||
|
|
||||||
|
1. **ПОСТУПЛЕНИЕ**: Товар приходит на склад фулфилмента (статус "на складе")
|
||||||
|
2. **СОРТИРОВКА**: Перебор товара, отсеивание брака
|
||||||
|
3. **ПОДГОТОВКА К РАБОТЕ**: Менеджер задает параметры работы
|
||||||
|
4. **ОБРАБОТКА**: Превращение товара в продукт по "рецептуре" селлера
|
||||||
|
5. **ЗАВЕРШЕНИЕ**: Готовый продукт (статус "готов к отправке")
|
||||||
|
|
||||||
|
### 6.2 Управление процессом
|
||||||
|
|
||||||
|
**РАЗДЕЛ "СОЗДАНИЕ ПРОДУКТА":**
|
||||||
|
|
||||||
|
- **НОВЫЕ**: Поставки после нажатия "принято", ожидающие назначения
|
||||||
|
- **В РАБОТЕ**: Поставки в процессе обработки
|
||||||
|
- **ВЫПОЛНЕНО**: Завершенные поставки с готовыми продуктами
|
||||||
|
|
||||||
|
**ДЕЙСТВИЯ МЕНЕДЖЕРА В "НОВЫЕ":**
|
||||||
|
|
||||||
|
- **ДЕДЛАЙН**: Указание срока выполнения работы
|
||||||
|
- **ОТВЕТСТВЕННЫЙ**: Назначение исполнителя
|
||||||
|
- **МЕСТО ХРАНЕНИЯ**: Указание локации для готовых продуктов (опционально)
|
||||||
|
- **ЗАПУСК**: Нажатие кнопки "В работе"
|
||||||
|
|
||||||
|
### 6.3 Рецептура продукта
|
||||||
|
|
||||||
|
**СЕЛЛЕР УКАЗЫВАЕТ В ПОСТАВКЕ:**
|
||||||
|
|
||||||
|
- **БАЗОВЫЙ ТОВАР**: Исходный материал (например, футболка)
|
||||||
|
- **УСЛУГА ФУЛФИЛМЕНТА**: Из каталога услуг (например, "погладить")
|
||||||
|
- **РАСХОДНИК СЕЛЛЕРА**: Материалы селлера (например, фирменный пакет)
|
||||||
|
- **РАСХОДНИК ФУЛФИЛМЕНТА**: Материалы фулфилмента (например, короб + маркировка)
|
||||||
|
|
||||||
|
**РЕЗУЛЬТАТ**: ПРОДУКТ = Товар + Услуга + Расходники
|
||||||
|
|
||||||
|
### 6.4 Учет план/факт в процессе работы
|
||||||
|
|
||||||
|
**ПЛАН**: Количество товара из поставки селлера
|
||||||
|
**ФАКТ**: Реальное количество после сортировки
|
||||||
|
|
||||||
|
**ФИКСАЦИЯ БРАКА:**
|
||||||
|
- **КОГДА**: В процессе работы (вкладка "В работе")
|
||||||
|
- **КТО**: Ответственный исполнитель
|
||||||
|
- **ДЕТАЛИЗАЦИЯ**: По каждому предмету (размер/объем)
|
||||||
|
- **ОБНОВЛЕНИЕ**: Можно вносить изменения до нажатия "Выполнено"
|
||||||
|
|
||||||
|
**ВЛИЯНИЕ НА СТАТИСТИКУ:**
|
||||||
|
- При принятии поставки: +План в статистику
|
||||||
|
- При выявлении факта: корректировка на реальные данные
|
||||||
|
- **ФОРМУЛА**: Факт = Брак + Хороший товар
|
||||||
|
- **ЛОГИКА**: Фактическое количество = сумма всех пересчитанных предметов
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 📊 СИСТЕМА УЧЕТА ДВИЖЕНИЯ ТОВАРОВ
|
||||||
|
|
||||||
|
### 7.1 Принципы учета в фулфилменте
|
||||||
|
|
||||||
|
**ОСНОВНЫЕ ПРИНЦИПЫ:**
|
||||||
|
|
||||||
|
- **ПРИХОД**: Товары поступают через принятые поставки (из состояния "в пути" → "на складе")
|
||||||
|
- **ОБРАБОТКА**: Товары переходят в статус "в работе" для создания продуктов
|
||||||
|
- **РАСХОД**: Товары убывают при отгрузке, списании, возврате, превращении в продукты
|
||||||
|
- **УЧЁТ**: Ведется учет прихода и расхода для каждого типа предметов
|
||||||
|
- **ВИЗУАЛИЗАЦИЯ**: Движение отображается в дополнительных значениях
|
||||||
|
|
||||||
|
### 7.2 Дополнительные и основные значения
|
||||||
|
|
||||||
|
**ДОПОЛНИТЕЛЬНЫЕ ЗНАЧЕНИЯ (показатели движения):**
|
||||||
|
|
||||||
|
- **ПРИБЫЛО**: Количество предметов, поступивших на склад
|
||||||
|
- **УБЫЛО**: Количество предметов, списанных со склада
|
||||||
|
- **ВЛИЯНИЕ**: От этих значений зависят основные значения (общее количество)
|
||||||
|
|
||||||
|
**ОСНОВНЫЕ ЗНАЧЕНИЯ (текущие остатки):**
|
||||||
|
|
||||||
|
- **ОПРЕДЕЛЕНИЕ**: Итоговое количество предметов на складе
|
||||||
|
- **РАСЧЁТ**: Основные значения = Предыдущие остатки + Прибыло - Убыло
|
||||||
|
- **ОТОБРАЖЕНИЕ**: Показываются в каждом модуле статистики
|
||||||
|
- **РАЗДЕЛЕНИЕ ТОВАРОВ**:
|
||||||
|
- Товары "на складе" - готовы к обработке
|
||||||
|
- Товары "в обработке" - находятся в процессе создания продукта
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 🏠 ОБЩИЕ ПРАВИЛА КАБИНЕТОВ
|
||||||
|
|
||||||
|
### 8.1 Универсальная структура кабинетов
|
||||||
|
|
||||||
|
**ВСЕ ТИПЫ КАБИНЕТОВ** включают следующие обязательные разделы:
|
||||||
|
|
||||||
|
#### 8.1.1 Страница "Главная"
|
||||||
|
|
||||||
|
**СТАТУС**: Реализовано
|
||||||
|
**ДОСТУП**: Через навигацию в sidebar для всех типов кабинетов
|
||||||
|
**СОДЕРЖАНИЕ**: Универсальная страница с типо-зависимыми компонентами
|
||||||
|
|
||||||
|
**ПРАВИЛА**:
|
||||||
|
|
||||||
|
- **ОБЯЗАТЕЛЬНО**: Каждый тип кабинета должен иметь страницу "Главная"
|
||||||
|
- **НАВИГАЦИЯ**: Доступ через кнопку в sidebar (первая в списке)
|
||||||
|
- **УНИВЕРСАЛЬНОСТЬ**: Одинаковая структура навигации для всех кабинетов
|
||||||
|
- **РОУТ**: `/home` с универсальным компонентом HomePageWrapper
|
||||||
|
- **КОМПОНЕНТЫ**: 4 типо-зависимых компонента: SellerHomePage, FulfillmentHomePage, WholesaleHomePage, LogistHomePage
|
||||||
|
|
||||||
|
#### 8.1.2 Раздел "Экономика"
|
||||||
|
|
||||||
|
**СТАТУС**: Реализовано в системе
|
||||||
|
**РАСПОЛОЖЕНИЕ**: Перед настройками в каждом кабинете
|
||||||
|
**СОДЕРЖАНИЕ**: Пустые разделы-заглушки с пометкой "будет добавлен позже"
|
||||||
|
|
||||||
|
**ПРАВИЛА**:
|
||||||
|
|
||||||
|
- **ОБЯЗАТЕЛЬНО**: Каждый кабинет имеет раздел "Экономика"
|
||||||
|
- **РОУТ**: `/economics` с универсальным компонентом EconomicsPageWrapper
|
||||||
|
- **КОМПОНЕНТЫ**: 4 типо-зависимых компонента экономики: SellerEconomicsPage, FulfillmentEconomicsPage, WholesaleEconomicsPage, LogistEconomicsPage
|
||||||
|
- **КНОПКА**: "Экономика" в sidebar навигации перед настройками
|
||||||
|
- **БЕЗОПАСНОСТЬ**: Проверки доступа и безопасности в экономических компонентах
|
||||||
|
|
||||||
|
#### 8.1.3 Общие разделы для всех кабинетов
|
||||||
|
|
||||||
|
**УНИВЕРСАЛЬНЫЕ РАЗДЕЛЫ** (доступны всем типам):
|
||||||
|
|
||||||
|
- 🏠 **Главная** - основная страница кабинета (реализовано)
|
||||||
|
- 🛒 **Маркет** - просмотр и заказ товаров
|
||||||
|
- 🤝 **Партнеры** - управление контрагентами
|
||||||
|
- 💬 **Мессенджер** - внутренняя связь
|
||||||
|
- 💰 **Экономика** - финансовая аналитика (реализовано)
|
||||||
|
- ⚙️ **Настройки** - профиль и конфигурация
|
||||||
|
|
||||||
|
**СПЕЦИАЛИЗИРОВАННЫЕ РАЗДЕЛЫ** (зависят от типа кабинета):
|
||||||
|
- Определяются в соответствующих разделах каждого кабинета
|
||||||
|
|
||||||
|
### 8.2 Правила sidebar навигации
|
||||||
|
|
||||||
|
#### 8.2.1 Структура навигации
|
||||||
|
|
||||||
|
**ОБЩИЙ ПРИНЦИП**:
|
||||||
|
|
||||||
|
- Условное отображение: `{user?.organization?.type === "TYPE" && (...)}`
|
||||||
|
- Адаптивность: сворачиваемый sidebar с `getSidebarMargin()`
|
||||||
|
- Состояния активности: подсветка текущего раздела
|
||||||
|
|
||||||
|
**ПОРЯДОК РАЗДЕЛОВ В SIDEBAR**:
|
||||||
|
|
||||||
|
1. 🏠 **Главная** (реализовано для всех)
|
||||||
|
2. **Специализированные разделы** (зависят от типа кабинета)
|
||||||
|
3. 🛒 **Маркет** (универсальный)
|
||||||
|
4. 🤝 **Партнеры** (универсальный)
|
||||||
|
5. 💬 **Мессенджер** (универсальный)
|
||||||
|
6. 💰 **Экономика** (универсальный, реализовано)
|
||||||
|
7. ⚙️ **Настройки** (универсальный)
|
||||||
|
8. **Выход** (универсальный)
|
||||||
|
|
||||||
|
#### 8.2.2 Типо-зависимая логика
|
||||||
|
|
||||||
|
**АДАПТИВНЫЙ РОУТИНГ**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Пример: кнопка "Поставки" ведет на разные страницы
|
||||||
|
const handleSuppliesClick = () => {
|
||||||
|
switch (user?.organization?.type) {
|
||||||
|
case "FULFILLMENT":
|
||||||
|
router.push("/fulfillment-supplies");
|
||||||
|
break;
|
||||||
|
case "SELLER":
|
||||||
|
router.push("/supplies");
|
||||||
|
break;
|
||||||
|
case "WHOLESALE":
|
||||||
|
router.push("/supplies");
|
||||||
|
break;
|
||||||
|
case "LOGIST":
|
||||||
|
router.push("/logistics-orders");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 🏠 КАБИНЕТ СЕЛЛЕРА (ДЕТАЛЬНЫЕ ПРАВИЛА)
|
||||||
|
|
||||||
|
### 9.1 Структура раздела "Мои поставки"
|
||||||
|
|
||||||
|
#### **🏢 ПОСТАВКИ НА ФУЛФИЛМЕНТ**:
|
||||||
|
|
||||||
|
- **Товар** - поставка товаров для создания продуктов
|
||||||
|
- **Карточки** - поставка через WB API с рецептурой (результат: WildberriesSupply)
|
||||||
|
- **Поставщики** - заказ товаров у поставщиков с рецептурой (результат: SupplyOrder)
|
||||||
|
- **Расходники селлера** - поставка материалов для товаров селлера
|
||||||
|
|
||||||
|
#### **🛒 ПОСТАВКИ НА МАРКЕТПЛЕЙСЫ** _(планируется)_:
|
||||||
|
|
||||||
|
- **Wildberries** - прямые поставки на WB
|
||||||
|
- **Ozon** - прямые поставки на Ozon
|
||||||
|
|
||||||
|
### 9.2 Правила кнопки "Создать поставку" в разделе "Мои поставки"
|
||||||
|
|
||||||
|
#### **9.2.1 Общие принципы**
|
||||||
|
|
||||||
|
- **КОНТЕКСТНОСТЬ**: Кнопка создания появляется только в активном табе
|
||||||
|
- **РАСПОЛОЖЕНИЕ**: Правая часть строки таба, на том же уровне что и название
|
||||||
|
- **СТИЛИСТИКА**: В том же стиле что и сами табы (соответствует уровню иерархии)
|
||||||
|
- **ФУНКЦИОНАЛЬНОСТЬ**: Кнопка ведет на страницу создания поставки соответствующего типа
|
||||||
|
|
||||||
|
#### **9.2.2 Размещение кнопок по табам**
|
||||||
|
|
||||||
|
**УРОВЕНЬ 2 (Подтабы фулфилмента):**
|
||||||
|
|
||||||
|
- **📦 Товар → Карточки**: Кнопка "Создать поставку" → `/supplies/create-cards`
|
||||||
|
- **📦 Товар → Поставщики**: Кнопка "Создать поставку" → `/supplies/create-suppliers`
|
||||||
|
- **🔧 Расходники селлера**: Кнопка "Создать поставку" → `/supplies/create-consumables`
|
||||||
|
|
||||||
|
**УРОВЕНЬ 2 (Подтабы маркетплейсов):**
|
||||||
|
|
||||||
|
- **🟣 Wildberries**: Кнопка "Создать поставку" → `/supplies/create-wildberries`
|
||||||
|
- **🔵 Ozon**: Кнопка "Создать поставку" → `/supplies/create-ozon`
|
||||||
|
|
||||||
|
#### **9.2.3 Стили кнопок**
|
||||||
|
|
||||||
|
**ДЛЯ УРОВНЯ 2 (h-9):**
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Размер и отступы */
|
||||||
|
h-9 px-3 py-1 ml-auto
|
||||||
|
|
||||||
|
/* Фон и границы */
|
||||||
|
bg-white/8 border border-white/20 hover:bg-white/12
|
||||||
|
|
||||||
|
/* Текст и иконки */
|
||||||
|
text-xs font-medium text-white/80 hover:text-white
|
||||||
|
|
||||||
|
/* Скругления */
|
||||||
|
rounded-lg
|
||||||
|
|
||||||
|
/* Переходы */
|
||||||
|
transition-all duration-150
|
||||||
|
```
|
||||||
|
|
||||||
|
**ДЛЯ УРОВНЯ 3 (h-8):**
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Размер и отступы */
|
||||||
|
h-8 px-2 py-1 ml-auto
|
||||||
|
|
||||||
|
/* Фон и границы */
|
||||||
|
bg-white/5 border border-white/15 hover:bg-white/8
|
||||||
|
|
||||||
|
/* Текст и иконки */
|
||||||
|
text-xs font-normal text-white/60 hover:text-white/80
|
||||||
|
|
||||||
|
/* Скругления */
|
||||||
|
rounded-md
|
||||||
|
|
||||||
|
/* Переходы */
|
||||||
|
transition-all duration-150
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **9.2.4 Поведение кнопок**
|
||||||
|
|
||||||
|
- **ВИДИМОСТЬ**: Кнопка показывается только в активном табе
|
||||||
|
- **ИКОНКА**: `Plus` размером `h-3 w-3` слева от текста
|
||||||
|
- **ТЕКСТ**: "Создать" на мобильных, "Создать поставку" на десктопах
|
||||||
|
- **АДАПТИВНОСТЬ**: Скрытие текста на маленьких экранах (`hidden sm:inline`)
|
||||||
|
|
||||||
|
#### **9.2.5 Удаление старой кнопки**
|
||||||
|
|
||||||
|
- **УБРАТЬ**: Текущий dropdown "Создать поставку" из верхней части
|
||||||
|
- **ПРИЧИНА**: Заменяется контекстными кнопками в табах
|
||||||
|
- **СОХРАНИТЬ**: Стили и логику навигации, но адаптировать под новые роуты
|
||||||
|
|
||||||
|
### 9.3 Структура страницы "Мои поставки" - Трёхблочная архитектура
|
||||||
|
|
||||||
|
#### **9.3.1 Обязательная структура страницы**
|
||||||
|
|
||||||
|
**ПРИНЦИП**: Страница состоит из трёх визуально разделённых блоков
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ 1. БЛОК ТАБОВ (навигация) │
|
||||||
|
│ - Фиксированная высота │
|
||||||
|
│ - Glass-эффект │
|
||||||
|
│ - Иерархическая структура │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ 2. БЛОК СТАТИСТИКИ (метрики) │
|
||||||
|
│ - Контекстные данные │
|
||||||
|
│ - 4 карточки в ряд (desktop) │
|
||||||
|
│ - Динамическое обновление │
|
||||||
|
├─────────────────────────────────────────┤
|
||||||
|
│ 3. ОСНОВНОЙ БЛОК (контент) │
|
||||||
|
│ - Сохраняет весь функционал │
|
||||||
|
│ - Таблицы, фильтры, действия │
|
||||||
|
│ - Высота до низа sidebar │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **9.3.2 Блок статистики - контекстные метрики**
|
||||||
|
|
||||||
|
**ПРАВИЛО**: Статистика меняется в зависимости от выбранных табов
|
||||||
|
|
||||||
|
**Для путей "Фулфилмент → Товар → Карточки/Поставщики":**
|
||||||
|
- Всего поставок
|
||||||
|
- Активных поставок
|
||||||
|
- Сумма активных поставок
|
||||||
|
- В пути
|
||||||
|
|
||||||
|
**Для пути "Фулфилмент → Расходники селлера":**
|
||||||
|
- Всего поставок
|
||||||
|
- Активных поставок
|
||||||
|
- Видов расходников
|
||||||
|
- Критические остатки
|
||||||
|
|
||||||
|
**Для путей "Маркетплейсы → Wildberries/Ozon":**
|
||||||
|
- Поставок на маркетплейс
|
||||||
|
- Товаров отправлено
|
||||||
|
- Возвраты за неделю
|
||||||
|
- Эффективность поставок
|
||||||
|
|
||||||
|
#### **9.3.3 Высота основного блока**
|
||||||
|
|
||||||
|
**ФОРМУЛА РАСЧЕТА**:
|
||||||
|
```css
|
||||||
|
height: calc(100vh - headerHeight - tabsHeight - statsHeight - margins)
|
||||||
|
```
|
||||||
|
|
||||||
|
**ПРАВИЛО ВЫРАВНИВАНИЯ**:
|
||||||
|
- Нижняя граница основного блока должна быть на одном уровне с нижней границей sidebar
|
||||||
|
- При изменении размера окна высота пересчитывается
|
||||||
|
- Внутренний скролл: `overflow-y-auto`
|
||||||
|
|
||||||
|
#### **9.3.4 Сохранение функционала**
|
||||||
|
|
||||||
|
**КРИТИЧЕСКИ ВАЖНО**: При добавлении блока статистики весь существующий функционал сохраняется:
|
||||||
|
- Таблицы с данными поставок
|
||||||
|
- Фильтры и сортировка
|
||||||
|
- Кнопки действий
|
||||||
|
- Детализация при клике
|
||||||
|
- Пагинация
|
||||||
|
- Поиск
|
||||||
|
|
||||||
|
**ЗАПРЕЩЕНО**:
|
||||||
|
- Удалять существующие компоненты
|
||||||
|
- Изменять логику работы таблиц
|
||||||
|
- Нарушать существующие API вызовы
|
||||||
|
|
||||||
|
### 9.4 Табы "Карточки" и "Поставщики" - объединённая логика
|
||||||
|
|
||||||
|
#### **9.4.1 Принцип единого типа предмета**
|
||||||
|
|
||||||
|
**КЛЮЧЕВОЕ ПРАВИЛО**: Табы "Карточки" и "Поставщики" - это два способа создания поставок одного типа предмета (ТОВАР)
|
||||||
|
|
||||||
|
**СПОСОБЫ СОЗДАНИЯ**:
|
||||||
|
- **Карточки** - импорт товаров через WB API с автоматическим созданием поставки
|
||||||
|
- **Поставщики** - прямой заказ товаров у поставщика с указанием рецептуры
|
||||||
|
|
||||||
|
**РЕЗУЛЬТАТ**: Оба способа создают `SupplyOrder` с товарами типа `PRODUCT`
|
||||||
|
|
||||||
|
#### **9.4.2 Общая статистика**
|
||||||
|
|
||||||
|
**ПРАВИЛО**: Блок статистики показывает ОДИНАКОВЫЕ данные для обоих табов
|
||||||
|
|
||||||
|
**МЕТРИКИ ДЛЯ ТАБОВ "КАРТОЧКИ" И "ПОСТАВЩИКИ"**:
|
||||||
|
- Всего поставок товаров (из всех источников)
|
||||||
|
- Активных поставок товаров (в работе)
|
||||||
|
- Сумма активных поставок товаров
|
||||||
|
- Товаров в пути (все способы доставки)
|
||||||
|
|
||||||
|
**ЗАПРЕЩЕНО**: Разделять статистику по способу создания
|
||||||
|
|
||||||
|
#### **9.4.3 Общий основной блок**
|
||||||
|
|
||||||
|
**СОДЕРЖИМОЕ**: Единая таблица всех поставок товаров
|
||||||
|
|
||||||
|
**ИСТОЧНИКИ ДАННЫХ**:
|
||||||
|
- Поставки, созданные через импорт карточек WB
|
||||||
|
- Поставки, созданные через заказ у поставщиков
|
||||||
|
- Все промежуточные и завершённые поставки
|
||||||
|
|
||||||
|
**РАЗЛИЧИЯ ТАБОВ**:
|
||||||
|
- Только кнопки создания ведут на разные страницы
|
||||||
|
- Таб "Карточки": `/supplies/create-cards`
|
||||||
|
- Таб "Поставщики": `/supplies/create-suppliers`
|
||||||
|
|
||||||
|
### 9.5 Создание поставки расходников селлера
|
||||||
|
|
||||||
|
#### **Структура страницы**:
|
||||||
|
|
||||||
|
**БЛОК 1: ПОСТАВЩИКИ** _(обязательный, верхняя часть)_:
|
||||||
|
|
||||||
|
- Карточки поставщиков из раздела "Партнеры"
|
||||||
|
- Горизонтальный скролл при превышении ширины
|
||||||
|
- Выбор только одного поставщика одновременно
|
||||||
|
|
||||||
|
**БЛОК 2: РАСХОДНИКИ** _(зависимый, центральная часть)_:
|
||||||
|
|
||||||
|
- Активен только после выбора поставщика
|
||||||
|
- Сортировка: цена, название, категория
|
||||||
|
- Фильтры: категория, ценовой диапазон
|
||||||
|
- Карточка с полем ввода количества и кнопками +/-
|
||||||
|
|
||||||
|
**БЛОК 3: КОРЗИНА** _(правая часть)_:
|
||||||
|
|
||||||
|
- **РАСПОЛОЖЕНИЕ**: Правая часть экрана
|
||||||
|
- **СОДЕРЖАНИЕ**:
|
||||||
|
- Счетчик видов расходников
|
||||||
|
- Детализация по каждому расходнику (название, количество, цена, сумма)
|
||||||
|
- Общая сумма всех расходников
|
||||||
|
- **УПРАВЛЕНИЕ**:
|
||||||
|
- Изменение количества (с валидацией остатков)
|
||||||
|
- Удаление позиций
|
||||||
|
- **ОБЯЗАТЕЛЬНЫЕ ПОЛЯ**:
|
||||||
|
- Выбор фулфилмент-центра (из партнеров)
|
||||||
|
- Дата поставки (не прошедшая, по умолчанию - текущая)
|
||||||
|
|
||||||
|
### 9.6 Многоуровневая таблица поставок
|
||||||
|
|
||||||
|
#### **ПЕРВЫЙ УРОВЕНЬ** _(основной список)_:
|
||||||
|
|
||||||
|
- **СОРТИРОВКА**: Номер поставки от большего к меньшему
|
||||||
|
- **ОБЯЗАТЕЛЬНЫЕ КОЛОНКИ**:
|
||||||
|
- Порядковый номер поставки
|
||||||
|
- Количество видов расходников
|
||||||
|
- Стоимость всей поставки
|
||||||
|
- Количество категорий
|
||||||
|
- Статус поставки
|
||||||
|
|
||||||
|
#### **ВТОРОЙ УРОВЕНЬ** _(детализация по клику)_:
|
||||||
|
|
||||||
|
- **АКТИВАЦИЯ**: По клику на строку первого уровня
|
||||||
|
- **СОДЕРЖАНИЕ**:
|
||||||
|
- Название расходника
|
||||||
|
- Количество
|
||||||
|
- Цена
|
||||||
|
- Категория
|
||||||
|
- Поставщик
|
||||||
|
- **ОГРАНИЧЕНИЯ**: Только просмотр, редактирование запрещено
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 🏪 КАБИНЕТ ПОСТАВЩИКА
|
||||||
|
|
||||||
|
### 10.1 Основные возможности
|
||||||
|
|
||||||
|
**СОЗДАНИЕ КАРТОЧЕК**:
|
||||||
|
|
||||||
|
- **ТОВАР** - базовые товары поставщика
|
||||||
|
- **РАСХОДНИКИ** - материалы и вспомогательные товары
|
||||||
|
|
||||||
|
### 10.2 Обязательные поля карточки
|
||||||
|
|
||||||
|
**Базовые параметры**:
|
||||||
|
|
||||||
|
- Фото (система загрузки и управления изображениями)
|
||||||
|
- Название
|
||||||
|
- Автоматическая генерация артикула СФ
|
||||||
|
- Описание
|
||||||
|
- Количество предметов в единицах
|
||||||
|
- Количество комплектов (если применимо)
|
||||||
|
- Категория (28 предустановленных + специализированные для расходников)
|
||||||
|
- Бренд, Цвет, Размер/объем, Вес, Габариты, Материал
|
||||||
|
- Цена за единицу и за комплект
|
||||||
|
- Заказано, В пути, Остаток, Продано
|
||||||
|
|
||||||
|
### 10.3 Отображение информации в карточках
|
||||||
|
|
||||||
|
**Каждая карточка содержит**:
|
||||||
|
|
||||||
|
- Основное изображение
|
||||||
|
- Название и артикул СФ
|
||||||
|
- Цена за единицу/комплект
|
||||||
|
- Категория и статус активности
|
||||||
|
- Данные о движении: остаток, заказано, в пути, продано
|
||||||
|
- Индикаторы низких остатков
|
||||||
|
|
||||||
|
### 10.4 Статистика поставщика
|
||||||
|
|
||||||
|
**Блок статистики включает**:
|
||||||
|
|
||||||
|
- **ТОВАРЫ**: Общая статистика товаров поставщика
|
||||||
|
- **РАСХОДНИКИ**: Материалы и вспомогательные товары
|
||||||
|
- Классифицируются при заказе в зависимости от заказчика
|
||||||
|
- Общая статистика по всем расходникам
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 🏭 КАБИНЕТ ФУЛФИЛМЕНТА
|
||||||
|
|
||||||
|
### 11.1 Структура раздела склад фулфилмента
|
||||||
|
|
||||||
|
#### **Модули в обязательной последовательности**:
|
||||||
|
|
||||||
|
1. **📦 ПРОДУКТ** - готовые к продаже товары
|
||||||
|
2. **🛒 ТОВАР** - базовые товары от поставщиков
|
||||||
|
- Товары "на складе" - готовы к обработке
|
||||||
|
- Товары "в обработке" - в процессе создания продукта
|
||||||
|
3. **❌ БРАК** - товары с дефектами
|
||||||
|
4. **↩️ ВОЗВРАТЫ С ПВЗ** - возвращенные товары
|
||||||
|
5. **🎯 РАСХОДНИКИ СЕЛЛЕРОВ** - материалы для селлеров
|
||||||
|
6. **⚙️ РАСХОДНИКИ ФУЛФИЛМЕНТ** - операционные материалы
|
||||||
|
- **КЛИКАБЕЛЬНЫЙ МОДУЛЬ** - содержит полноценный раздел учёта
|
||||||
|
|
||||||
|
### 11.2 Движение товаров в фулфилменте
|
||||||
|
|
||||||
|
#### **Поступление товаров**:
|
||||||
|
|
||||||
|
- **ПОСТАВКИ**: От поставщиков через систему заказов
|
||||||
|
- **ВОЗВРАТЫ**: Товары, возвращенные с ПВЗ
|
||||||
|
- **ПЕРЕМЕЩЕНИЯ**: Между складами и магазинами
|
||||||
|
|
||||||
|
#### **Расход товаров**:
|
||||||
|
|
||||||
|
- **ОТГРУЗКА**: Товары отправлены селлерам
|
||||||
|
- **СПИСАНИЕ**: Брак, утрата, утилизация
|
||||||
|
- **ВОЗВРАТ**: Возврат поставщику
|
||||||
|
- **ИСПОЛЬЗОВАНИЕ**: Расходники для операций
|
||||||
|
|
||||||
|
### 11.3 Модуль "Расходники фулфилмента"
|
||||||
|
|
||||||
|
**ОСОБЕННОСТИ**:
|
||||||
|
|
||||||
|
- **ИНТЕРАКТИВНОСТЬ**: Кликабельный элемент в статистике
|
||||||
|
- **ФУНКЦИОНАЛЬНОСТЬ**: Полноценный раздел учёта
|
||||||
|
- **СОДЕРЖАНИЕ**: Управление расходниками фулфилмента
|
||||||
|
|
||||||
|
### 11.4 Блок детализации по магазинам
|
||||||
|
|
||||||
|
**НАЗНАЧЕНИЕ**: Распределение товаров по торговым точкам/магазинам
|
||||||
|
|
||||||
|
**ФУНКЦИИ**:
|
||||||
|
|
||||||
|
- **ОСТАТКИ ПО МАГАЗИНАМ**: Отображение количества товаров в каждом магазине
|
||||||
|
- **УПРАВЛЕНИЕ РАСПРЕДЕЛЕНИЕМ**: Перемещение товаров между точками
|
||||||
|
- **КОНТРОЛЬ ДВИЖЕНИЯ**: Отслеживание перемещений между складами и магазинами
|
||||||
|
- **АНАЛИТИКА**: Сравнение эффективности разных точек
|
||||||
|
- **ПЛАНИРОВАНИЕ**: Оптимизация распределения товаров
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 🚚 КАБИНЕТ ЛОГИСТИКИ
|
||||||
|
|
||||||
|
### 12.1 Основные функции логистики
|
||||||
|
|
||||||
|
**РОЛЬ В СИСТЕМЕ**: Управление доставками и транспортировкой
|
||||||
|
|
||||||
|
**ОСНОВНЫЕ ФУНКЦИИ**:
|
||||||
|
|
||||||
|
- **ПОДТВЕРЖДЕНИЕ ДОСТАВКИ**: Подтверждение возможности доставки поставок
|
||||||
|
- **ТРАНСПОРТИРОВКА**: Организация и выполнение доставки товаров
|
||||||
|
- **КОНТРОЛЬ МАРШРУТОВ**: Управление логистическими маршрутами
|
||||||
|
- **ОТСЛЕЖИВАНИЕ**: Мониторинг грузов в пути
|
||||||
|
|
||||||
|
### 12.2 Workflow для логистики
|
||||||
|
|
||||||
|
#### **ЭТАП 1: Получение заявки**
|
||||||
|
|
||||||
|
1. Логистика получает уведомление о новой поставке
|
||||||
|
2. Заявка появляется в разделе "Заявки" кабинета логистики
|
||||||
|
3. Логист изучает детали поставки (объем, вес, маршрут)
|
||||||
|
|
||||||
|
#### **ЭТАП 2: Подтверждение доставки**
|
||||||
|
|
||||||
|
4. Логист нажимает кнопку "Одобрить"
|
||||||
|
5. Статус поставки меняется на `LOGISTICS_CONFIRMED`
|
||||||
|
6. Уведомления отправляются всем участникам
|
||||||
|
|
||||||
|
#### **ЭТАП 3: Забор товара**
|
||||||
|
|
||||||
|
7. Логист приезжает к поставщику за товаром
|
||||||
|
8. Поставщик отгружает товар логисту
|
||||||
|
9. Поставщик отмечает "Отправлено"
|
||||||
|
10. Статус меняется на `SHIPPED`, затем `IN_TRANSIT`
|
||||||
|
|
||||||
|
#### **ЭТАП 4: Доставка**
|
||||||
|
|
||||||
|
11. Логистика доставляет товар на фулфилмент-центр
|
||||||
|
12. В кабинете логистики нажимают "Доставлено"
|
||||||
|
13. Фулфилмент принимает товар и отмечает "Принято"
|
||||||
|
|
||||||
|
### 12.3 Система тарификации
|
||||||
|
|
||||||
|
**ПАРАМЕТРЫ ТАРИФИКАЦИИ**:
|
||||||
|
|
||||||
|
- **Тариф до 1м³** - базовая стоимость для малых грузов
|
||||||
|
- **Тариф свыше 1м³** - стоимость для крупных грузов
|
||||||
|
- **Маршруты доставки** - от точки отправления до точки назначения
|
||||||
|
- **Описание услуг** - дополнительные условия доставки
|
||||||
|
|
||||||
|
**РАСЧЕТ СТОИМОСТИ**:
|
||||||
|
|
||||||
|
- Автоматический расчет стоимости доставки по объему груза
|
||||||
|
- Отображение примерной стоимости при создании заказа
|
||||||
|
- Учет специфики маршрута и условий доставки
|
||||||
|
|
||||||
|
### 12.4 Управление заявками
|
||||||
|
|
||||||
|
**РАЗДЕЛЫ КАБИНЕТА ЛОГИСТИКИ**:
|
||||||
|
|
||||||
|
- **НОВЫЕ ЗАЯВКИ** - поступившие заявки на доставку
|
||||||
|
- **В РАБОТЕ** - принятые к исполнению заявки
|
||||||
|
- **ВЫПОЛНЕННЫЕ** - завершенные доставки
|
||||||
|
- **ОТКЛОНЕННЫЕ** - заявки, которые не могут быть выполнены
|
||||||
|
|
||||||
|
**ИНФОРМАЦИЯ О ЗАЯВКЕ**:
|
||||||
|
|
||||||
|
- Детали груза (объем, вес, габариты)
|
||||||
|
- Маршрут доставки (откуда - куда)
|
||||||
|
- Срочность доставки
|
||||||
|
- Особые требования к транспортировке
|
||||||
|
- Контактная информация участников
|
||||||
|
|
||||||
|
### 12.5 Правила логистики
|
||||||
|
|
||||||
|
**ОБЯЗАТЕЛЬНО**:
|
||||||
|
|
||||||
|
- Своевременное подтверждение заявок
|
||||||
|
- Соблюдение сроков доставки
|
||||||
|
- Бережная транспортировка товаров
|
||||||
|
- Уведомление о статусе доставки
|
||||||
|
|
||||||
|
**ЗАПРЕЩЕНО**:
|
||||||
|
|
||||||
|
- Принятие заявок без подтверждения возможности выполнения
|
||||||
|
- Нарушение сроков доставки без уведомления
|
||||||
|
- Повреждение товаров при транспортировке
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. 🤝 СИСТЕМА ПАРТНЕРСТВА И КОНТРАГЕНТОВ
|
||||||
|
|
||||||
|
### 13.1 Основы системы партнерства
|
||||||
|
|
||||||
|
**ПРИНЦИП РАБОТЫ**:
|
||||||
|
- Все типы кабинетов могут создавать партнерские отношения
|
||||||
|
- Партнерство реализовано через таблицы `Counterparty` и `CounterpartyRequest`
|
||||||
|
- Двустороннее партнерство: каждая организация видит другую в разделе "Партнеры"
|
||||||
|
|
||||||
|
**ТИПЫ ОРГАНИЗАЦИЙ-ПАРТНЕРОВ**:
|
||||||
|
- `WHOLESALE` - Поставщики товаров и расходников
|
||||||
|
- `FULFILLMENT` - Фулфилмент-центры
|
||||||
|
- `LOGIST` - Логистические компании
|
||||||
|
- `SELLER` - Селлеры (торговые организации)
|
||||||
|
|
||||||
|
### 13.2 Способы создания партнерства
|
||||||
|
|
||||||
|
#### **СПОСОБ 1: Через заказ в маркете (автоматическое партнерство)**
|
||||||
|
|
||||||
|
**WORKFLOW**:
|
||||||
|
1. Поставщик создает товар → товар попадает в глобальный маркет
|
||||||
|
2. Селлер/Фулфилмент находит товар в маркете
|
||||||
|
3. Создает заказ (`SupplyOrder`) → статус `PENDING`
|
||||||
|
4. Поставщик получает уведомление в разделе "Заявки"
|
||||||
|
5. Поставщик одобряет заявку → статус `SUPPLIER_APPROVED`
|
||||||
|
6. **Автоматически создается двустороннее партнерство**:
|
||||||
|
- Запись в `Counterparty` для заказчика (`organizationId` → `counterpartyId`)
|
||||||
|
- Обратная запись в `Counterparty` для поставщика
|
||||||
|
7. Обе организации видят друг друга в разделе "Партнеры"
|
||||||
|
|
||||||
|
#### **СПОСОБ 2: Через раздел "Партнеры" (заявочная система)**
|
||||||
|
|
||||||
|
**WORKFLOW**:
|
||||||
|
1. Любая организация идет в раздел "Партнеры"
|
||||||
|
2. Использует поиск для нахождения нужной организации
|
||||||
|
3. Отправляет заявку на партнерство → создается `CounterpartyRequest`:
|
||||||
|
- `senderId` - отправитель заявки
|
||||||
|
- `receiverId` - получатель заявки
|
||||||
|
- `status: PENDING`
|
||||||
|
- `message` - опциональное сообщение
|
||||||
|
4. Получатель видит заявку в разделе "Партнеры" → "Входящие заявки"
|
||||||
|
5. Получатель принимает заявку → статус меняется на `ACCEPTED`
|
||||||
|
6. **Автоматически создается двустороннее партнерство** (аналогично способу 1)
|
||||||
|
|
||||||
|
**СТАТУСЫ ЗАЯВОК**:
|
||||||
|
- `PENDING` - Ожидает рассмотрения
|
||||||
|
- `ACCEPTED` - Принята (партнерство создано)
|
||||||
|
- `REJECTED` - Отклонена
|
||||||
|
- `CANCELLED` - Отменена отправителем
|
||||||
|
|
||||||
|
### 13.3 Использование партнерства в системе
|
||||||
|
|
||||||
|
#### **В форме создания поставки товаров через поставщиков**
|
||||||
|
|
||||||
|
**ПРАВИЛО ОТОБРАЖЕНИЯ ПОСТАВЩИКОВ**:
|
||||||
|
- Показываются только партнеры с типом `WHOLESALE`
|
||||||
|
- Источник: таблица `Counterparty` where `counterparty.type === "WHOLESALE"`
|
||||||
|
- Фильтрация по `organizationId` текущего пользователя
|
||||||
|
|
||||||
|
**ЛОГИКА РАБОТЫ**:
|
||||||
|
1. Пользователь выбирает поставщика из dropdown партнеров-поставщиков
|
||||||
|
2. Загружается каталог товаров поставщика из `Product` таблицы
|
||||||
|
3. Товары фильтруются по `organizationId = поставщик.id`
|
||||||
|
4. Пользователь может добавлять товары в корзину и создавать заказ
|
||||||
|
|
||||||
|
#### **В других разделах системы**
|
||||||
|
|
||||||
|
**ВЫБОР ФУЛФИЛМЕНТ-ЦЕНТРА**:
|
||||||
|
- Партнеры с типом `FULFILLMENT`
|
||||||
|
- Используется при создании поставок расходников
|
||||||
|
|
||||||
|
**ВЫБОР ЛОГИСТИКИ**:
|
||||||
|
- Партнеры с типом `LOGIST`
|
||||||
|
- Используется при планировании доставок
|
||||||
|
|
||||||
|
**МЕССЕНДЖЕР**:
|
||||||
|
- Общение доступно только между партнерами
|
||||||
|
- Список чатов формируется из таблицы `Counterparty`
|
||||||
|
|
||||||
|
### 13.4 Технические правила
|
||||||
|
|
||||||
|
**СОЗДАНИЕ ЗАПИСЕЙ В COUNTERPARTY**:
|
||||||
|
```sql
|
||||||
|
-- При создании партнерства создаются ДВЕ записи
|
||||||
|
INSERT INTO counterparties (organizationId, counterpartyId) VALUES (org1_id, org2_id);
|
||||||
|
INSERT INTO counterparties (organizationId, counterpartyId) VALUES (org2_id, org1_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**ПРОВЕРКА ПАРТНЕРСТВА**:
|
||||||
|
```typescript
|
||||||
|
const isPartner = await prisma.counterparty.findFirst({
|
||||||
|
where: {
|
||||||
|
organizationId: currentOrgId,
|
||||||
|
counterpartyId: targetOrgId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**ПОЛУЧЕНИЕ ПАРТНЕРОВ ПО ТИПУ**:
|
||||||
|
```typescript
|
||||||
|
const wholesalePartners = await prisma.counterparty.findMany({
|
||||||
|
where: {
|
||||||
|
organizationId: currentOrgId,
|
||||||
|
counterparty: {
|
||||||
|
type: "WHOLESALE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
counterparty: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13.5 Решение распространенных проблем
|
||||||
|
|
||||||
|
#### **ПРОБЛЕМА: GraphQL запрос не возвращает данные партнеров**
|
||||||
|
|
||||||
|
**Симптомы**:
|
||||||
|
- В консоли браузера: `All counterparties: 0`, `All counterparties data: []`
|
||||||
|
- GraphQL запрос отправляется успешно, но возвращает пустой массив
|
||||||
|
- В базе данных партнеры существуют
|
||||||
|
|
||||||
|
**Возможные причины и решения**:
|
||||||
|
|
||||||
|
1. **НЕПРАВИЛЬНОЕ ИМЯ ПОЛЯ В КОДЕ** (наиболее частая ошибка):
|
||||||
|
```typescript
|
||||||
|
// ❌ НЕПРАВИЛЬНО
|
||||||
|
const allCounterparties = counterpartiesData?.getMyCounterparties || [];
|
||||||
|
|
||||||
|
// ✅ ПРАВИЛЬНО
|
||||||
|
const allCounterparties = counterpartiesData?.myCounterparties || [];
|
||||||
|
```
|
||||||
|
**Объяснение**: В GraphQL схеме поле называется `myCounterparties`, а не `getMyCounterparties`
|
||||||
|
|
||||||
|
2. **НЕСООТВЕТСТВИЕ ID ПОЛЬЗОВАТЕЛЯ**:
|
||||||
|
- Проверить что пользователь авторизован под правильным аккаунтом
|
||||||
|
- Убедиться что `context.user.id` соответствует ожидаемому пользователю
|
||||||
|
|
||||||
|
3. **ПРОБЛЕМЫ С КЕШИРОВАНИЕМ APOLLO CLIENT**:
|
||||||
|
```typescript
|
||||||
|
const { data, loading, error } = useQuery(GET_MY_COUNTERPARTIES, {
|
||||||
|
fetchPolicy: 'network-only', // Обходим кеш
|
||||||
|
errorPolicy: 'all'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **ОТСУТСТВИЕ ЛОГИРОВАНИЯ В РЕЗОЛВЕРЕ**:
|
||||||
|
- Добавить console.log в GraphQL резолвер для отладки
|
||||||
|
- Проверить что резолвер вызывается
|
||||||
|
|
||||||
|
**Чек-лист для диагностики**:
|
||||||
|
- [ ] Проверить правильность имени поля в коде (`myCounterparties`)
|
||||||
|
- [ ] Убедиться что пользователь авторизован
|
||||||
|
- [ ] Проверить логи сервера на вызов резолвера
|
||||||
|
- [ ] Добавить отладочное логирование в браузере
|
||||||
|
- [ ] Проверить данные в базе через Prisma Studio
|
||||||
|
- [ ] Использовать `fetchPolicy: 'network-only'` для обхода кеша
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. 🌐 ИНТЕГРАЦИИ С СИСТЕМОЙ
|
||||||
|
|
||||||
|
### 14.1 Глобальная интеграция
|
||||||
|
|
||||||
|
- **МАРКЕТ**: Товары поставщиков отображаются в глобальном маркете
|
||||||
|
- **СИНХРОНИЗАЦИЯ**: Данные склада синхронизируются с модулем аналитики
|
||||||
|
- **УВЕДОМЛЕНИЯ**: Единая система через встроенный мессенджер
|
||||||
|
|
||||||
|
### 14.2 Интеграция с маркетплейсами
|
||||||
|
|
||||||
|
- **WILDBERRIES**: Обязательная проверка активности API ключа
|
||||||
|
- **СИНХРОНИЗАЦИЯ**: Регулярное обновление данных из внешних источников
|
||||||
|
- **ЛОКАЛЬНЫЕ КОПИИ**: Сохранение данных для офлайн работы
|
||||||
|
|
||||||
|
### 14.3 Интеграция с модулем "Услуги"
|
||||||
|
|
||||||
|
**РАСХОДНИКИ ФУЛФИЛМЕНТА В УСЛУГАХ**:
|
||||||
|
|
||||||
|
- Расходники фулфилмента - собственность фулфилмента (куплены у поставщиков)
|
||||||
|
- Фулфилмент создает заявки-поставки для покупки расходников у поставщиков
|
||||||
|
- Селлеры могут использовать расходники фулфилмента в разделе "Услуги / Расходники"
|
||||||
|
- Для создания продукта из товара
|
||||||
|
- Расходники списываются с остатков фулфилмента
|
||||||
|
- Стоимость включается в стоимость услуги
|
||||||
|
|
||||||
|
**WORKFLOW ИСПОЛЬЗОВАНИЯ**:
|
||||||
|
|
||||||
|
1. Селлер выбирает услугу "Создание продукта"
|
||||||
|
2. Указывает базовый товар
|
||||||
|
3. Выбирает необходимые расходники фулфилмента
|
||||||
|
4. Фулфилмент обрабатывает заказ и создает продукт
|
||||||
|
5. Расходники списываются, создается готовый продукт
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. 📊 СТАТИСТИКА И АНАЛИТИКА
|
||||||
|
|
||||||
|
### 15.1 Структура статистики по кабинетам
|
||||||
|
|
||||||
|
#### **В КАБИНЕТЕ ПОСТАВЩИКА**:
|
||||||
|
|
||||||
|
- **ТОВАРЫ**: Общая статистика товаров поставщика
|
||||||
|
- **РАСХОДНИКИ**: Материалы и вспомогательные товары (классифицируются при заказе)
|
||||||
|
|
||||||
|
#### **В КАБИНЕТЕ ФУЛФИЛМЕНТА**:
|
||||||
|
|
||||||
|
- **ТОВАРЫ**: Базовые товары от поставщиков (принятые на склад)
|
||||||
|
- **ПРОДУКТЫ**: Отдельный блок готовой продукции
|
||||||
|
- **БРАК**: Статистика потерь и списаний
|
||||||
|
- **РАСХОДНИКИ ФУЛФИЛМЕНТА**: Операционные материалы фулфилмента
|
||||||
|
- **РАСХОДНИКИ СЕЛЛЕРОВ**: Материалы для товаров селлеров
|
||||||
|
|
||||||
|
### 15.2 Ключевые метрики
|
||||||
|
|
||||||
|
**ОБЩИЕ ПОКАЗАТЕЛИ**:
|
||||||
|
|
||||||
|
- Общие остатки, заказано, в пути, остаток, продано
|
||||||
|
- Подсветка предметов с остатками ниже критического уровня
|
||||||
|
|
||||||
|
**АКТУАЛИЗАЦИЯ ДАННЫХ**:
|
||||||
|
|
||||||
|
- При изменении количества в карточке данные актуализируются во всей системе
|
||||||
|
- Статистика обновляется в реальном времени
|
||||||
|
- Отслеживание изменений для аналитики
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. 📱 ПРАВИЛА UI/UX И ИНТЕРФЕЙСА
|
||||||
|
|
||||||
|
### 16.1 Отзывчивость интерфейса
|
||||||
|
|
||||||
|
- **ОБЯЗАТЕЛЬНО**: Интерфейс должен работать на всех устройствах
|
||||||
|
- **ПРАВИЛО**: Адаптивная сетка для карточек товаров
|
||||||
|
- **ФУНКЦИЯ**: Оптимизация для мобильных устройств
|
||||||
|
- **ТРЕБОВАНИЕ**: Корректное отображение на экранах от 320px до 4K
|
||||||
|
|
||||||
|
### 16.2 Обратная связь пользователю
|
||||||
|
|
||||||
|
- **ОБЯЗАТЕЛЬНО**: Уведомления об успешных/неуспешных операциях
|
||||||
|
- **ПРАВИЛО**: Индикаторы загрузки для длительных операций
|
||||||
|
- **ФУНКЦИЯ**: Подтверждение критических действий (удаление, деактивация)
|
||||||
|
- **UX**: Понятные сообщения об ошибках с предложением решения
|
||||||
|
|
||||||
|
### 16.3 Правила обработки ошибок
|
||||||
|
|
||||||
|
- **ОБЯЗАТЕЛЬНО**: Логирование всех ошибок с детальной информацией
|
||||||
|
- **ПРАВИЛО**: Понятные сообщения об ошибках для пользователя
|
||||||
|
- **ФУНКЦИЯ**: Автоматическое восстановление после сбоев
|
||||||
|
- **МОНИТОРИНГ**: Отслеживание критических ошибок в реальном времени
|
||||||
|
|
||||||
|
### 16.4 Производительность
|
||||||
|
|
||||||
|
- **ПРАВИЛО**: Пагинация для больших списков товаров
|
||||||
|
- **ФУНКЦИЯ**: Ленивая загрузка изображений
|
||||||
|
- **ОПТИМИЗАЦИЯ**: Кэширование часто запрашиваемых данных
|
||||||
|
- **ПРОИЗВОДИТЕЛЬНОСТЬ**: Время загрузки страницы не более 3 секунд
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. ⚠️ КРИТИЧЕСКИЕ ЗАПРЕТЫ
|
||||||
|
|
||||||
|
### 17.1 НИКОГДА НЕ ДЕЛАТЬ:
|
||||||
|
|
||||||
|
1. ❌ Удалять предметы с существующими заказами
|
||||||
|
2. ❌ Изменять статусы заказов без уведомлений
|
||||||
|
3. ❌ Обходить проверки остатков предметов
|
||||||
|
4. ❌ Давать доступ к чужим данным
|
||||||
|
5. ❌ Игнорировать ошибки валидации
|
||||||
|
6. ❌ Сохранять пароли в открытом виде
|
||||||
|
7. ❌ Пропускать логирование критических операций
|
||||||
|
8. ❌ Блокировать интерфейс без индикации загрузки
|
||||||
|
9. ❌ Создавать брак или продукт без связи с родительским товаром
|
||||||
|
10. ❌ Создавать отдельные типы расходников (только общий тип "РАСХОДНИКИ")
|
||||||
|
11. ❌ Разрешать заказ брака
|
||||||
|
12. ❌ Нарушать иерархию типов предметов
|
||||||
|
13. ❌ Пропускать промежуточные статусы в workflow
|
||||||
|
14. ❌ Нарушать обязательную последовательность модулей в статистике фулфилмента
|
||||||
|
15. ❌ **Использовать неправильные имена полей GraphQL** (`getMyCounterparties` вместо `myCounterparties`)
|
||||||
|
16. ❌ **Использовать GET_SUPPLY_SUPPLIERS для отображения поставщиков в формах** (только партнеры WHOLESALE)
|
||||||
|
17. ❌ **Создавать интерфейсы TypeScript не соответствующие schema.prisma** (`sku`/`stock` вместо `article`/`quantity`)
|
||||||
|
18. ❌ **Использовать общие запросы вместо специализированных** (`GET_ALL_PRODUCTS` вместо `GET_ORGANIZATION_PRODUCTS` для конкретного поставщика)
|
||||||
|
19. ❌ **Показывать расходники в формах создания поставок товаров** (строгая типизация `PRODUCT`/`CONSUMABLE`)
|
||||||
|
20. ❌ **Фильтровать предметы по типу на фронтенде** (фильтрация должна быть в GraphQL резолвере)
|
||||||
|
|
||||||
|
### 17.2 ОБЯЗАТЕЛЬНЫЕ ПРАВИЛА:
|
||||||
|
|
||||||
|
1. ✅ Проверка остатков перед добавлением в корзину
|
||||||
|
2. ✅ Валидация всех числовых значений (цена > 0, вес > 0)
|
||||||
|
3. ✅ Автоматическая генерация уникальных артикулов СФ
|
||||||
|
4. ✅ Логирование всех изменений статусов
|
||||||
|
5. ✅ Уведомления всех участников при изменении статусов
|
||||||
|
6. ✅ Обязательная типизация всех предметов
|
||||||
|
7. ✅ Связь производных типов с родительскими предметами
|
||||||
|
8. ✅ Проверка доступности товаров перед заказом
|
||||||
|
9. ✅ Соблюдение жизненного цикла статусов поставок
|
||||||
|
10. ✅ Фиксация план/факт в процессе создания продукта
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. 🛠️ GRAPHQL И TYPESCRIPT ПРАВИЛА
|
||||||
|
|
||||||
|
### 18.1 Правила именования полей
|
||||||
|
|
||||||
|
**ВАЖНО**: Имена полей в GraphQL запросах должны точно соответствовать схеме!
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ ПРАВИЛЬНО - соответствует схеме
|
||||||
|
export const GET_MY_COUNTERPARTIES = gql`
|
||||||
|
query GetMyCounterparties {
|
||||||
|
myCounterparties { // <- имя поля в схеме
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Использование в компоненте
|
||||||
|
const allCounterparties = counterpartiesData?.myCounterparties || [];
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ❌ НЕПРАВИЛЬНО - не соответствует схеме
|
||||||
|
const allCounterparties = counterpartiesData?.getMyCounterparties || []; // Ошибка!
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.2 Правила отладки GraphQL
|
||||||
|
|
||||||
|
**При проблемах с GraphQL запросами следовать чек-листу**:
|
||||||
|
|
||||||
|
1. **Проверить соответствие имен полей схеме**
|
||||||
|
2. **Добавить fetchPolicy: 'network-only' для обхода кеша**
|
||||||
|
3. **Логировать данные в браузере и сервере**
|
||||||
|
4. **Проверить авторизацию пользователя**
|
||||||
|
5. **Убедиться что резолвер вызывается**
|
||||||
|
|
||||||
|
### 18.3 Обязательные поля для отладки
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { data, loading, error } = useQuery(QUERY_NAME, {
|
||||||
|
fetchPolicy: 'network-only', // Обходим кеш при отладке
|
||||||
|
errorPolicy: 'all' // Показываем все ошибки
|
||||||
|
});
|
||||||
|
|
||||||
|
// Логирование для отладки
|
||||||
|
console.log("Data:", data);
|
||||||
|
console.log("Loading:", loading);
|
||||||
|
console.log("Error:", error);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.4 TypeScript Rules
|
||||||
|
|
||||||
|
#### **Интерфейсы данных**
|
||||||
|
|
||||||
|
**Поля интерфейсов должны соответствовать GraphQL схеме**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ ПРАВИЛЬНО - соответствует schema.prisma
|
||||||
|
interface GoodsProduct {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
article: string; // <- соответствует полю в schema
|
||||||
|
quantity?: number; // <- соответствует полю в schema
|
||||||
|
organization: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ❌ НЕПРАВИЛЬНО - не соответствует schema
|
||||||
|
interface GoodsProduct {
|
||||||
|
sku: string; // <- в schema поле называется 'article'
|
||||||
|
stock?: number; // <- в schema поле называется 'quantity'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.5 Система партнерства в GraphQL
|
||||||
|
|
||||||
|
**ПРАВИЛА ИСПОЛЬЗОВАНИЯ ПАРТНЕРСТВА**:
|
||||||
|
|
||||||
|
- Поставщики в формах берутся только из партнеров с типом `WHOLESALE`
|
||||||
|
- Используется запрос `GET_MY_COUNTERPARTIES` с фильтрацией по типу
|
||||||
|
- НЕ используется `GET_SUPPLY_SUPPLIERS` для отображения поставщиков в формах
|
||||||
|
- Партнерство создается двумя способами: через заказ в маркете или через раздел "Партнеры"
|
||||||
|
- Двусторонние записи в таблице `Counterparty` при создании партнерства
|
||||||
|
|
||||||
|
### 18.6 Архитектурные принципы GraphQL
|
||||||
|
|
||||||
|
- **Создавать специализированные резолверы** для каждого контекста использования
|
||||||
|
- **Использовать параметризованные запросы** (`organizationId`, `type`, `search`) вместо фильтрации на фронтенде
|
||||||
|
- **Добавлять подробное логирование** в резолверы для отладки (входные параметры, результаты фильтрации)
|
||||||
|
- **Типы запросов должны отражать бизнес-логику**: `organizationProducts` для товаров конкретной организации
|
||||||
|
- **Значения по умолчанию в резолверах** для критических параметров (`type: args.type || "PRODUCT"`)
|
||||||
|
- **Валидация обязательных параметров** на уровне схемы (`organizationId: ID!`)
|
||||||
|
- **Кеширование обходить при проблемах** через `fetchPolicy: 'network-only'`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 19. 🔧 АРХИТЕКТУРНЫЕ ПРИНЦИПЫ
|
||||||
|
|
||||||
|
### 19.1 Стандарты разработки
|
||||||
|
|
||||||
|
- **ОБЯЗАТЕЛЬНО**: Покрытие тестами критической функциональности
|
||||||
|
- **ПРАВИЛО**: Следование принципам SOLID
|
||||||
|
- **ФУНКЦИЯ**: Автоматическое тестирование при развертывании
|
||||||
|
- **КАЧЕСТВО**: Минимальное покрытие тестами 80%
|
||||||
|
|
||||||
|
### 19.2 Документация
|
||||||
|
|
||||||
|
- **ОБЯЗАТЕЛЬНО**: Документирование всех API методов
|
||||||
|
- **ПРАВИЛО**: Комментарии к сложной бизнес-логике
|
||||||
|
- **ФУНКЦИЯ**: Автоматическая генерация документации
|
||||||
|
- **СТАНДАРТ**: Актуальная техническая документация
|
||||||
|
|
||||||
|
### 19.3 Масштабируемость
|
||||||
|
|
||||||
|
- **АРХИТЕКТУРА**: Модульная структура для легкого расширения
|
||||||
|
- **ПРАВИЛО**: Использование индексов для быстрого поиска
|
||||||
|
- **ФУНКЦИЯ**: Горизонтальное масштабирование при росте нагрузки
|
||||||
|
- **ПЛАНИРОВАНИЕ**: Готовность к увеличению нагрузки в 10 раз
|
||||||
|
|
||||||
|
### 19.4 Безопасность данных
|
||||||
|
|
||||||
|
- **ОБЯЗАТЕЛЬНО**: Шифрование чувствительных данных
|
||||||
|
- **ПРАВИЛО**: Аудит всех действий пользователей
|
||||||
|
- **ФУНКЦИЯ**: Контроль доступа на уровне API
|
||||||
|
- **БЕЗОПАСНОСТЬ**: Двухфакторная аутентификация для критических операций
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 20. 🎯 ПРАВИЛА КАЧЕСТВА КОДА
|
||||||
|
|
||||||
|
### 20.1 Проверки и валидация
|
||||||
|
|
||||||
|
**ОБЯЗАТЕЛЬНЫЕ ПРОВЕРКИ**:
|
||||||
|
|
||||||
|
- ✅ Типизация предметов: каждый предмет имеет один из типов: ТОВАР (`PRODUCT`), РАСХОДНИКИ (`CONSUMABLE`), БРАК и ПРОДУКТ (планируются)
|
||||||
|
- ✅ БРАК и ПРОДУКТ имеют обязательную связь с родительским товаром (parentId)
|
||||||
|
- ✅ Расходники создаются как универсальный тип, классифицируются при заказе
|
||||||
|
- ✅ **В формах создания поставок товаров показываются ТОЛЬКО предметы типа ТОВАР** (`PRODUCT`)
|
||||||
|
- ✅ **В формах создания поставок расходников показываются ТОЛЬКО предметы типа РАСХОДНИКИ** (`CONSUMABLE`)
|
||||||
|
- ✅ **Фильтрация по типу предмета происходит на уровне GraphQL резолвера**, не на фронтенде
|
||||||
|
|
||||||
|
### 20.2 Workflow статусов
|
||||||
|
|
||||||
|
- ✅ Соблюдена последовательность: PENDING → SUPPLIER_APPROVED → CONFIRMED → LOGISTICS_CONFIRMED → SHIPPED → IN_TRANSIT → DELIVERED
|
||||||
|
- ✅ Нет пропуска промежуточных статусов
|
||||||
|
- ✅ Каждое изменение статуса сопровождается уведомлением
|
||||||
|
|
||||||
|
### 20.3 Правила доступа
|
||||||
|
|
||||||
|
- ✅ Поставщик НЕ может добавлять собственные товары в корзину
|
||||||
|
- ✅ Заказ брака ЗАПРЕЩЕН
|
||||||
|
- ✅ Только активные предметы отображаются в маркете
|
||||||
|
- ✅ Проверка остатков перед добавлением в корзину
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 21. 🔍 ДИАГНОСТИКА И РЕШЕНИЕ ПРОБЛЕМ
|
||||||
|
|
||||||
|
### 21.1 Правило уточнений
|
||||||
|
|
||||||
|
**КРИТИЧЕСКИ ВАЖНО**: Если я не уверен в выполнении задачи или вижу противоречия в правилах - ОБЯЗАТЕЛЬНО уточнить у пользователя!
|
||||||
|
|
||||||
|
### 21.2 КОГДА УТОЧНЯТЬ:
|
||||||
|
|
||||||
|
- [ ] Недостаточно информации для правильного выполнения
|
||||||
|
- [ ] Вижу противоречия между правилами
|
||||||
|
- [ ] Задача может нарушить критические правила
|
||||||
|
- [ ] Неясно как применить правило в конкретной ситуации
|
||||||
|
- [ ] Есть сомнения в интерпретации требований
|
||||||
|
|
||||||
|
### 21.3 КАК УТОЧНЯТЬ:
|
||||||
|
|
||||||
|
1. Четко сформулировать что именно неясно
|
||||||
|
2. Указать какие правила/требования конфликтуют
|
||||||
|
3. Предложить варианты решения если возможно
|
||||||
|
4. Запросить конкретные уточнения
|
||||||
|
|
||||||
|
**❌ НИКОГДА не делать предположений если есть сомнения!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 22. 📋 КАТЕГОРИИ ТОВАРОВ И РАСХОДНИКОВ
|
||||||
|
|
||||||
|
### 22.1 Полный список 28 универсальных категорий товаров
|
||||||
|
|
||||||
|
1. Одежда и обувь
|
||||||
|
2. Косметика и парфюмерия
|
||||||
|
3. Дом и сад
|
||||||
|
4. Детские товары
|
||||||
|
5. Спорт и отдых
|
||||||
|
6. Электроника
|
||||||
|
7. Книги
|
||||||
|
8. Здоровье
|
||||||
|
9. Автотовары
|
||||||
|
10. Строительство и ремонт
|
||||||
|
11. Продукты питания
|
||||||
|
12. Зоотовары
|
||||||
|
13. Дача, сад и огород
|
||||||
|
14. Канцелярские товары
|
||||||
|
15. Хобби и творчество
|
||||||
|
16. Украшения и аксессуары
|
||||||
|
17. Сумки и чемоданы
|
||||||
|
18. Техника для дома
|
||||||
|
19. Музыкальные инструменты
|
||||||
|
20. Игры и игрушки
|
||||||
|
21. Мебель
|
||||||
|
22. Товары для красоты
|
||||||
|
23. Бытовая химия
|
||||||
|
24. Товары для путешествий
|
||||||
|
25. Медицинские товары
|
||||||
|
26. Религиозные товары
|
||||||
|
27. Антиквариат и коллекционирование
|
||||||
|
28. Прочие товары
|
||||||
|
|
||||||
|
### 22.2 12 специализированных категорий расходников
|
||||||
|
|
||||||
|
#### 🎁 **1. УПАКОВКА И ЗАЩИТА**
|
||||||
|
- Коробки (различных размеров)
|
||||||
|
- Пакеты (полиэтиленовые, бумажные, фирменные)
|
||||||
|
- Пузырчатая пленка, воздушные подушки
|
||||||
|
- Стрейч-пленка, гофрокартон
|
||||||
|
- Паллетная пленка, защитные уголки
|
||||||
|
|
||||||
|
#### 🏷️ **2. МАРКИРОВКА И ИДЕНТИФИКАЦИЯ**
|
||||||
|
- Этикетки (адресные, штрих-код, QR-код)
|
||||||
|
- Бирки (ценники, размерники)
|
||||||
|
- Стикеры и наклейки
|
||||||
|
- Маркеры и ручки
|
||||||
|
- Штампы и печати, термоэтикетки
|
||||||
|
|
||||||
|
#### 🔧 **3. КРЕПЕЖ И СОЕДИНЕНИЕ**
|
||||||
|
- Скотч (прозрачный, цветной, армированный)
|
||||||
|
- Клей и клеевые составы
|
||||||
|
- Стяжки пластиковые
|
||||||
|
- Степлер и скобы
|
||||||
|
- Веревки и шнуры, стрейч-лента
|
||||||
|
|
||||||
|
#### 📄 **4. ДОКУМЕНТООБОРОТ И ВКЛАДЫШИ**
|
||||||
|
- Накладные и сопроводительные документы
|
||||||
|
- Инструкции по эксплуатации
|
||||||
|
- Гарантийные талоны
|
||||||
|
- Рекламные буклеты, визитки и флаеры
|
||||||
|
- Благодарственные письма, купоны и промокоды
|
||||||
|
|
||||||
|
#### 🧼 **5. ГИГИЕНА И БЕЗОПАСНОСТЬ**
|
||||||
|
- Перчатки (латексные, нитриловые)
|
||||||
|
- Маски и респираторы
|
||||||
|
- Антисептики и дезинфекторы
|
||||||
|
- Салфетки и тряпки
|
||||||
|
- Фартуки и халаты, бахилы
|
||||||
|
|
||||||
|
#### 🛠️ **6. ИНСТРУМЕНТЫ И ПРИСПОСОБЛЕНИЯ**
|
||||||
|
- Ножи и резаки, ножницы
|
||||||
|
- Линейки и рулетки
|
||||||
|
- Упаковочные машины (ленточные)
|
||||||
|
- Дозаторы скотча
|
||||||
|
- Пистолеты для термоклея
|
||||||
|
- Весы и мерная тара
|
||||||
|
|
||||||
|
#### 🎨 **7. БРЕНДИНГ И ДИЗАЙН**
|
||||||
|
- Фирменные пакеты с логотипом
|
||||||
|
- Брендированные коробки
|
||||||
|
- Цветная упаковочная бумага
|
||||||
|
- Ленты и банты
|
||||||
|
- Наклейки с логотипом компании
|
||||||
|
- Подарочная упаковка
|
||||||
|
|
||||||
|
#### ⚡ **8. СПЕЦИАЛИЗИРОВАННЫЕ МАТЕРИАЛЫ**
|
||||||
|
- Антистатические пакеты
|
||||||
|
- Влагопоглотители
|
||||||
|
- Температурные индикаторы
|
||||||
|
- Хрупкие наклейки
|
||||||
|
- Пломбы и пломбировочные материалы
|
||||||
|
- Защита от краж (магнитные датчики)
|
||||||
|
|
||||||
|
#### 🏪 **9. ТОРГОВОЕ ОБОРУДОВАНИЕ**
|
||||||
|
- Манекены и вешалки
|
||||||
|
- Ценникодержатели
|
||||||
|
- Подставки и стойки
|
||||||
|
- Корзины и тележки
|
||||||
|
- Зеркала примерочные
|
||||||
|
- Освещение витрин
|
||||||
|
|
||||||
|
#### 🚚 **10. ЛОГИСТИКА И СКЛАДИРОВАНИЕ**
|
||||||
|
- Паллеты и поддоны
|
||||||
|
- Контейнеры и ящики
|
||||||
|
- Стеллажные системы
|
||||||
|
- Погрузочные ремни
|
||||||
|
- Защитные чехлы
|
||||||
|
- Адресные ярлыки для груза
|
||||||
|
|
||||||
|
#### 💻 **11. ТЕХНИЧЕСКИЕ РАСХОДНИКИ**
|
||||||
|
- Картриджи для принтеров
|
||||||
|
- Термоголовки, красящие ленты
|
||||||
|
- Батарейки для сканеров
|
||||||
|
- Чистящие средства для техники
|
||||||
|
- Запчасти для упаковочного оборудования
|
||||||
|
|
||||||
|
#### 🎪 **12. СЕЗОННЫЕ И ПРАЗДНИЧНЫЕ**
|
||||||
|
- Новогодняя упаковка
|
||||||
|
- Подарочные мешки
|
||||||
|
- Праздничные ленты
|
||||||
|
- Тематические наклейки
|
||||||
|
- Открытки и поздравления
|
||||||
|
- Сезонная упаковочная бумага
|
||||||
|
|
||||||
|
**ПРИМЕЧАНИЕ**: Данные категории являются рекомендательными и могут быть адаптированы под специфику конкретного поставщика расходников.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 23. 🎖️ ПРИОРИТЕТЫ РАЗРАБОТКИ
|
||||||
|
|
||||||
|
### 23.1 ВЫСОКИЙ ПРИОРИТЕТ:
|
||||||
|
|
||||||
|
1. 🔴 Безопасность и контроль доступа
|
||||||
|
2. 🔴 Целостность данных и валидация
|
||||||
|
3. 🔴 Корректность статусов поставок
|
||||||
|
4. 🔴 Уведомления участников процесса
|
||||||
|
5. 🔴 Правильная типизация предметов
|
||||||
|
6. 🔴 Связи между товарами и производными типами
|
||||||
|
|
||||||
|
### 23.2 СРЕДНИЙ ПРИОРИТЕТ:
|
||||||
|
|
||||||
|
1. 🟡 Производительность и оптимизация
|
||||||
|
2. 🟡 Пользовательский опыт
|
||||||
|
3. 🟡 Аналитика и отчетность
|
||||||
|
4. 🟡 Интеграции с внешними системами
|
||||||
|
5. 🟡 Workflow для брака и продуктов
|
||||||
|
6. 🟡 Разделение расходников по типам
|
||||||
|
|
||||||
|
### 23.3 НИЗКИЙ ПРИОРИТЕТ:
|
||||||
|
|
||||||
|
1. 🟢 Дополнительные фильтры
|
||||||
|
2. 🟢 Косметические улучшения
|
||||||
|
3. 🟢 Экспериментальные функции
|
||||||
|
4. 🟢 Расширенная кастомизация
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Эта база знаний создана на основе анализа rules.md, rules1.md и rules2.md и является единым источником понимания структуры и логики проекта._
|
||||||
|
|
||||||
|
_Версия: 4.0_
|
||||||
|
_Дата создания: 2024_
|
||||||
|
_Статус: ПОЛНАЯ УНИФИЦИРОВАННАЯ БАЗА ЗНАНИЙ_
|
3644
server.log
Normal file
3644
server.log
Normal file
@ -0,0 +1,3644 @@
|
|||||||
|
|
||||||
|
> sferav@0.1.0 dev
|
||||||
|
> next dev --turbopack
|
||||||
|
|
||||||
|
▲ Next.js 15.4.1 (Turbopack)
|
||||||
|
- Local: http://localhost:3000
|
||||||
|
- Network: http://192.168.0.104:3000
|
||||||
|
- Environments: .env
|
||||||
|
|
||||||
|
✓ Starting...
|
||||||
|
✓ Ready in 823ms
|
||||||
|
○ Compiling /api/graphql ...
|
||||||
|
✓ Compiled /api/graphql in 1234ms
|
||||||
|
🚀 Проверка инициализации базы данных...
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 582ms
|
||||||
|
✨ Инициализация базы данных завершена
|
||||||
|
POST /api/graphql 200 in 3932ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 4435ms
|
||||||
|
POST /api/graphql 200 in 4521ms
|
||||||
|
POST /api/graphql 200 in 1014ms
|
||||||
|
POST /api/graphql 200 in 4627ms
|
||||||
|
POST /api/graphql 200 in 1194ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 776ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 466ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 288ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 716ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 1026ms
|
||||||
|
POST /api/graphql 200 in 605ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
POST /api/graphql 200 in 369ms
|
||||||
|
POST /api/graphql 200 in 782ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 302ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 381ms
|
||||||
|
POST /api/graphql 200 in 389ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 275ms
|
||||||
|
POST /api/graphql 200 in 326ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 738ms
|
||||||
|
POST /api/graphql 200 in 1119ms
|
||||||
|
POST /api/graphql 200 in 1131ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 1185ms
|
||||||
|
POST /api/graphql 200 in 1052ms
|
||||||
|
POST /api/graphql 200 in 1198ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 482ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 286ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 710ms
|
||||||
|
POST /api/graphql 200 in 622ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 601ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
POST /api/graphql 200 in 458ms
|
||||||
|
POST /api/graphql 200 in 1121ms
|
||||||
|
○ Compiling / ...
|
||||||
|
✓ Compiled / in 1642ms
|
||||||
|
GET / 200 in 1852ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 397ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 343ms
|
||||||
|
POST /api/graphql 200 in 341ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 265ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 279ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 1281ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 683ms
|
||||||
|
POST /api/graphql 200 in 589ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 602ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 1193ms
|
||||||
|
POST /api/graphql 200 in 602ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 379ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 267ms
|
||||||
|
○ Compiling /supplies/create-suppliers ...
|
||||||
|
✓ Compiled /supplies/create-suppliers in 580ms
|
||||||
|
GET /supplies/create-suppliers 200 in 797ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
✓ Compiled /favicon.ico in 130ms
|
||||||
|
GET /favicon.ico?favicon.45db1c09.ico 200 in 393ms
|
||||||
|
POST /api/graphql 200 in 413ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
POST /api/graphql 200 in 325ms
|
||||||
|
POST /api/graphql 200 in 499ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 827ms
|
||||||
|
POST /api/graphql 200 in 833ms
|
||||||
|
POST /api/graphql 200 in 629ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 615ms
|
||||||
|
POST /api/graphql 200 in 619ms
|
||||||
|
GET /supplies/create-suppliers 200 in 118ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GET /favicon.ico?favicon.45db1c09.ico 200 in 245ms
|
||||||
|
POST /api/graphql 200 in 371ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
POST /api/graphql 200 in 321ms
|
||||||
|
POST /api/graphql 200 in 335ms
|
||||||
|
POST /api/graphql 200 in 724ms
|
||||||
|
POST /api/graphql 200 in 950ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
🏢 ORGANIZATION_PRODUCTS RESOLVER - ВЫЗВАН: {
|
||||||
|
userId: 'cmdxbhacs0004y52vqs3cu86k',
|
||||||
|
organizationId: 'cmdxbievj000ay52vliq4h23r',
|
||||||
|
search: '',
|
||||||
|
category: '',
|
||||||
|
type: 'ТОВАР',
|
||||||
|
timestamp: '2025-08-04T17:38:34.516Z'
|
||||||
|
}
|
||||||
|
POST /api/graphql 200 in 58ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 387ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 285ms
|
||||||
|
POST /api/graphql 200 in 358ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 285ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 282ms
|
||||||
|
POST /api/graphql 200 in 705ms
|
||||||
|
POST /api/graphql 200 in 818ms
|
||||||
|
POST /api/graphql 200 in 819ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 638ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 602ms
|
||||||
|
POST /api/graphql 200 in 601ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 408ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 385ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 794ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 606ms
|
||||||
|
POST /api/graphql 200 in 692ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmhhY3MwMDA0eTUydnFzM2N1ODZrIiwicGhvbmUiOiI3ODg4ODg4ODg4OCIsImlhdCI6MTc1NDMyNDQ0NywiZXhwIjoxNzU2OTE2NDQ3fQ.G3fwU6DYC7Ue649ibdA7G3D9Xy3DBHQukcXNVRaFQ6E
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbhacs0004y52vqs3cu86k', phone: '78888888888' }
|
||||||
|
POST /api/graphql 200 in 291ms
|
||||||
|
POST /api/graphql 200 in 822ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 273ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 273ms
|
||||||
|
POST /api/graphql 200 in 359ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 295ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
POST /api/graphql 200 in 699ms
|
||||||
|
POST /api/graphql 200 in 695ms
|
||||||
|
POST /api/graphql 200 in 810ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 293ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Yml2NjQwMDBjeTUydnhoNTdkMnJ5IiwicGhvbmUiOiI3NjY2NjY2NjY2NiIsImlhdCI6MTc1NDMyNDUyMCwiZXhwIjoxNzU2OTE2NTIwfQ.JFgG4M95MTlnkRxJET8ZkcGEwVtaOaH4ofbosZbk9yQ
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbiv64000cy52vxh57d2ry', phone: '76666666666' }
|
||||||
|
POST /api/graphql 200 in 1482ms
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4YmkxcHYwMDA5eTUydnZrenkzOWdyIiwicGhvbmUiOiI3Nzc3Nzc3Nzc3NyIsImlhdCI6MTc1NDMyNDQ4MiwiZXhwIjoxNzU2OTE2NDgyfQ.uudvqWO9a8S0RAiFO1J-rTHHUO13x3QZ5m1d7VQ8YOU
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbi1pv0009y52vvkzy39gr', phone: '77777777777' }
|
||||||
|
GraphQL Context - Auth header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWR4Ymc2cmswMDAxeTUydjNnbm9ncGcyIiwicGhvbmUiOiI3OTk5OTk5OTk5OSIsImlhdCI6MTc1NDMyNDM5NSwiZXhwIjoxNzU2OTE2Mzk1fQ.kQHVo5zIbifiqwkxnXKGiKh0fZJYEvC8LSG1k-C929s
|
||||||
|
GraphQL Context - Token: eyJhbGciOiJIUzI1NiIs...
|
||||||
|
GraphQL Context - Decoded user: { id: 'cmdxbg6rk0001y52v3gnogpg2', phone: '79999999999' }
|
||||||
|
POST /api/graphql 200 in 932ms
|
||||||
|
POST /api/graphql 200 in 932ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
○ Compiling /_error ...
|
||||||
|
✓ Compiled /_error in 847ms
|
||||||
|
POST /api/graphql 500 in 1332ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 92ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 70ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 132ms
|
||||||
|
POST /api/graphql 500 in 129ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 118ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 85ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 114ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 138ms
|
||||||
|
POST /api/graphql 500 in 138ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 77ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 143ms
|
||||||
|
POST /api/graphql 500 in 149ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 51ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 95ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 61ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 114ms
|
||||||
|
POST /api/graphql 500 in 119ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 92ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 80ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 105ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 121ms
|
||||||
|
POST /api/graphql 500 in 124ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 93ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 85ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 94ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 141ms
|
||||||
|
POST /api/graphql 500 in 145ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 83ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 91ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 97ms
|
||||||
|
POST /api/graphql 500 in 106ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 113ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 95ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 188ms
|
||||||
|
POST /api/graphql 500 in 188ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 90ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 92ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 77ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 283ms
|
||||||
|
POST /api/graphql 500 in 295ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 222ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 84ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 152ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 101ms
|
||||||
|
POST /api/graphql 500 in 109ms
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/supplies/create-suppliers/page/app-build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/favicon.ico/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/app/api/graphql/[__metadata_id__]/route/app-paths-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 205ms
|
||||||
|
POST /api/graphql 500 in 60ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ [Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
[Error: ENOENT: no such file or directory, open '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'] {
|
||||||
|
errno: -2,
|
||||||
|
code: 'ENOENT',
|
||||||
|
syscall: 'open',
|
||||||
|
path: '/Users/veronikasmirnova/Desktop/Projects/sfera/.next/server/pages/_app/build-manifest.json'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 336ms
|
||||||
|
POST /api/graphql 500 in 42ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 108ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 115ms
|
||||||
|
POST /api/graphql 500 in 118ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 75ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 65ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 103ms
|
||||||
|
POST /api/graphql 500 in 101ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 86ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 56ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 93ms
|
||||||
|
POST /api/graphql 500 in 95ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 53ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 140ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 98ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 84ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 108ms
|
||||||
|
POST /api/graphql 500 in 110ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 119ms
|
||||||
|
POST /api/graphql 500 in 116ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 94ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 83ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 94ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 113ms
|
||||||
|
POST /api/graphql 500 in 116ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 70ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 160ms
|
||||||
|
POST /api/graphql 500 in 171ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 110ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 119ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 66ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 122ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 115ms
|
||||||
|
POST /api/graphql 500 in 124ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 115ms
|
||||||
|
POST /api/graphql 500 in 117ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 90ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 82ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 79ms
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
⨯ GraphQLError: Syntax Error: Unexpected character: U+0422.
|
||||||
|
at parseDocument (../src/index.ts:96:25)
|
||||||
|
at Module.gql (../src/index.ts:134:9)
|
||||||
|
at [project]/src/graphql/typedefs.ts [app-route] (ecmascript) (src/graphql/typedefs.ts:3:28)
|
||||||
|
at [project]/src/app/api/graphql/route.ts [app-route] (ecmascript) (src/app/api/graphql/route.ts:5:0)
|
||||||
|
at Object.<anonymous> (.next/server/app/api/graphql/route.js:15:9)
|
||||||
|
1 | import { gql } from "graphql-tag";
|
||||||
|
2 |
|
||||||
|
> 3 | export const typeDefs = gql`
|
||||||
|
| ^
|
||||||
|
4 | scalar DateTime
|
||||||
|
5 |
|
||||||
|
6 | type Query { {
|
||||||
|
path: undefined,
|
||||||
|
locations: [Array],
|
||||||
|
extensions: [Object: null prototype] {},
|
||||||
|
page: '/api/graphql'
|
||||||
|
}
|
||||||
|
POST /api/graphql 500 in 106ms
|
||||||
|
POST /api/graphql 500 in 110ms
|
||||||
|
[?25h
|
@ -1,19 +1,10 @@
|
|||||||
import { AuthGuard } from "@/components/auth-guard";
|
import { AuthGuard } from "@/components/auth-guard";
|
||||||
|
import { CreateSuppliersSupplyPage } from "@/components/supplies/create-suppliers-supply-page";
|
||||||
|
|
||||||
export default function CreateSuppliersSupplyPageRoute() {
|
export default function CreateSuppliersSupplyPageRoute() {
|
||||||
return (
|
return (
|
||||||
<AuthGuard>
|
<AuthGuard>
|
||||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900">
|
<CreateSuppliersSupplyPage />
|
||||||
<div className="text-center text-white">
|
|
||||||
<h1 className="text-3xl font-bold mb-4">
|
|
||||||
Создание поставки поставщиков
|
|
||||||
</h1>
|
|
||||||
<p className="text-white/70 mb-6">
|
|
||||||
Заказ товаров у поставщиков с рецептурой
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-white/50">Раздел находится в разработке</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AuthGuard>
|
</AuthGuard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
392
src/components/supplies/add-goods-modal.tsx
Normal file
392
src/components/supplies/add-goods-modal.tsx
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import {
|
||||||
|
Package,
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
X,
|
||||||
|
FileText,
|
||||||
|
Settings,
|
||||||
|
ShoppingCart,
|
||||||
|
} from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
interface GoodsProduct {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
price: number;
|
||||||
|
category?: { name: string };
|
||||||
|
images: string[];
|
||||||
|
mainImage?: string;
|
||||||
|
sku: string;
|
||||||
|
organization: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
stock?: number;
|
||||||
|
unit?: string;
|
||||||
|
weight?: number;
|
||||||
|
dimensions?: {
|
||||||
|
length: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProductParameter {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddGoodsModalProps {
|
||||||
|
product: GoodsProduct | null;
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onAdd: (
|
||||||
|
product: GoodsProduct,
|
||||||
|
quantity: number,
|
||||||
|
additionalData: {
|
||||||
|
completeness?: string;
|
||||||
|
recipe?: string;
|
||||||
|
specialRequirements?: string;
|
||||||
|
parameters?: ProductParameter[];
|
||||||
|
customPrice?: number;
|
||||||
|
}
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddGoodsModal({
|
||||||
|
product,
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onAdd,
|
||||||
|
}: AddGoodsModalProps) {
|
||||||
|
const [quantity, setQuantity] = useState(1);
|
||||||
|
const [completeness, setCompleteness] = useState("");
|
||||||
|
const [recipe, setRecipe] = useState("");
|
||||||
|
const [specialRequirements, setSpecialRequirements] = useState("");
|
||||||
|
const [customPrice, setCustomPrice] = useState("");
|
||||||
|
const [parameters, setParameters] = useState<ProductParameter[]>([]);
|
||||||
|
const [newParameterName, setNewParameterName] = useState("");
|
||||||
|
const [newParameterValue, setNewParameterValue] = useState("");
|
||||||
|
|
||||||
|
// Сброс формы при закрытии
|
||||||
|
const handleClose = () => {
|
||||||
|
setQuantity(1);
|
||||||
|
setCompleteness("");
|
||||||
|
setRecipe("");
|
||||||
|
setSpecialRequirements("");
|
||||||
|
setCustomPrice("");
|
||||||
|
setParameters([]);
|
||||||
|
setNewParameterName("");
|
||||||
|
setNewParameterValue("");
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Добавление параметра
|
||||||
|
const addParameter = () => {
|
||||||
|
if (newParameterName.trim() && newParameterValue.trim()) {
|
||||||
|
setParameters(prev => [
|
||||||
|
...prev,
|
||||||
|
{ name: newParameterName.trim(), value: newParameterValue.trim() }
|
||||||
|
]);
|
||||||
|
setNewParameterName("");
|
||||||
|
setNewParameterValue("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Удаление параметра
|
||||||
|
const removeParameter = (index: number) => {
|
||||||
|
setParameters(prev => prev.filter((_, i) => i !== index));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Обработка добавления в корзину
|
||||||
|
const handleAdd = () => {
|
||||||
|
if (!product) return;
|
||||||
|
|
||||||
|
// Проверка остатков согласно rules2.md 9.7.9
|
||||||
|
if (product.stock !== undefined && quantity > product.stock) {
|
||||||
|
return; // Ошибка будет показана в родительском компоненте
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalPrice = customPrice ? parseFloat(customPrice) : product.price;
|
||||||
|
|
||||||
|
onAdd(product, quantity, {
|
||||||
|
completeness: completeness.trim() || undefined,
|
||||||
|
recipe: recipe.trim() || undefined,
|
||||||
|
specialRequirements: specialRequirements.trim() || undefined,
|
||||||
|
parameters: parameters.length > 0 ? parameters : undefined,
|
||||||
|
customPrice: finalPrice !== product.price ? finalPrice : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!product) return null;
|
||||||
|
|
||||||
|
const isStockAvailable = product.stock === undefined || quantity <= product.stock;
|
||||||
|
const totalPrice = (customPrice ? parseFloat(customPrice) || product.price : product.price) * quantity;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||||
|
<DialogContent className="sm:max-w-[600px] bg-white/10 backdrop-blur-xl border border-white/20 text-white">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-2 text-xl">
|
||||||
|
<Package className="h-6 w-6 text-white/80" />
|
||||||
|
Добавить товар в корзину
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="space-y-6 max-h-[70vh] overflow-y-auto pr-2">
|
||||||
|
{/* Информация о товаре */}
|
||||||
|
<div className="bg-white/5 border border-white/10 rounded-lg p-4">
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{product.mainImage && (
|
||||||
|
<div className="w-24 h-24 rounded-lg overflow-hidden bg-white/5 flex-shrink-0">
|
||||||
|
<Image
|
||||||
|
src={product.mainImage}
|
||||||
|
alt={product.name}
|
||||||
|
width={96}
|
||||||
|
height={96}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<h3 className="text-white font-semibold text-lg">{product.name}</h3>
|
||||||
|
<p className="text-white/60 text-sm">Артикул: {product.sku}</p>
|
||||||
|
{product.category && (
|
||||||
|
<Badge className="bg-white/10 text-white/70 text-xs">
|
||||||
|
{product.category.name}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<p className="text-white font-medium">
|
||||||
|
Цена: {product.price.toLocaleString('ru-RU')} ₽ за {product.unit || 'шт'}
|
||||||
|
</p>
|
||||||
|
{product.stock !== undefined && (
|
||||||
|
<p className="text-white/60 text-sm">
|
||||||
|
В наличии: {product.stock} {product.unit || 'шт'}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{product.description && (
|
||||||
|
<p className="text-white/70 text-sm mt-3 pt-3 border-t border-white/10">
|
||||||
|
{product.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Основные поля */}
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{/* Количество */}
|
||||||
|
<div>
|
||||||
|
<Label className="text-white/70 text-sm mb-2 block">
|
||||||
|
Количество *
|
||||||
|
</Label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setQuantity(Math.max(1, quantity - 1))}
|
||||||
|
className="border-white/20 text-white hover:bg-white/10"
|
||||||
|
>
|
||||||
|
<Minus className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max={product.stock}
|
||||||
|
value={quantity}
|
||||||
|
onChange={(e) => setQuantity(Math.max(1, parseInt(e.target.value) || 1))}
|
||||||
|
className="bg-white/10 border-white/20 text-white text-center w-20"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setQuantity(product.stock ? Math.min(product.stock, quantity + 1) : quantity + 1)}
|
||||||
|
className="border-white/20 text-white hover:bg-white/10"
|
||||||
|
disabled={product.stock !== undefined && quantity >= product.stock}
|
||||||
|
>
|
||||||
|
<Plus className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{!isStockAvailable && (
|
||||||
|
<p className="text-red-400 text-xs mt-1">
|
||||||
|
Недостаточно товара на складе
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Договорная цена */}
|
||||||
|
<div>
|
||||||
|
<Label className="text-white/70 text-sm mb-2 block">
|
||||||
|
Договорная цена (₽)
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value={customPrice}
|
||||||
|
onChange={(e) => setCustomPrice(e.target.value)}
|
||||||
|
placeholder={product.price.toString()}
|
||||||
|
className="bg-white/10 border-white/20 text-white"
|
||||||
|
/>
|
||||||
|
<p className="text-white/40 text-xs mt-1">
|
||||||
|
Оставьте пустым для использования базовой цены
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Комплектность согласно rules2.md 9.7.2 */}
|
||||||
|
<div>
|
||||||
|
<Label className="text-white/70 text-sm mb-2 block">
|
||||||
|
Комплектность (если есть)
|
||||||
|
</Label>
|
||||||
|
<Textarea
|
||||||
|
value={completeness}
|
||||||
|
onChange={(e) => setCompleteness(e.target.value)}
|
||||||
|
placeholder="Опишите состав комплекта, если товар продается набором..."
|
||||||
|
className="bg-white/10 border-white/20 text-white placeholder-white/40 resize-none"
|
||||||
|
rows={2}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Рецептура/состав согласно rules2.md 9.7.2 */}
|
||||||
|
<div>
|
||||||
|
<Label className="text-white/70 text-sm mb-2 block">
|
||||||
|
Рецептура/состав
|
||||||
|
</Label>
|
||||||
|
<Textarea
|
||||||
|
value={recipe}
|
||||||
|
onChange={(e) => setRecipe(e.target.value)}
|
||||||
|
placeholder="Дополнительные требования к составу, рецептуре или производству..."
|
||||||
|
className="bg-white/10 border-white/20 text-white placeholder-white/40 resize-none"
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Особые требования */}
|
||||||
|
<div>
|
||||||
|
<Label className="text-white/70 text-sm mb-2 block">
|
||||||
|
Особые требования к товару
|
||||||
|
</Label>
|
||||||
|
<Textarea
|
||||||
|
value={specialRequirements}
|
||||||
|
onChange={(e) => setSpecialRequirements(e.target.value)}
|
||||||
|
placeholder="Особые требования к качеству, упаковке, срокам и т.д..."
|
||||||
|
className="bg-white/10 border-white/20 text-white placeholder-white/40 resize-none"
|
||||||
|
rows={2}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Параметры товара согласно rules2.md 9.7.5 */}
|
||||||
|
<div>
|
||||||
|
<Label className="text-white/70 text-sm mb-2 block flex items-center gap-2">
|
||||||
|
<Settings className="h-4 w-4" />
|
||||||
|
Параметры товара
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
{/* Существующие параметры */}
|
||||||
|
{parameters.length > 0 && (
|
||||||
|
<div className="space-y-2 mb-3">
|
||||||
|
{parameters.map((param, index) => (
|
||||||
|
<div key={index} className="flex items-center gap-2 bg-white/5 border border-white/10 rounded-lg p-2">
|
||||||
|
<span className="text-white/80 text-sm font-medium">{param.name}:</span>
|
||||||
|
<span className="text-white text-sm">{param.value}</span>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => removeParameter(index)}
|
||||||
|
className="text-red-400 hover:text-red-300 hover:bg-red-500/10 ml-auto p-1 h-auto"
|
||||||
|
>
|
||||||
|
<X className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Добавление нового параметра */}
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Input
|
||||||
|
value={newParameterName}
|
||||||
|
onChange={(e) => setNewParameterName(e.target.value)}
|
||||||
|
placeholder="Название параметра (цвет, размер...)"
|
||||||
|
className="bg-white/10 border-white/20 text-white placeholder-white/40 flex-1"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
value={newParameterValue}
|
||||||
|
onChange={(e) => setNewParameterValue(e.target.value)}
|
||||||
|
placeholder="Значение"
|
||||||
|
className="bg-white/10 border-white/20 text-white placeholder-white/40 flex-1"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={addParameter}
|
||||||
|
disabled={!newParameterName.trim() || !newParameterValue.trim()}
|
||||||
|
className="bg-blue-500/20 hover:bg-blue-500/30 text-blue-300 border-blue-500/30"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Итоговая стоимость */}
|
||||||
|
<div className="bg-white/5 border border-white/10 rounded-lg p-4">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-white/70">Итоговая стоимость:</span>
|
||||||
|
<span className="text-green-400 font-bold text-lg">
|
||||||
|
{totalPrice.toLocaleString('ru-RU')} ₽
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{customPrice && parseFloat(customPrice) !== product.price && (
|
||||||
|
<p className="text-yellow-400 text-xs mt-1">
|
||||||
|
Используется договорная цена: {parseFloat(customPrice).toLocaleString('ru-RU')} ₽ за {product.unit || 'шт'}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter className="gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={handleClose}
|
||||||
|
className="border-white/20 text-white hover:bg-white/10"
|
||||||
|
>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleAdd}
|
||||||
|
disabled={!isStockAvailable || quantity < 1}
|
||||||
|
className="bg-gradient-to-r from-green-500 to-blue-500 hover:from-green-600 hover:to-blue-600 text-white"
|
||||||
|
>
|
||||||
|
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||||
|
Добавить в корзину
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
@ -169,6 +169,29 @@ export function CreateConsumablesSupplyPage() {
|
|||||||
);
|
);
|
||||||
if (!product || !selectedSupplier) return;
|
if (!product || !selectedSupplier) return;
|
||||||
|
|
||||||
|
// ✅ ПРОВЕРКА ОСТАТКОВ согласно rules2.md раздел 9.4.5
|
||||||
|
if (quantity > 0) {
|
||||||
|
// Проверяем доступность на складе
|
||||||
|
if (product.stock !== undefined && quantity > product.stock) {
|
||||||
|
toast.error(
|
||||||
|
`Недостаточно товара на складе. Доступно: ${product.stock} ${product.unit || 'шт'}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Логируем попытку добавления для аудита
|
||||||
|
console.log('📊 Stock check:', {
|
||||||
|
action: 'add_to_cart',
|
||||||
|
productId: product.id,
|
||||||
|
productName: product.name,
|
||||||
|
requested: quantity,
|
||||||
|
available: product.stock || 'unlimited',
|
||||||
|
result: quantity <= (product.stock || Infinity) ? 'allowed' : 'blocked',
|
||||||
|
userId: selectedSupplier.id,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedConsumables((prev) => {
|
setSelectedConsumables((prev) => {
|
||||||
const existing = prev.find((p) => p.id === productId);
|
const existing = prev.find((p) => p.id === productId);
|
||||||
|
|
||||||
@ -233,6 +256,18 @@ export function CreateConsumablesSupplyPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ ФИНАЛЬНАЯ ПРОВЕРКА ОСТАТКОВ перед созданием заказа
|
||||||
|
for (const consumable of selectedConsumables) {
|
||||||
|
const product = supplierProducts.find(p => p.id === consumable.id);
|
||||||
|
if (product?.stock !== undefined && consumable.selectedQuantity > product.stock) {
|
||||||
|
toast.error(
|
||||||
|
`Товар "${consumable.name}" недоступен в количестве ${consumable.selectedQuantity}. ` +
|
||||||
|
`Доступно: ${product.stock} ${product.unit || 'шт'}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Для селлеров требуется выбор фулфилмент-центра
|
// Для селлеров требуется выбор фулфилмент-центра
|
||||||
if (!selectedFulfillmentCenter) {
|
if (!selectedFulfillmentCenter) {
|
||||||
toast.error("Выберите фулфилмент-центр для доставки");
|
toast.error("Выберите фулфилмент-центр для доставки");
|
||||||
@ -261,12 +296,20 @@ export function CreateConsumablesSupplyPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем дату
|
// ✅ ПРОВЕРКА ДАТЫ согласно rules2.md - запрет прошедших дат
|
||||||
const deliveryDateObj = new Date(deliveryDate);
|
const deliveryDateObj = new Date(deliveryDate);
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
if (isNaN(deliveryDateObj.getTime())) {
|
if (isNaN(deliveryDateObj.getTime())) {
|
||||||
toast.error("Некорректная дата поставки");
|
toast.error("Некорректная дата поставки");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deliveryDateObj < today) {
|
||||||
|
toast.error("Нельзя выбрать прошедшую дату поставки");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsCreatingSupply(true);
|
setIsCreatingSupply(true);
|
||||||
|
|
||||||
@ -948,7 +991,17 @@ export function CreateConsumablesSupplyPage() {
|
|||||||
<Input
|
<Input
|
||||||
type="date"
|
type="date"
|
||||||
value={deliveryDate}
|
value={deliveryDate}
|
||||||
onChange={(e) => setDeliveryDate(e.target.value)}
|
onChange={(e) => {
|
||||||
|
const selectedDate = new Date(e.target.value);
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0,0,0,0);
|
||||||
|
|
||||||
|
if (selectedDate < today) {
|
||||||
|
toast.error("Нельзя выбрать прошедшую дату поставки");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDeliveryDate(e.target.value);
|
||||||
|
}}
|
||||||
className="bg-white/10 border-white/20 text-white h-8 text-sm"
|
className="bg-white/10 border-white/20 text-white h-8 text-sm"
|
||||||
min={new Date().toISOString().split("T")[0]}
|
min={new Date().toISOString().split("T")[0]}
|
||||||
required
|
required
|
||||||
|
1210
src/components/supplies/create-suppliers-supply-page.tsx
Normal file
1210
src/components/supplies/create-suppliers-supply-page.tsx
Normal file
@ -0,0 +1,1210 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useQuery, useMutation } from "@apollo/client";
|
||||||
|
import { Sidebar } from "@/components/dashboard/sidebar";
|
||||||
|
import { useSidebar } from "@/hooks/useSidebar";
|
||||||
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
|
import { Card } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
ArrowLeft,
|
||||||
|
Building2,
|
||||||
|
MapPin,
|
||||||
|
Phone,
|
||||||
|
Mail,
|
||||||
|
Star,
|
||||||
|
Search,
|
||||||
|
Package,
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
ShoppingCart,
|
||||||
|
Calendar,
|
||||||
|
Truck,
|
||||||
|
Box,
|
||||||
|
FileText,
|
||||||
|
AlertCircle,
|
||||||
|
Settings,
|
||||||
|
DollarSign,
|
||||||
|
} from "lucide-react";
|
||||||
|
import {
|
||||||
|
GET_MY_COUNTERPARTIES,
|
||||||
|
GET_ORGANIZATION_PRODUCTS,
|
||||||
|
} from "@/graphql/queries";
|
||||||
|
import { CREATE_SUPPLY_ORDER } from "@/graphql/mutations";
|
||||||
|
import { OrganizationAvatar } from "@/components/market/organization-avatar";
|
||||||
|
import { AddGoodsModal } from "./add-goods-modal";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
// Интерфейсы согласно rules2.md 9.7
|
||||||
|
interface GoodsSupplier {
|
||||||
|
id: string;
|
||||||
|
inn: string;
|
||||||
|
name?: string;
|
||||||
|
fullName?: string;
|
||||||
|
type: "FULFILLMENT" | "SELLER" | "LOGIST" | "WHOLESALE";
|
||||||
|
address?: string;
|
||||||
|
phones?: Array<{ value: string }>;
|
||||||
|
emails?: Array<{ value: string }>;
|
||||||
|
users?: Array<{ id: string; avatar?: string; managerName?: string }>;
|
||||||
|
createdAt: string;
|
||||||
|
rating?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoodsProduct {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
price: number;
|
||||||
|
category?: { name: string };
|
||||||
|
images: string[];
|
||||||
|
mainImage?: string;
|
||||||
|
article: string; // Артикул поставщика
|
||||||
|
organization: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
quantity?: number;
|
||||||
|
unit?: string;
|
||||||
|
weight?: number;
|
||||||
|
dimensions?: {
|
||||||
|
length: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SelectedGoodsItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
sku: string;
|
||||||
|
price: number;
|
||||||
|
selectedQuantity: number;
|
||||||
|
unit?: string;
|
||||||
|
category?: string;
|
||||||
|
supplierId: string;
|
||||||
|
supplierName: string;
|
||||||
|
completeness?: string; // Комплектность согласно rules2.md 9.7.2
|
||||||
|
recipe?: string; // Рецептура/состав
|
||||||
|
specialRequirements?: string; // Особые требования
|
||||||
|
parameters?: Array<{ name: string; value: string }>; // Параметры товара
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LogisticsCompany {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
estimatedCost: number;
|
||||||
|
deliveryDays: number;
|
||||||
|
type: "EXPRESS" | "STANDARD" | "ECONOMY";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreateSuppliersSupplyPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { getSidebarMargin } = useSidebar();
|
||||||
|
|
||||||
|
// Основные состояния
|
||||||
|
const [selectedSupplier, setSelectedSupplier] = useState<GoodsSupplier | null>(null);
|
||||||
|
const [selectedGoods, setSelectedGoods] = useState<SelectedGoodsItem[]>([]);
|
||||||
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
const [productSearchQuery, setProductSearchQuery] = useState("");
|
||||||
|
|
||||||
|
// Обязательные поля согласно rules2.md 9.7.8
|
||||||
|
const [deliveryDate, setDeliveryDate] = useState("");
|
||||||
|
|
||||||
|
// Выбор логистики согласно rules2.md 9.7.7
|
||||||
|
const [selectedLogistics, setSelectedLogistics] = useState<string>("auto"); // "auto" или ID компании
|
||||||
|
|
||||||
|
// Выбор фулфилмента согласно rules2.md 9.7.2
|
||||||
|
const [selectedFulfillment, setSelectedFulfillment] = useState<string>("");
|
||||||
|
|
||||||
|
// Модальное окно для детального добавления товара
|
||||||
|
const [selectedProductForModal, setSelectedProductForModal] = useState<GoodsProduct | null>(null);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const [isCreatingSupply, setIsCreatingSupply] = useState(false);
|
||||||
|
|
||||||
|
// Состояние количества товаров для карточек согласно rules2.md 13.3
|
||||||
|
const [productQuantities, setProductQuantities] = useState<Record<string, number>>({});
|
||||||
|
|
||||||
|
// Загружаем партнеров-поставщиков согласно rules2.md 13.3
|
||||||
|
const { data: counterpartiesData, loading: counterpartiesLoading, error: counterpartiesError } = useQuery(
|
||||||
|
GET_MY_COUNTERPARTIES,
|
||||||
|
{
|
||||||
|
errorPolicy: 'all', // Показываем все ошибки, но не прерываем работу
|
||||||
|
onError: (error) => {
|
||||||
|
try {
|
||||||
|
console.log("🚨 GET_MY_COUNTERPARTIES ERROR:", {
|
||||||
|
errorMessage: error?.message || "Unknown error",
|
||||||
|
hasGraphQLErrors: !!error?.graphQLErrors?.length,
|
||||||
|
hasNetworkError: !!error?.networkError,
|
||||||
|
});
|
||||||
|
} catch (logError) {
|
||||||
|
console.log("❌ Error in counterparties error handler:", logError);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Загружаем каталог товаров согласно rules2.md 13.3
|
||||||
|
// Товары поставщика загружаются из Product таблицы where organizationId = поставщик.id
|
||||||
|
const { data: productsData, loading: productsLoading, error: productsError } = useQuery(
|
||||||
|
GET_ORGANIZATION_PRODUCTS,
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
organizationId: selectedSupplier?.id || "", // Избегаем undefined для обязательного параметра
|
||||||
|
search: productSearchQuery, // Используем поисковый запрос для фильтрации
|
||||||
|
category: "", // Пока без фильтра по категории
|
||||||
|
type: "PRODUCT", // КРИТИЧЕСКИ ВАЖНО: показываем только PRODUCT, не CONSUMABLE согласно development-checklist.md
|
||||||
|
},
|
||||||
|
skip: !selectedSupplier || !selectedSupplier.id, // Более строгая проверка
|
||||||
|
fetchPolicy: 'network-only', // Обходим кеш для получения актуальных данных
|
||||||
|
errorPolicy: 'all', // Показываем все ошибки, но не прерываем работу
|
||||||
|
onError: (error) => {
|
||||||
|
try {
|
||||||
|
console.log("🚨 GET_ORGANIZATION_PRODUCTS ERROR:", {
|
||||||
|
errorMessage: error?.message || "Unknown error",
|
||||||
|
hasGraphQLErrors: !!error?.graphQLErrors?.length,
|
||||||
|
hasNetworkError: !!error?.networkError,
|
||||||
|
variables: {
|
||||||
|
organizationId: selectedSupplier?.id || "not_selected",
|
||||||
|
search: productSearchQuery || "empty",
|
||||||
|
category: "",
|
||||||
|
type: "PRODUCT",
|
||||||
|
},
|
||||||
|
selectedSupplier: selectedSupplier ? {
|
||||||
|
id: selectedSupplier.id,
|
||||||
|
name: selectedSupplier.name || selectedSupplier.fullName || "Unknown",
|
||||||
|
} : "not_selected",
|
||||||
|
});
|
||||||
|
} catch (logError) {
|
||||||
|
console.log("❌ Error in error handler:", logError);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Мутация создания поставки
|
||||||
|
const [createSupplyOrder] = useMutation(CREATE_SUPPLY_ORDER);
|
||||||
|
|
||||||
|
// Фильтруем только партнеров-поставщиков согласно rules2.md 13.3
|
||||||
|
const allCounterparties = counterpartiesData?.myCounterparties || [];
|
||||||
|
|
||||||
|
// Показываем только партнеров с типом WHOLESALE согласно rules2.md 13.3
|
||||||
|
const wholesaleSuppliers = allCounterparties.filter((cp: any) => {
|
||||||
|
try {
|
||||||
|
return cp && cp.type === "WHOLESALE";
|
||||||
|
} catch (error) {
|
||||||
|
console.log("❌ Error filtering wholesale suppliers:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const suppliers = wholesaleSuppliers.filter((cp: GoodsSupplier) => {
|
||||||
|
try {
|
||||||
|
if (!cp) return false;
|
||||||
|
const searchLower = searchQuery.toLowerCase();
|
||||||
|
return (
|
||||||
|
cp.name?.toLowerCase().includes(searchLower) ||
|
||||||
|
cp.fullName?.toLowerCase().includes(searchLower) ||
|
||||||
|
cp.inn?.includes(searchQuery) ||
|
||||||
|
cp.phones?.some(phone => phone.value?.includes(searchQuery))
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("❌ Error filtering suppliers by search:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const isLoading = counterpartiesLoading;
|
||||||
|
|
||||||
|
// Получаем товары выбранного поставщика согласно rules2.md 13.3
|
||||||
|
// Теперь фильтрация происходит на сервере через GraphQL запрос
|
||||||
|
const products = (productsData?.organizationProducts || []).filter((product: any) => {
|
||||||
|
try {
|
||||||
|
return product && product.id && product.name;
|
||||||
|
} catch (error) {
|
||||||
|
console.log("❌ Error filtering products:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Отладочные логи согласно development-checklist.md
|
||||||
|
console.log("🛒 CREATE_SUPPLIERS_SUPPLY DEBUG:", {
|
||||||
|
selectedSupplier: selectedSupplier ? {
|
||||||
|
id: selectedSupplier.id,
|
||||||
|
name: selectedSupplier.name,
|
||||||
|
type: selectedSupplier.type,
|
||||||
|
} : null,
|
||||||
|
counterpartiesStatus: {
|
||||||
|
loading: counterpartiesLoading,
|
||||||
|
error: counterpartiesError?.message,
|
||||||
|
dataCount: counterpartiesData?.myCounterparties?.length || 0,
|
||||||
|
},
|
||||||
|
productsStatus: {
|
||||||
|
loading: productsLoading,
|
||||||
|
error: productsError?.message,
|
||||||
|
dataCount: products.length,
|
||||||
|
hasData: !!productsData?.organizationProducts,
|
||||||
|
productSample: products.slice(0, 3).map(p => ({ id: p.id, name: p.name, article: p.article })),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Моковые логистические компании согласно rules2.md 9.7.7
|
||||||
|
const logisticsCompanies: LogisticsCompany[] = [
|
||||||
|
{ id: "express", name: "Экспресс доставка", estimatedCost: 2500, deliveryDays: 1, type: "EXPRESS" },
|
||||||
|
{ id: "standard", name: "Стандартная доставка", estimatedCost: 1200, deliveryDays: 3, type: "STANDARD" },
|
||||||
|
{ id: "economy", name: "Экономичная доставка", estimatedCost: 800, deliveryDays: 7, type: "ECONOMY" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Моковые фулфилмент-центры согласно rules2.md 9.7.2
|
||||||
|
const fulfillmentCenters = [
|
||||||
|
{ id: "ff1", name: "СФ Центр Москва", address: "г. Москва, ул. Складская 10" },
|
||||||
|
{ id: "ff2", name: "СФ Центр СПб", address: "г. Санкт-Петербург, пр. Логистический 5" },
|
||||||
|
{ id: "ff3", name: "СФ Центр Екатеринбург", address: "г. Екатеринбург, ул. Промышленная 15" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Функции для работы с количеством товаров в карточках согласно rules2.md 13.3
|
||||||
|
const getProductQuantity = (productId: string): number => {
|
||||||
|
return productQuantities[productId] || 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setProductQuantity = (productId: string, quantity: number): void => {
|
||||||
|
setProductQuantities(prev => ({
|
||||||
|
...prev,
|
||||||
|
[productId]: Math.max(0, quantity)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateProductQuantity = (productId: string, delta: number): void => {
|
||||||
|
const currentQuantity = getProductQuantity(productId);
|
||||||
|
const newQuantity = currentQuantity + delta;
|
||||||
|
setProductQuantity(productId, newQuantity);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Добавление товара в корзину из карточки с заданным количеством
|
||||||
|
const addToCart = (product: GoodsProduct) => {
|
||||||
|
const quantity = getProductQuantity(product.id);
|
||||||
|
if (quantity <= 0) {
|
||||||
|
toast.error("Укажите количество товара");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка остатков согласно rules2.md 9.7.9
|
||||||
|
if (product.quantity !== undefined && quantity > product.quantity) {
|
||||||
|
toast.error(`Недостаточно товара на складе. Доступно: ${product.quantity} ${product.unit || 'шт'}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedSupplier) {
|
||||||
|
toast.error("Не выбран поставщик");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newGoodsItem: SelectedGoodsItem = {
|
||||||
|
id: product.id,
|
||||||
|
name: product.name,
|
||||||
|
sku: product.article,
|
||||||
|
price: product.price,
|
||||||
|
selectedQuantity: quantity,
|
||||||
|
unit: product.unit,
|
||||||
|
category: product.category?.name,
|
||||||
|
supplierId: selectedSupplier.id,
|
||||||
|
supplierName: selectedSupplier.name || selectedSupplier.fullName || "Неизвестный поставщик",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Проверяем, есть ли уже такой товар в корзине
|
||||||
|
const existingItemIndex = selectedGoods.findIndex(item => item.id === product.id);
|
||||||
|
|
||||||
|
if (existingItemIndex >= 0) {
|
||||||
|
// Обновляем количество существующего товара
|
||||||
|
const updatedGoods = [...selectedGoods];
|
||||||
|
updatedGoods[existingItemIndex] = {
|
||||||
|
...updatedGoods[existingItemIndex],
|
||||||
|
selectedQuantity: quantity
|
||||||
|
};
|
||||||
|
setSelectedGoods(updatedGoods);
|
||||||
|
toast.success(`Количество товара "${product.name}" обновлено в корзине`);
|
||||||
|
} else {
|
||||||
|
// Добавляем новый товар
|
||||||
|
setSelectedGoods(prev => [...prev, newGoodsItem]);
|
||||||
|
toast.success(`Товар "${product.name}" добавлен в корзину`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сбрасываем количество в карточке
|
||||||
|
setProductQuantity(product.id, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Открытие модального окна для детального добавления
|
||||||
|
const openAddModal = (product: GoodsProduct) => {
|
||||||
|
setSelectedProductForModal(product);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Добавление товара в корзину из модального окна с дополнительными данными
|
||||||
|
const addToCartFromModal = (
|
||||||
|
product: GoodsProduct,
|
||||||
|
quantity: number,
|
||||||
|
additionalData?: {
|
||||||
|
completeness?: string;
|
||||||
|
recipe?: string;
|
||||||
|
specialRequirements?: string;
|
||||||
|
parameters?: Array<{ name: string; value: string }>;
|
||||||
|
customPrice?: number;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
// Проверка остатков согласно rules2.md 9.7.9
|
||||||
|
if (product.quantity !== undefined && quantity > product.quantity) {
|
||||||
|
toast.error(`Недостаточно товара на складе. Доступно: ${product.quantity} ${product.unit || 'шт'}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingItem = selectedGoods.find(item => item.id === product.id);
|
||||||
|
const finalPrice = additionalData?.customPrice || product.price;
|
||||||
|
|
||||||
|
if (existingItem) {
|
||||||
|
// Обновляем существующий товар
|
||||||
|
setSelectedGoods(prev =>
|
||||||
|
prev.map(item =>
|
||||||
|
item.id === product.id
|
||||||
|
? {
|
||||||
|
...item,
|
||||||
|
selectedQuantity: quantity,
|
||||||
|
price: finalPrice,
|
||||||
|
completeness: additionalData?.completeness,
|
||||||
|
recipe: additionalData?.recipe,
|
||||||
|
specialRequirements: additionalData?.specialRequirements,
|
||||||
|
parameters: additionalData?.parameters,
|
||||||
|
}
|
||||||
|
: item
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Добавляем новый товар
|
||||||
|
const newItem: SelectedGoodsItem = {
|
||||||
|
id: product.id,
|
||||||
|
name: product.name,
|
||||||
|
sku: product.article,
|
||||||
|
price: finalPrice,
|
||||||
|
selectedQuantity: quantity,
|
||||||
|
unit: product.unit,
|
||||||
|
category: product.category?.name,
|
||||||
|
supplierId: selectedSupplier!.id,
|
||||||
|
supplierName: selectedSupplier!.name || selectedSupplier!.fullName || '',
|
||||||
|
completeness: additionalData?.completeness,
|
||||||
|
recipe: additionalData?.recipe,
|
||||||
|
specialRequirements: additionalData?.specialRequirements,
|
||||||
|
parameters: additionalData?.parameters,
|
||||||
|
};
|
||||||
|
|
||||||
|
setSelectedGoods(prev => [...prev, newItem]);
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success("Товар добавлен в корзину");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Удаление из корзины
|
||||||
|
const removeFromCart = (productId: string) => {
|
||||||
|
setSelectedGoods(prev => prev.filter(item => item.id !== productId));
|
||||||
|
toast.success("Товар удален из корзины");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Расчеты согласно rules2.md 9.7.6
|
||||||
|
const totalGoodsAmount = selectedGoods.reduce((sum, item) => sum + (item.price * item.selectedQuantity), 0);
|
||||||
|
const totalQuantity = selectedGoods.reduce((sum, item) => sum + item.selectedQuantity, 0);
|
||||||
|
const fulfillmentFee = totalGoodsAmount * 0.08; // 8% комиссия фулфилмента
|
||||||
|
const selectedLogisticsCompany = logisticsCompanies.find(lc => lc.id === selectedLogistics);
|
||||||
|
const logisticsCost = selectedLogistics === "auto" ? 1000 : (selectedLogisticsCompany?.estimatedCost || 0);
|
||||||
|
const totalAmount = totalGoodsAmount + fulfillmentFee + logisticsCost;
|
||||||
|
|
||||||
|
// Валидация формы согласно rules2.md 9.7.6
|
||||||
|
const isFormValid = selectedSupplier && selectedGoods.length > 0 && deliveryDate && selectedFulfillment;
|
||||||
|
|
||||||
|
// Создание поставки
|
||||||
|
const handleCreateSupply = async () => {
|
||||||
|
if (!isFormValid) {
|
||||||
|
toast.error("Заполните все обязательные поля");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsCreatingSupply(true);
|
||||||
|
try {
|
||||||
|
await createSupplyOrder({
|
||||||
|
variables: {
|
||||||
|
supplierId: selectedSupplier!.id,
|
||||||
|
fulfillmentCenterId: selectedFulfillment,
|
||||||
|
items: selectedGoods.map(item => ({
|
||||||
|
productId: item.id,
|
||||||
|
quantity: item.selectedQuantity,
|
||||||
|
price: item.price,
|
||||||
|
completeness: item.completeness,
|
||||||
|
recipe: item.recipe,
|
||||||
|
specialRequirements: item.specialRequirements,
|
||||||
|
parameters: item.parameters,
|
||||||
|
})),
|
||||||
|
deliveryDate,
|
||||||
|
logisticsCompany: selectedLogistics === "auto" ? null : selectedLogistics,
|
||||||
|
type: "ТОВАР",
|
||||||
|
creationMethod: "suppliers",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
toast.success("Поставка успешно создана");
|
||||||
|
router.push("/supplies?tab=goods&subTab=suppliers");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Ошибка создания поставки:", error);
|
||||||
|
toast.error("Ошибка при создании поставки");
|
||||||
|
} finally {
|
||||||
|
setIsCreatingSupply(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Получение минимальной и максимальной даты согласно rules2.md 9.7.8
|
||||||
|
const tomorrow = new Date();
|
||||||
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||||
|
const maxDate = new Date();
|
||||||
|
maxDate.setDate(maxDate.getDate() + 90);
|
||||||
|
|
||||||
|
const minDateString = tomorrow.toISOString().split('T')[0];
|
||||||
|
const maxDateString = maxDate.toISOString().split('T')[0];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-screen flex overflow-hidden">
|
||||||
|
<Sidebar />
|
||||||
|
<main className={`flex-1 ${getSidebarMargin()} overflow-hidden transition-all duration-300`}>
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
|
||||||
|
{/* СТРУКТУРА ИЗ 3 БЛОКОВ согласно rules1.md 19.2.1 - блоки точно по уровню сайдбара */}
|
||||||
|
<div className="flex-1 flex gap-2 min-h-0">
|
||||||
|
|
||||||
|
{/* ЛЕВЫЙ БЛОК: ПОСТАВЩИКИ И ТОВАРЫ */}
|
||||||
|
<div className="flex-1 flex flex-col gap-2 min-h-0">
|
||||||
|
|
||||||
|
{/* БЛОК 1: ПОСТАВЩИКИ - обязательный блок согласно rules1.md 19.2.1 */}
|
||||||
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl flex-shrink-0 flex flex-col"
|
||||||
|
style={{ minHeight: '120px', maxHeight: suppliers.length > 4 ? '200px' : 'auto' }}>
|
||||||
|
<div className="p-4 flex-shrink-0">
|
||||||
|
|
||||||
|
{/* Навигация и заголовок в одном блоке */}
|
||||||
|
<div className="flex items-center justify-between gap-4 mb-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => router.push("/supplies?tab=goods&subTab=suppliers")}
|
||||||
|
className="glass-secondary hover:text-white/90 gap-2 transition-all duration-200 -ml-2"
|
||||||
|
>
|
||||||
|
<ArrowLeft className="h-3 w-3" />
|
||||||
|
Назад
|
||||||
|
</Button>
|
||||||
|
<div className="h-4 w-px bg-white/20"></div>
|
||||||
|
<div className="p-2 bg-green-400/10 rounded-lg border border-green-400/20">
|
||||||
|
<Building2 className="h-4 w-4 text-green-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-base font-semibold text-white">Поставщики товаров</h2>
|
||||||
|
<Badge className="bg-purple-500/20 text-purple-300 border border-purple-500/30 text-xs font-medium mt-0.5">
|
||||||
|
Создание поставки
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 max-w-sm">
|
||||||
|
<div className="relative">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-white/40 h-4 w-4" />
|
||||||
|
<Input
|
||||||
|
placeholder="Поиск..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
className="bg-white/5 border-white/10 text-white placeholder:text-white/50 pl-10 h-9 text-sm transition-all duration-200 focus:border-white/20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{allCounterparties.length === 0 && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => router.push("/market")}
|
||||||
|
className="glass-secondary hover:text-white/90 transition-all duration-200 mt-2 w-full"
|
||||||
|
>
|
||||||
|
<Building2 className="h-3 w-3 mr-2" />
|
||||||
|
Найти поставщиков в маркете
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Список поставщиков согласно visual-design-rules.md */}
|
||||||
|
<div className="flex-1 min-h-0">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex items-center justify-center py-8">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-white/60"></div>
|
||||||
|
<span className="ml-3 text-white/70">Загрузка поставщиков...</span>
|
||||||
|
</div>
|
||||||
|
) : suppliers.length === 0 ? (
|
||||||
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<div className="text-center space-y-3">
|
||||||
|
<div className="w-12 h-12 mx-auto bg-white/5 rounded-full flex items-center justify-center">
|
||||||
|
<Building2 className="h-6 w-6 text-white/40" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-base font-medium text-white mb-1">Поставщики товаров не найдены</h3>
|
||||||
|
<p className="text-white/60 text-xs max-w-md mx-auto">
|
||||||
|
{allCounterparties.length === 0
|
||||||
|
? "У вас нет партнеров. Найдите поставщиков в маркете или добавьте их через раздел 'Партнеры'"
|
||||||
|
: wholesaleSuppliers.length === 0
|
||||||
|
? `Найдено ${allCounterparties.length} партнеров, но среди них нет поставщиков (тип WHOLESALE)`
|
||||||
|
: searchQuery && suppliers.length === 0
|
||||||
|
? "Поставщики-партнеры не найдены по вашему запросу"
|
||||||
|
: `Найдено ${suppliers.length} поставщиков-партнеров`
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={`gap-2 overflow-y-auto ${
|
||||||
|
suppliers.length <= 2
|
||||||
|
? 'flex flex-wrap'
|
||||||
|
: suppliers.length <= 4
|
||||||
|
? 'grid grid-cols-2'
|
||||||
|
: 'grid grid-cols-1 md:grid-cols-2 max-h-32'
|
||||||
|
}`}>
|
||||||
|
{suppliers.map((supplier: GoodsSupplier) => (
|
||||||
|
<div
|
||||||
|
key={supplier.id}
|
||||||
|
onClick={() => setSelectedSupplier(supplier)}
|
||||||
|
className={`p-3 rounded-lg cursor-pointer group transition-all duration-200 ${
|
||||||
|
selectedSupplier?.id === supplier.id
|
||||||
|
? "bg-white/15 border border-white/40 shadow-lg"
|
||||||
|
: "bg-white/5 border border-white/10 hover:border-white/20 hover:bg-white/10"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<OrganizationAvatar organization={supplier} size="sm" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<h4 className="text-white font-medium text-sm truncate group-hover:text-white transition-colors">
|
||||||
|
{supplier.name || supplier.fullName}
|
||||||
|
</h4>
|
||||||
|
{supplier.rating && (
|
||||||
|
<div className="flex items-center gap-1 bg-yellow-400/10 px-2 py-0.5 rounded-full">
|
||||||
|
<Star className="h-3 w-3 text-yellow-400 fill-current" />
|
||||||
|
<span className="text-yellow-300 text-xs font-medium">{supplier.rating}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<p className="text-white/60 text-xs font-mono">ИНН: {supplier.inn}</p>
|
||||||
|
<Badge className={`text-xs font-medium ${
|
||||||
|
supplier.type === "WHOLESALE"
|
||||||
|
? "bg-green-500/20 text-green-300 border border-green-500/30"
|
||||||
|
: "bg-yellow-500/20 text-yellow-300 border border-yellow-500/30"
|
||||||
|
}`}>
|
||||||
|
{supplier.type === "WHOLESALE" ? "Поставщик" : supplier.type}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
{supplier.address && (
|
||||||
|
<p className="text-white/50 text-xs line-clamp-1">{supplier.address}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* БЛОК 2: ТОВАРЫ - зависимый блок согласно rules1.md 19.2.1 */}
|
||||||
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl flex-1 min-h-0 flex flex-col">
|
||||||
|
<div className="p-6 border-b border-white/10 flex-shrink-0">
|
||||||
|
<div className="flex items-center justify-between gap-6">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="p-2 bg-blue-400/10 rounded-lg border border-blue-400/20">
|
||||||
|
<Package className="h-6 w-6 text-blue-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-semibold text-white">
|
||||||
|
{selectedSupplier ? `Товары ${selectedSupplier.name || selectedSupplier.fullName}` : "Каталог товаров"}
|
||||||
|
</h3>
|
||||||
|
{selectedSupplier && (
|
||||||
|
<p className="text-white/60 text-sm mt-1">
|
||||||
|
Выберите товары для добавления в корзину
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{selectedSupplier && (
|
||||||
|
<div className="flex-1 max-w-sm">
|
||||||
|
<div className="relative">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-white/40 h-4 w-4" />
|
||||||
|
<Input
|
||||||
|
placeholder="Поиск товаров..."
|
||||||
|
value={productSearchQuery}
|
||||||
|
onChange={(e) => setProductSearchQuery(e.target.value)}
|
||||||
|
className="glass-input text-white placeholder:text-white/50 pl-10 h-10 transition-all duration-200 focus-visible:ring-ring/50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-y-auto p-6">
|
||||||
|
{!selectedSupplier ? (
|
||||||
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<div className="text-center space-y-4">
|
||||||
|
<div className="w-24 h-24 mx-auto bg-blue-400/5 rounded-full flex items-center justify-center">
|
||||||
|
<Building2 className="h-12 w-12 text-blue-400/50" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-xl font-medium text-white mb-2">Выберите поставщика</h4>
|
||||||
|
<p className="text-white/60 max-w-sm mx-auto">
|
||||||
|
Для просмотра каталога товаров сначала выберите поставщика из списка выше
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : productsError ? (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<div className="w-16 h-16 mx-auto bg-red-500/10 rounded-full flex items-center justify-center mb-4">
|
||||||
|
<AlertCircle className="h-8 w-8 text-red-400" />
|
||||||
|
</div>
|
||||||
|
<h4 className="text-xl font-medium text-white mb-2">Ошибка загрузки товаров</h4>
|
||||||
|
<p className="text-red-400 text-sm">
|
||||||
|
{productsError.message || "Произошла ошибка при загрузке каталога"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : productsLoading ? (
|
||||||
|
<div className="flex items-center justify-center py-12">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-400"></div>
|
||||||
|
<span className="ml-3 text-white/70">Загрузка товаров...</span>
|
||||||
|
</div>
|
||||||
|
) : products.length === 0 ? (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<div className="w-16 h-16 mx-auto bg-white/5 rounded-full flex items-center justify-center mb-4">
|
||||||
|
<Package className="h-8 w-8 text-white/40" />
|
||||||
|
</div>
|
||||||
|
<h4 className="text-xl font-medium text-white mb-2">
|
||||||
|
{productSearchQuery ? "Товары не найдены" : "Каталог пуст"}
|
||||||
|
</h4>
|
||||||
|
<p className="text-white/60">
|
||||||
|
{productSearchQuery
|
||||||
|
? "Попробуйте изменить поисковый запрос"
|
||||||
|
: "У выбранного поставщика нет товаров"
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{products.map((product: GoodsProduct) => (
|
||||||
|
<div
|
||||||
|
key={product.id}
|
||||||
|
className="glass-card hover:border-white/20 hover:bg-white/10 hover:scale-105 hover:shadow-xl hover:shadow-blue-500/20 transition-all duration-200 cursor-pointer group"
|
||||||
|
>
|
||||||
|
<div className="p-5 space-y-4">
|
||||||
|
{product.mainImage && (
|
||||||
|
<div className="w-full h-40 rounded-lg overflow-hidden bg-white/5">
|
||||||
|
<Image
|
||||||
|
src={product.mainImage}
|
||||||
|
alt={product.name}
|
||||||
|
width={280}
|
||||||
|
height={160}
|
||||||
|
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-300"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white font-semibold text-base line-clamp-2 group-hover:text-white transition-colors">
|
||||||
|
{product.name}
|
||||||
|
</h4>
|
||||||
|
<p className="text-white/60 text-sm font-mono mt-1">Артикул: {product.article}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{product.category && (
|
||||||
|
<Badge className="bg-blue-500/20 text-blue-300 border border-blue-500/30 text-xs font-medium">
|
||||||
|
{product.category.name}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-white font-bold text-lg">
|
||||||
|
{product.price.toLocaleString('ru-RU')} ₽
|
||||||
|
</p>
|
||||||
|
<p className="text-white/60 text-xs">за единицу</p>
|
||||||
|
</div>
|
||||||
|
{product.quantity !== undefined && (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<div className={`w-2 h-2 rounded-full ${
|
||||||
|
product.quantity > 0 ? 'bg-green-400' : 'bg-red-400'
|
||||||
|
}`}></div>
|
||||||
|
<p className={`text-xs font-medium ${
|
||||||
|
product.quantity > 0 ? 'text-green-400' : 'text-red-400'
|
||||||
|
}`}>
|
||||||
|
{product.quantity > 0
|
||||||
|
? `Доступно: ${product.quantity}`
|
||||||
|
: 'Нет в наличии'
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Поле количества с кнопками +/- согласно rules2.md 13.3 */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => updateProductQuantity(product.id, -1)}
|
||||||
|
className="h-8 w-8 p-0 bg-white/5 border-white/20 hover:bg-white/10 hover:border-white/30 text-white"
|
||||||
|
disabled={!getProductQuantity(product.id) || getProductQuantity(product.id) <= 0}
|
||||||
|
>
|
||||||
|
<Minus className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max={product.quantity}
|
||||||
|
value={getProductQuantity(product.id) || ""}
|
||||||
|
onChange={(e) => setProductQuantity(product.id, parseInt(e.target.value) || 0)}
|
||||||
|
className="h-8 w-20 text-center bg-white/5 border-white/20 text-white placeholder:text-white/40 focus:border-white/40"
|
||||||
|
placeholder="00000"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => updateProductQuantity(product.id, 1)}
|
||||||
|
className="h-8 w-8 p-0 bg-white/5 border-white/20 hover:bg-white/10 hover:border-white/30 text-white"
|
||||||
|
disabled={product.quantity === 0 || getProductQuantity(product.id) >= product.quantity}
|
||||||
|
>
|
||||||
|
<Plus className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={() => addToCart(product)}
|
||||||
|
className="w-full bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white border border-green-500/30 hover:border-green-400/50 transition-all duration-200 h-8 text-xs"
|
||||||
|
disabled={product.quantity === 0 || !getProductQuantity(product.id)}
|
||||||
|
>
|
||||||
|
<ShoppingCart className="h-3 w-3 mr-1" />
|
||||||
|
В корзину
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* БЛОК 3: КОРЗИНА - правый блок согласно rules1.md 19.2.1 */}
|
||||||
|
<div className="w-96 flex-shrink-0 flex flex-col min-h-0">
|
||||||
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl flex-1 flex flex-col min-h-0">
|
||||||
|
|
||||||
|
{/* ЗАГОЛОВОК И СТАТИСТИКА */}
|
||||||
|
<div className="p-4 border-b border-white/10 flex-shrink-0">
|
||||||
|
<div className="flex items-center gap-3 mb-4">
|
||||||
|
<div className="p-2 bg-purple-400/10 rounded-lg border border-purple-400/20">
|
||||||
|
<ShoppingCart className="h-5 w-5 text-purple-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold text-white">Корзина и настройки поставки</h3>
|
||||||
|
<p className="text-white/60 text-xs mt-1">
|
||||||
|
Управление заказом и параметрами доставки
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* СТАТИСТИКА ПОСТАВКИ */}
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<div className="bg-white/5 border border-white/10 rounded-lg p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-white/60">Поставщиков</p>
|
||||||
|
<p className="text-lg font-semibold text-white">{selectedSupplier ? 1 : 0}</p>
|
||||||
|
</div>
|
||||||
|
<Building2 className="h-4 w-4 text-green-400" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/5 border border-white/10 rounded-lg p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-white/60">Товаров</p>
|
||||||
|
<p className="text-lg font-semibold text-white">{selectedGoods.length}</p>
|
||||||
|
</div>
|
||||||
|
<Package className="h-4 w-4 text-blue-400" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/5 border border-white/10 rounded-lg p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-white/60">Количество</p>
|
||||||
|
<p className="text-lg font-semibold text-white">{totalQuantity} шт</p>
|
||||||
|
</div>
|
||||||
|
<Box className="h-4 w-4 text-orange-400" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/5 border border-white/10 rounded-lg p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-white/60">Сумма</p>
|
||||||
|
<p className="text-lg font-semibold text-white">{totalAmount.toLocaleString('ru-RU')} ₽</p>
|
||||||
|
</div>
|
||||||
|
<DollarSign className="h-4 w-4 text-purple-400" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* НАСТРОЙКИ ПОСТАВКИ */}
|
||||||
|
<div className="p-4 border-b border-white/10 flex-shrink-0">
|
||||||
|
<h4 className="text-sm font-semibold text-white mb-3 flex items-center gap-2">
|
||||||
|
<Settings className="h-4 w-4 text-blue-400" />
|
||||||
|
Настройки поставки
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
{/* Выбор фулфилмент-центра */}
|
||||||
|
<div>
|
||||||
|
<label className="text-white/70 text-xs font-medium mb-2 flex items-center gap-2">
|
||||||
|
<Building2 className="h-3 w-3 text-green-400" />
|
||||||
|
Фулфилмент-центр *
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={selectedFulfillment}
|
||||||
|
onChange={(e) => setSelectedFulfillment(e.target.value)}
|
||||||
|
className="w-full bg-white/5 border-white/10 text-white h-8 text-sm rounded-lg hover:border-white/30 focus:border-green-400/50 transition-all duration-200"
|
||||||
|
>
|
||||||
|
<option value="" className="bg-gray-800 text-white">Выберите фулфилмент-центр</option>
|
||||||
|
{fulfillmentCenters.map((center) => (
|
||||||
|
<option key={center.id} value={center.id} className="bg-gray-800 text-white">
|
||||||
|
{center.name} - {center.address}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Дата поставки */}
|
||||||
|
<div>
|
||||||
|
<label className="text-white/70 text-xs font-medium mb-2 flex items-center gap-2">
|
||||||
|
<Calendar className="h-3 w-3 text-blue-400" />
|
||||||
|
Желаемая дата поставки *
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<Calendar className="absolute left-3 top-1/2 transform -translate-y-1/2 text-white/40 h-3 w-3 z-10" />
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
value={deliveryDate}
|
||||||
|
onChange={(e) => setDeliveryDate(e.target.value)}
|
||||||
|
min={minDateString}
|
||||||
|
max={maxDateString}
|
||||||
|
className="bg-white/5 border-white/10 text-white pl-9 h-8 text-sm hover:border-white/30 focus:border-blue-400/50 transition-all duration-200"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Выбор логистики */}
|
||||||
|
<div>
|
||||||
|
<label className="text-white/70 text-xs font-medium mb-2 flex items-center gap-2">
|
||||||
|
<Truck className="h-3 w-3 text-orange-400" />
|
||||||
|
Логистическая компания
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={selectedLogistics}
|
||||||
|
onChange={(e) => setSelectedLogistics(e.target.value)}
|
||||||
|
className="w-full bg-white/5 border-white/10 text-white h-8 text-sm rounded-lg hover:border-white/30 focus:border-orange-400/50 transition-all duration-200"
|
||||||
|
>
|
||||||
|
<option value="auto" className="bg-gray-800 text-white">Автоматический выбор</option>
|
||||||
|
{logisticsCompanies.map((company) => (
|
||||||
|
<option key={company.id} value={company.id} className="bg-gray-800 text-white">
|
||||||
|
{company.name} (~{company.estimatedCost} ₽, {company.deliveryDays} дн.)
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ТОВАРЫ В КОРЗИНЕ */}
|
||||||
|
<div className="flex-1 overflow-y-auto p-4">
|
||||||
|
{selectedGoods.length === 0 ? (
|
||||||
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<div className="text-center space-y-3">
|
||||||
|
<div className="w-16 h-16 mx-auto bg-purple-400/5 rounded-full flex items-center justify-center">
|
||||||
|
<ShoppingCart className="h-8 w-8 text-purple-400/50" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-base font-medium text-white mb-2">Корзина пуста</h4>
|
||||||
|
<p className="text-white/60 text-sm">
|
||||||
|
Добавьте товары из каталога поставщика
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{selectedGoods.map((item) => (
|
||||||
|
<div key={item.id} className="glass-card hover:border-white/20 transition-all duration-200 group">
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="flex items-start justify-between mb-3">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h4 className="text-white font-semibold text-base truncate group-hover:text-white transition-colors">
|
||||||
|
{item.name}
|
||||||
|
</h4>
|
||||||
|
<p className="text-white/60 text-sm font-mono mt-1">Артикул: {item.sku}</p>
|
||||||
|
{item.category && (
|
||||||
|
<Badge className="bg-blue-500/20 text-blue-300 border border-blue-500/30 text-xs font-medium mt-2">
|
||||||
|
{item.category}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => removeFromCart(item.id)}
|
||||||
|
className="text-red-400 hover:text-red-300 hover:bg-red-500/20 border border-transparent hover:border-red-500/30 p-2 transition-all duration-200"
|
||||||
|
>
|
||||||
|
<Minus className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-white/70 text-xs font-medium">Количество:</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
const newQuantity = Math.max(1, item.selectedQuantity - 1);
|
||||||
|
addToCart(
|
||||||
|
{
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
sku: item.sku,
|
||||||
|
price: item.price,
|
||||||
|
category: { name: item.category || '' },
|
||||||
|
images: [],
|
||||||
|
organization: { id: item.supplierId, name: item.supplierName },
|
||||||
|
unit: item.unit,
|
||||||
|
} as GoodsProduct,
|
||||||
|
newQuantity
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className="h-7 w-7 p-0 border border-white/20 text-white/70 hover:text-white hover:bg-white/10 transition-all duration-200"
|
||||||
|
>
|
||||||
|
<Minus className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
value={item.selectedQuantity}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newQuantity = parseInt(e.target.value) || 1;
|
||||||
|
addToCart(
|
||||||
|
{
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
sku: item.sku,
|
||||||
|
price: item.price,
|
||||||
|
category: { name: item.category || '' },
|
||||||
|
images: [],
|
||||||
|
organization: { id: item.supplierId, name: item.supplierName },
|
||||||
|
unit: item.unit,
|
||||||
|
} as GoodsProduct,
|
||||||
|
newQuantity
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className="glass-input text-white w-16 h-7 text-center text-xs font-medium"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
const newQuantity = item.selectedQuantity + 1;
|
||||||
|
addToCart(
|
||||||
|
{
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
sku: item.sku,
|
||||||
|
price: item.price,
|
||||||
|
category: { name: item.category || '' },
|
||||||
|
images: [],
|
||||||
|
organization: { id: item.supplierId, name: item.supplierName },
|
||||||
|
unit: item.unit,
|
||||||
|
} as GoodsProduct,
|
||||||
|
newQuantity
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className="h-7 w-7 p-0 border border-white/20 text-white/70 hover:text-white hover:bg-white/10 transition-all duration-200"
|
||||||
|
>
|
||||||
|
<Plus className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-white/70 text-xs font-medium">Цена за {item.unit || 'шт'}:</span>
|
||||||
|
<span className="text-white text-xs font-semibold">{item.price.toLocaleString('ru-RU')} ₽</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between p-2 bg-white/5 rounded-lg border border-white/10">
|
||||||
|
<span className="text-white/80 text-sm font-medium">Сумма:</span>
|
||||||
|
<span className="text-green-400 text-base font-bold">
|
||||||
|
{(item.price * item.selectedQuantity).toLocaleString('ru-RU')} ₽
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Дополнительная информация */}
|
||||||
|
{(item.completeness || item.recipe || item.specialRequirements || item.parameters) && (
|
||||||
|
<div className="mt-2 pt-2 border-t border-white/10 space-y-1">
|
||||||
|
{item.completeness && (
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<FileText className="h-3 w-3 text-blue-400 flex-shrink-0 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<span className="text-blue-300 text-xs font-medium">Комплектность: </span>
|
||||||
|
<span className="text-white/80 text-xs">{item.completeness}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.recipe && (
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<Settings className="h-3 w-3 text-purple-400 flex-shrink-0 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<span className="text-purple-300 text-xs font-medium">Рецептура: </span>
|
||||||
|
<span className="text-white/80 text-xs">{item.recipe}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.specialRequirements && (
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<AlertCircle className="h-3 w-3 text-yellow-400 flex-shrink-0 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<span className="text-yellow-300 text-xs font-medium">Требования: </span>
|
||||||
|
<span className="text-white/80 text-xs">{item.specialRequirements}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.parameters && item.parameters.length > 0 && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Settings className="h-3 w-3 text-green-400" />
|
||||||
|
<span className="text-green-300 text-xs font-medium">Параметры:</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{item.parameters.map((param, idx) => (
|
||||||
|
<Badge key={idx} className="bg-green-500/10 text-green-300 border border-green-500/20 text-xs">
|
||||||
|
{param.name}: {param.value}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ИТОГИ И КНОПКА СОЗДАНИЯ */}
|
||||||
|
<div className="p-4 border-t border-white/10 space-y-4 flex-shrink-0">
|
||||||
|
{/* Детальные итоги */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-white/70 text-xs font-medium">Товаров:</span>
|
||||||
|
<span className="text-white text-xs font-semibold">{totalQuantity} шт</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-white/70 text-xs font-medium">Стоимость товаров:</span>
|
||||||
|
<span className="text-white text-xs font-semibold">{totalGoodsAmount.toLocaleString('ru-RU')} ₽</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-purple-300 text-xs font-medium">Фулфилмент (8%):</span>
|
||||||
|
<span className="text-purple-300 text-xs font-semibold">{fulfillmentFee.toLocaleString('ru-RU')} ₽</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-orange-300 text-xs font-medium">Логистика:</span>
|
||||||
|
<span className="text-orange-300 text-xs font-semibold">
|
||||||
|
{selectedLogistics === "auto" ? "~" : ""}{logisticsCost.toLocaleString('ru-RU')} ₽
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center p-2 bg-gradient-to-r from-green-500/10 to-emerald-500/10 border border-green-500/20 rounded-lg">
|
||||||
|
<span className="text-white text-sm font-semibold">Итого к оплате:</span>
|
||||||
|
<span className="text-green-400 text-lg font-bold">{totalAmount.toLocaleString('ru-RU')} ₽</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопка создания поставки */}
|
||||||
|
<Button
|
||||||
|
onClick={handleCreateSupply}
|
||||||
|
disabled={!isFormValid || isCreatingSupply}
|
||||||
|
className="w-full bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white font-semibold py-3 text-sm border border-green-500/30 hover:border-green-400/50 transition-all duration-300 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{isCreatingSupply ? (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||||
|
<span>Создание поставки...</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<FileText className="h-4 w-4" />
|
||||||
|
<span>Продолжить оформление</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* Сообщения об ошибках валидации */}
|
||||||
|
{!isFormValid && (
|
||||||
|
<div className="bg-red-500/10 border border-red-500/20 rounded-lg p-2 mt-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<AlertCircle className="h-3 w-3 text-red-400 flex-shrink-0" />
|
||||||
|
<p className="text-red-300 text-xs font-medium">
|
||||||
|
{!selectedSupplier && "Выберите поставщика"}
|
||||||
|
{selectedSupplier && selectedGoods.length === 0 && "Добавьте товары в корзину"}
|
||||||
|
{selectedSupplier && selectedGoods.length > 0 && !deliveryDate && "Укажите дату поставки"}
|
||||||
|
{selectedSupplier && selectedGoods.length > 0 && deliveryDate && !selectedFulfillment && "Выберите фулфилмент-центр"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{/* Модальное окно для детального добавления товара */}
|
||||||
|
<AddGoodsModal
|
||||||
|
product={selectedProductForModal}
|
||||||
|
isOpen={isModalOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
setSelectedProductForModal(null);
|
||||||
|
}}
|
||||||
|
onAdd={addToCartFromModal}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -2,40 +2,48 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
import { FulfillmentGoodsTab } from "./fulfillment-goods-tab";
|
import { GoodsSuppliesTable } from "../goods-supplies-table";
|
||||||
import { RealSupplyOrdersTab } from "./real-supply-orders-tab";
|
|
||||||
import { SellerSupplyOrdersTab } from "./seller-supply-orders-tab";
|
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
|
import { Package } from "lucide-react";
|
||||||
|
|
||||||
interface AllSuppliesTabProps {
|
interface AllSuppliesTabProps {
|
||||||
pendingSupplyOrders?: number;
|
pendingSupplyOrders?: number;
|
||||||
|
goodsSupplies?: any[]; // Данные поставок товаров из обоих источников
|
||||||
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AllSuppliesTab({
|
export function AllSuppliesTab({
|
||||||
pendingSupplyOrders = 0,
|
pendingSupplyOrders = 0,
|
||||||
|
goodsSupplies = [],
|
||||||
|
loading = false
|
||||||
}: AllSuppliesTabProps) {
|
}: AllSuppliesTabProps) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
// Определяем тип организации для выбора правильного компонента
|
// ✅ ЕДИНАЯ ТАБЛИЦА ПОСТАВОК ТОВАРОВ согласно rules2.md 9.5.3
|
||||||
const isWholesale = user?.organization?.type === "WHOLESALE";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full overflow-hidden space-y-4">
|
<div className="h-full">
|
||||||
{/* Секция товаров */}
|
{goodsSupplies.length === 0 && !loading ? (
|
||||||
<Card className="bg-white/10 backdrop-blur border-white/20 p-4">
|
<Card className="bg-white/10 backdrop-blur border-white/20 p-8">
|
||||||
<h3 className="text-white font-semibold text-lg mb-3">Товары</h3>
|
<div className="text-center">
|
||||||
<div className="h-64 overflow-hidden">
|
<Package className="h-16 w-16 mx-auto mb-4 text-white/30" />
|
||||||
<FulfillmentGoodsTab />
|
<h3 className="text-xl font-semibold text-white mb-2">
|
||||||
</div>
|
Поставки товаров
|
||||||
</Card>
|
</h3>
|
||||||
|
<p className="text-white/60 mb-4">
|
||||||
{/* Секция расходников */}
|
Здесь отображаются все поставки товаров, созданные через карточки и у поставщиков
|
||||||
<Card className="bg-white/10 backdrop-blur border-white/20 p-4">
|
</p>
|
||||||
<h3 className="text-white font-semibold text-lg mb-3">Расходники</h3>
|
<div className="text-sm text-white/50">
|
||||||
<div className="h-64 overflow-hidden">
|
<p>• Карточки - импорт через WB API</p>
|
||||||
{isWholesale ? <RealSupplyOrdersTab /> : <SellerSupplyOrdersTab />}
|
<p>• Поставщики - прямой заказ с рецептурой</p>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
</Card>
|
||||||
|
) : (
|
||||||
|
<GoodsSuppliesTable
|
||||||
|
supplies={goodsSupplies}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
735
src/components/supplies/goods-supplies-table.tsx
Normal file
735
src/components/supplies/goods-supplies-table.tsx
Normal file
@ -0,0 +1,735 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { Card } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Package,
|
||||||
|
Building2,
|
||||||
|
Calendar,
|
||||||
|
DollarSign,
|
||||||
|
Search,
|
||||||
|
Filter,
|
||||||
|
ChevronDown,
|
||||||
|
ChevronRight,
|
||||||
|
Smartphone,
|
||||||
|
Eye,
|
||||||
|
MoreHorizontal,
|
||||||
|
MapPin,
|
||||||
|
TrendingUp,
|
||||||
|
AlertTriangle,
|
||||||
|
Warehouse,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { formatCurrency } from "@/lib/utils";
|
||||||
|
|
||||||
|
// Простые компоненты таблицы
|
||||||
|
const Table = ({ children, ...props }: any) => (
|
||||||
|
<div className="w-full overflow-auto" {...props}>
|
||||||
|
<table className="w-full">{children}</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const TableHeader = ({ children, ...props }: any) => <thead {...props}>{children}</thead>;
|
||||||
|
const TableBody = ({ children, ...props }: any) => <tbody {...props}>{children}</tbody>;
|
||||||
|
const TableRow = ({ children, className, ...props }: any) => (
|
||||||
|
<tr className={className} {...props}>{children}</tr>
|
||||||
|
);
|
||||||
|
const TableHead = ({ children, className, ...props }: any) => (
|
||||||
|
<th className={`px-4 py-3 text-left font-medium ${className}`} {...props}>{children}</th>
|
||||||
|
);
|
||||||
|
const TableCell = ({ children, className, ...props }: any) => (
|
||||||
|
<td className={`px-4 py-3 ${className}`} {...props}>{children}</td>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Расширенные типы данных для детальной структуры поставок
|
||||||
|
interface ProductParameter {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
unit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoodsSupplyProduct {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
sku: string;
|
||||||
|
category: string;
|
||||||
|
plannedQty: number;
|
||||||
|
actualQty: number;
|
||||||
|
defectQty: number;
|
||||||
|
productPrice: number;
|
||||||
|
parameters: ProductParameter[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoodsSupplyWholesaler {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
inn: string;
|
||||||
|
contact: string;
|
||||||
|
address: string;
|
||||||
|
products: GoodsSupplyProduct[];
|
||||||
|
totalAmount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoodsSupplyRoute {
|
||||||
|
id: string;
|
||||||
|
from: string;
|
||||||
|
fromAddress: string;
|
||||||
|
to: string;
|
||||||
|
toAddress: string;
|
||||||
|
wholesalers: GoodsSupplyWholesaler[];
|
||||||
|
totalProductPrice: number;
|
||||||
|
fulfillmentServicePrice: number;
|
||||||
|
logisticsPrice: number;
|
||||||
|
totalAmount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Основной интерфейс поставки товаров согласно rules2.md 9.5.4
|
||||||
|
interface GoodsSupply {
|
||||||
|
id: string;
|
||||||
|
number: string;
|
||||||
|
creationMethod: 'cards' | 'suppliers'; // 📱 карточки / 🏢 поставщик
|
||||||
|
deliveryDate: string;
|
||||||
|
createdAt: string;
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
// Агрегированные данные
|
||||||
|
plannedTotal: number;
|
||||||
|
actualTotal: number;
|
||||||
|
defectTotal: number;
|
||||||
|
totalProductPrice: number;
|
||||||
|
totalFulfillmentPrice: number;
|
||||||
|
totalLogisticsPrice: number;
|
||||||
|
grandTotal: number;
|
||||||
|
|
||||||
|
// Детальная структура
|
||||||
|
routes: GoodsSupplyRoute[];
|
||||||
|
|
||||||
|
// Для обратной совместимости
|
||||||
|
goodsCount?: number;
|
||||||
|
totalAmount?: number;
|
||||||
|
supplier?: string;
|
||||||
|
items?: GoodsSupplyItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Простой интерфейс товара для базовой детализации
|
||||||
|
interface GoodsSupplyItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
quantity: number;
|
||||||
|
price: number;
|
||||||
|
category?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoodsSuppliesTableProps {
|
||||||
|
supplies?: GoodsSupply[];
|
||||||
|
loading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Компонент для иконки способа создания
|
||||||
|
function CreationMethodIcon({ method }: { method: 'cards' | 'suppliers' }) {
|
||||||
|
if (method === 'cards') {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1 text-blue-400">
|
||||||
|
<Smartphone className="h-3 w-3" />
|
||||||
|
<span className="text-xs hidden sm:inline">Карточки</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1 text-green-400">
|
||||||
|
<Building2 className="h-3 w-3" />
|
||||||
|
<span className="text-xs hidden sm:inline">Поставщик</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Компонент для статуса поставки
|
||||||
|
function StatusBadge({ status }: { status: string }) {
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status.toLowerCase()) {
|
||||||
|
case 'pending': return 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30';
|
||||||
|
case 'supplier_approved': return 'bg-blue-500/20 text-blue-300 border-blue-500/30';
|
||||||
|
case 'confirmed': return 'bg-purple-500/20 text-purple-300 border-purple-500/30';
|
||||||
|
case 'shipped': return 'bg-orange-500/20 text-orange-300 border-orange-500/30';
|
||||||
|
case 'in_transit': return 'bg-indigo-500/20 text-indigo-300 border-indigo-500/30';
|
||||||
|
case 'delivered': return 'bg-green-500/20 text-green-300 border-green-500/30';
|
||||||
|
default: return 'bg-gray-500/20 text-gray-300 border-gray-500/30';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
switch (status.toLowerCase()) {
|
||||||
|
case 'pending': return 'Ожидает';
|
||||||
|
case 'supplier_approved': return 'Одобрена';
|
||||||
|
case 'confirmed': return 'Подтверждена';
|
||||||
|
case 'shipped': return 'Отгружена';
|
||||||
|
case 'in_transit': return 'В пути';
|
||||||
|
case 'delivered': return 'Доставлена';
|
||||||
|
default: return status;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge className={`${getStatusColor(status)} border text-xs`}>
|
||||||
|
{getStatusText(status)}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GoodsSuppliesTable({ supplies = [], loading = false }: GoodsSuppliesTableProps) {
|
||||||
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
const [selectedMethod, setSelectedMethod] = useState<string>("all");
|
||||||
|
const [selectedStatus, setSelectedStatus] = useState<string>("all");
|
||||||
|
const [expandedSupplies, setExpandedSupplies] = useState<Set<string>>(new Set());
|
||||||
|
const [expandedRoutes, setExpandedRoutes] = useState<Set<string>>(new Set());
|
||||||
|
const [expandedWholesalers, setExpandedWholesalers] = useState<Set<string>>(new Set());
|
||||||
|
const [expandedProducts, setExpandedProducts] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
|
// Фильтрация согласно rules2.md 9.5.4 с поддержкой расширенной структуры
|
||||||
|
const filteredSupplies = supplies.filter(supply => {
|
||||||
|
const matchesSearch = supply.number.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
(supply.supplier && supply.supplier.toLowerCase().includes(searchQuery.toLowerCase())) ||
|
||||||
|
(supply.routes && supply.routes.some(route =>
|
||||||
|
route.wholesalers.some(wholesaler =>
|
||||||
|
wholesaler.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
)
|
||||||
|
));
|
||||||
|
const matchesMethod = selectedMethod === "all" || supply.creationMethod === selectedMethod;
|
||||||
|
const matchesStatus = selectedStatus === "all" || supply.status === selectedStatus;
|
||||||
|
|
||||||
|
return matchesSearch && matchesMethod && matchesStatus;
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleSupplyExpansion = (supplyId: string) => {
|
||||||
|
const newExpanded = new Set(expandedSupplies);
|
||||||
|
if (newExpanded.has(supplyId)) {
|
||||||
|
newExpanded.delete(supplyId);
|
||||||
|
} else {
|
||||||
|
newExpanded.add(supplyId);
|
||||||
|
}
|
||||||
|
setExpandedSupplies(newExpanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleRouteExpansion = (routeId: string) => {
|
||||||
|
const newExpanded = new Set(expandedRoutes);
|
||||||
|
if (newExpanded.has(routeId)) {
|
||||||
|
newExpanded.delete(routeId);
|
||||||
|
} else {
|
||||||
|
newExpanded.add(routeId);
|
||||||
|
}
|
||||||
|
setExpandedRoutes(newExpanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleWholesalerExpansion = (wholesalerId: string) => {
|
||||||
|
const newExpanded = new Set(expandedWholesalers);
|
||||||
|
if (newExpanded.has(wholesalerId)) {
|
||||||
|
newExpanded.delete(wholesalerId);
|
||||||
|
} else {
|
||||||
|
newExpanded.add(wholesalerId);
|
||||||
|
}
|
||||||
|
setExpandedWholesalers(newExpanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleProductExpansion = (productId: string) => {
|
||||||
|
const newExpanded = new Set(expandedProducts);
|
||||||
|
if (newExpanded.has(productId)) {
|
||||||
|
newExpanded.delete(productId);
|
||||||
|
} else {
|
||||||
|
newExpanded.add(productId);
|
||||||
|
}
|
||||||
|
setExpandedProducts(newExpanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Вспомогательные функции
|
||||||
|
const getStatusBadge = (status: string) => {
|
||||||
|
const statusMap = {
|
||||||
|
pending: { label: "Ожидает", color: "bg-yellow-500/20 text-yellow-300 border-yellow-500/30" },
|
||||||
|
supplier_approved: { label: "Одобрена", color: "bg-blue-500/20 text-blue-300 border-blue-500/30" },
|
||||||
|
confirmed: { label: "Подтверждена", color: "bg-purple-500/20 text-purple-300 border-purple-500/30" },
|
||||||
|
shipped: { label: "Отгружена", color: "bg-orange-500/20 text-orange-300 border-orange-500/30" },
|
||||||
|
in_transit: { label: "В пути", color: "bg-indigo-500/20 text-indigo-300 border-indigo-500/30" },
|
||||||
|
delivered: { label: "Доставлена", color: "bg-green-500/20 text-green-300 border-green-500/30" },
|
||||||
|
planned: { label: "Запланирована", color: "bg-blue-500/20 text-blue-300 border-blue-500/30" },
|
||||||
|
completed: { label: "Завершена", color: "bg-purple-500/20 text-purple-300 border-purple-500/30" },
|
||||||
|
};
|
||||||
|
const statusInfo = statusMap[status as keyof typeof statusMap] || { label: status, color: "bg-gray-500/20 text-gray-300 border-gray-500/30" };
|
||||||
|
return <Badge className={`${statusInfo.color} border`}>{statusInfo.label}</Badge>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEfficiencyBadge = (planned: number, actual: number, defect: number) => {
|
||||||
|
const efficiency = ((actual - defect) / planned) * 100;
|
||||||
|
if (efficiency >= 95) {
|
||||||
|
return <Badge className="bg-green-500/20 text-green-300 border-green-500/30 border">Отлично</Badge>;
|
||||||
|
} else if (efficiency >= 90) {
|
||||||
|
return <Badge className="bg-yellow-500/20 text-yellow-300 border-yellow-500/30 border">Хорошо</Badge>;
|
||||||
|
} else {
|
||||||
|
return <Badge className="bg-red-500/20 text-red-300 border-red-500/30 border">Проблемы</Badge>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateProductTotal = (product: GoodsSupplyProduct) => {
|
||||||
|
return product.actualQty * product.productPrice;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDate = (dateString: string) => {
|
||||||
|
return new Date(dateString).toLocaleDateString("ru-RU", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
year: "numeric",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Card className="bg-white/10 backdrop-blur border-white/20 p-6">
|
||||||
|
<div className="animate-pulse space-y-4">
|
||||||
|
<div className="h-4 bg-white/10 rounded w-1/4"></div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{[...Array(5)].map((_, i) => (
|
||||||
|
<div key={i} className="h-12 bg-white/5 rounded"></div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Фильтры */}
|
||||||
|
<Card className="bg-white/5 backdrop-blur border-white/10 p-4">
|
||||||
|
<div className="flex flex-col sm:flex-row gap-4">
|
||||||
|
{/* Поиск */}
|
||||||
|
<div className="relative flex-1">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-white/40 h-4 w-4" />
|
||||||
|
<Input
|
||||||
|
placeholder="Поиск по номеру или поставщику..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
className="bg-white/10 border-white/20 text-white placeholder-white/50 pl-10"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Фильтр по способу создания */}
|
||||||
|
<select
|
||||||
|
value={selectedMethod}
|
||||||
|
onChange={(e) => setSelectedMethod(e.target.value)}
|
||||||
|
className="bg-white/10 border border-white/20 text-white rounded-md px-3 py-2 text-sm"
|
||||||
|
>
|
||||||
|
<option value="all">Все способы</option>
|
||||||
|
<option value="cards">Карточки</option>
|
||||||
|
<option value="suppliers">Поставщики</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
{/* Фильтр по статусу */}
|
||||||
|
<select
|
||||||
|
value={selectedStatus}
|
||||||
|
onChange={(e) => setSelectedStatus(e.target.value)}
|
||||||
|
className="bg-white/10 border border-white/20 text-white rounded-md px-3 py-2 text-sm"
|
||||||
|
>
|
||||||
|
<option value="all">Все статусы</option>
|
||||||
|
<option value="pending">Ожидает</option>
|
||||||
|
<option value="supplier_approved">Одобрена</option>
|
||||||
|
<option value="confirmed">Подтверждена</option>
|
||||||
|
<option value="shipped">Отгружена</option>
|
||||||
|
<option value="in_transit">В пути</option>
|
||||||
|
<option value="delivered">Доставлена</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Таблица поставок согласно rules2.md 9.5.4 */}
|
||||||
|
<Card className="bg-white/10 backdrop-blur border-white/20 overflow-hidden">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="border-white/10 hover:bg-white/5">
|
||||||
|
<TableHead className="text-white/70">№</TableHead>
|
||||||
|
<TableHead className="text-white/70">
|
||||||
|
<span className="hidden sm:inline">Дата поставки</span>
|
||||||
|
<span className="sm:hidden">Поставка</span>
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-white/70 hidden lg:table-cell">Создана</TableHead>
|
||||||
|
<TableHead className="text-white/70">План</TableHead>
|
||||||
|
<TableHead className="text-white/70">Факт</TableHead>
|
||||||
|
<TableHead className="text-white/70">Брак</TableHead>
|
||||||
|
<TableHead className="text-white/70">
|
||||||
|
<span className="hidden md:inline">Цена товаров</span>
|
||||||
|
<span className="md:hidden">Цена</span>
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-white/70 hidden lg:table-cell">ФФ</TableHead>
|
||||||
|
<TableHead className="text-white/70 hidden lg:table-cell">Логистика</TableHead>
|
||||||
|
<TableHead className="text-white/70">Итого</TableHead>
|
||||||
|
<TableHead className="text-white/70">Статус</TableHead>
|
||||||
|
<TableHead className="text-white/70">Способ</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{filteredSupplies.length === 0 ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={12} className="text-center py-8 text-white/60">
|
||||||
|
{searchQuery || selectedMethod !== "all" || selectedStatus !== "all"
|
||||||
|
? "Поставки не найдены по заданным фильтрам"
|
||||||
|
: "Поставки товаров отсутствуют"
|
||||||
|
}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : (
|
||||||
|
filteredSupplies.map((supply) => {
|
||||||
|
const isSupplyExpanded = expandedSupplies.has(supply.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment key={supply.id}>
|
||||||
|
{/* Основная строка поставки */}
|
||||||
|
<TableRow
|
||||||
|
className="border-white/10 hover:bg-white/5 cursor-pointer transition-colors bg-purple-500/10"
|
||||||
|
onClick={() => toggleSupplyExpansion(supply.id)}
|
||||||
|
>
|
||||||
|
<TableCell className="text-white font-mono text-sm">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{isSupplyExpanded ? (
|
||||||
|
<ChevronDown className="h-4 w-4 text-white/40" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-4 w-4 text-white/40" />
|
||||||
|
)}
|
||||||
|
{supply.number}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
|
<Calendar className="h-3 w-3 text-white/40" />
|
||||||
|
<span className="text-white font-semibold text-sm">
|
||||||
|
{formatDate(supply.deliveryDate)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell">
|
||||||
|
<span className="text-white/80 text-sm">
|
||||||
|
{formatDate(supply.createdAt)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white font-semibold text-sm">
|
||||||
|
{supply.plannedTotal || supply.goodsCount || 0}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white font-semibold text-sm">
|
||||||
|
{supply.actualTotal || supply.goodsCount || 0}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className={`font-semibold text-sm ${
|
||||||
|
(supply.defectTotal || 0) > 0 ? "text-red-400" : "text-white"
|
||||||
|
}`}>
|
||||||
|
{supply.defectTotal || 0}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-green-400 font-semibold text-sm">
|
||||||
|
{formatCurrency(supply.totalProductPrice || supply.totalAmount || 0)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell">
|
||||||
|
<span className="text-blue-400 font-semibold text-sm">
|
||||||
|
{formatCurrency(supply.totalFulfillmentPrice || 0)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell">
|
||||||
|
<span className="text-purple-400 font-semibold text-sm">
|
||||||
|
{formatCurrency(supply.totalLogisticsPrice || 0)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
|
<DollarSign className="h-3 w-3 text-white/40" />
|
||||||
|
<span className="text-white font-bold text-sm">
|
||||||
|
{formatCurrency(supply.grandTotal || supply.totalAmount || 0)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{getStatusBadge(supply.status)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<CreationMethodIcon method={supply.creationMethod} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
{/* Развернутые уровни - маршруты, поставщики, товары */}
|
||||||
|
{isSupplyExpanded && supply.routes && supply.routes.map((route) => {
|
||||||
|
const isRouteExpanded = expandedRoutes.has(route.id);
|
||||||
|
return (
|
||||||
|
<React.Fragment key={route.id}>
|
||||||
|
<TableRow
|
||||||
|
className="border-white/10 hover:bg-white/5 cursor-pointer transition-colors bg-blue-500/10"
|
||||||
|
onClick={() => toggleRouteExpansion(route.id)}
|
||||||
|
>
|
||||||
|
<TableCell className="relative">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-1 h-1 rounded-full bg-blue-400 mr-1"></div>
|
||||||
|
<MapPin className="h-3 w-3 text-blue-400" />
|
||||||
|
<span className="text-white font-medium text-sm">Маршрут</span>
|
||||||
|
</div>
|
||||||
|
<div className="absolute left-0 top-0 w-0.5 h-full bg-blue-400/30"></div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell colSpan={1}>
|
||||||
|
<div className="text-white">
|
||||||
|
<div className="flex items-center space-x-2 mb-1">
|
||||||
|
<span className="font-medium text-sm">{route.from}</span>
|
||||||
|
<span className="text-white/60">→</span>
|
||||||
|
<span className="font-medium text-sm">{route.to}</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-white/60 hidden sm:block">
|
||||||
|
{route.fromAddress} → {route.toAddress}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell"></TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white/80 text-sm">
|
||||||
|
{route.wholesalers.reduce((sum, w) =>
|
||||||
|
sum + w.products.reduce((pSum, p) => pSum + p.plannedQty, 0), 0
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white/80 text-sm">
|
||||||
|
{route.wholesalers.reduce((sum, w) =>
|
||||||
|
sum + w.products.reduce((pSum, p) => pSum + p.actualQty, 0), 0
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white/80 text-sm">
|
||||||
|
{route.wholesalers.reduce((sum, w) =>
|
||||||
|
sum + w.products.reduce((pSum, p) => pSum + p.defectQty, 0), 0
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-green-400 font-medium text-sm">
|
||||||
|
{formatCurrency(route.totalProductPrice)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell">
|
||||||
|
<span className="text-blue-400 font-medium text-sm">
|
||||||
|
{formatCurrency(route.fulfillmentServicePrice)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell">
|
||||||
|
<span className="text-purple-400 font-medium text-sm">
|
||||||
|
{formatCurrency(route.logisticsPrice)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white font-semibold text-sm">
|
||||||
|
{formatCurrency(route.totalAmount)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell colSpan={2}></TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
{/* Поставщики в маршруте */}
|
||||||
|
{isRouteExpanded && route.wholesalers.map((wholesaler) => {
|
||||||
|
const isWholesalerExpanded = expandedWholesalers.has(wholesaler.id);
|
||||||
|
return (
|
||||||
|
<React.Fragment key={wholesaler.id}>
|
||||||
|
<TableRow
|
||||||
|
className="border-white/10 hover:bg-white/5 cursor-pointer transition-colors bg-green-500/10"
|
||||||
|
onClick={() => toggleWholesalerExpansion(wholesaler.id)}
|
||||||
|
>
|
||||||
|
<TableCell className="relative">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-1 h-1 rounded-full bg-green-400 mr-1"></div>
|
||||||
|
<div className="w-1 h-1 rounded-full bg-green-400 mr-1"></div>
|
||||||
|
<Building2 className="h-3 w-3 text-green-400" />
|
||||||
|
<span className="text-white font-medium text-sm">Поставщик</span>
|
||||||
|
</div>
|
||||||
|
<div className="absolute left-0 top-0 w-0.5 h-full bg-green-400/30"></div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell colSpan={1}>
|
||||||
|
<div className="text-white">
|
||||||
|
<div className="font-medium mb-1 text-sm">{wholesaler.name}</div>
|
||||||
|
<div className="text-xs text-white/60 mb-1 hidden sm:block">
|
||||||
|
ИНН: {wholesaler.inn}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-white/60 mb-1 hidden lg:block">
|
||||||
|
{wholesaler.address}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-white/60 hidden sm:block">
|
||||||
|
{wholesaler.contact}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell"></TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white/80 text-sm">
|
||||||
|
{wholesaler.products.reduce((sum, p) => sum + p.plannedQty, 0)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white/80 text-sm">
|
||||||
|
{wholesaler.products.reduce((sum, p) => sum + p.actualQty, 0)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white/80 text-sm">
|
||||||
|
{wholesaler.products.reduce((sum, p) => sum + p.defectQty, 0)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-green-400 font-medium text-sm">
|
||||||
|
{formatCurrency(wholesaler.products.reduce((sum, p) => sum + calculateProductTotal(p), 0))}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell" colSpan={2}></TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white font-semibold text-sm">
|
||||||
|
{formatCurrency(wholesaler.totalAmount)}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell colSpan={2}></TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
{/* Товары поставщика */}
|
||||||
|
{isWholesalerExpanded && wholesaler.products.map((product) => {
|
||||||
|
const isProductExpanded = expandedProducts.has(product.id);
|
||||||
|
return (
|
||||||
|
<React.Fragment key={product.id}>
|
||||||
|
<TableRow
|
||||||
|
className="border-white/10 hover:bg-white/5 cursor-pointer transition-colors bg-yellow-500/10"
|
||||||
|
onClick={() => toggleProductExpansion(product.id)}
|
||||||
|
>
|
||||||
|
<TableCell className="relative">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-1 h-1 rounded-full bg-yellow-400 mr-1"></div>
|
||||||
|
<div className="w-1 h-1 rounded-full bg-yellow-400 mr-1"></div>
|
||||||
|
<div className="w-1 h-1 rounded-full bg-yellow-400 mr-1"></div>
|
||||||
|
<Package className="h-3 w-3 text-yellow-400" />
|
||||||
|
<span className="text-white font-medium text-sm">Товар</span>
|
||||||
|
</div>
|
||||||
|
<div className="absolute left-0 top-0 w-0.5 h-full bg-yellow-400/30"></div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell colSpan={1}>
|
||||||
|
<div className="text-white">
|
||||||
|
<div className="font-medium mb-1 text-sm">{product.name}</div>
|
||||||
|
<div className="text-xs text-white/60 mb-1 hidden sm:block">
|
||||||
|
Артикул: {product.sku}
|
||||||
|
</div>
|
||||||
|
<Badge className="bg-gray-500/20 text-gray-300 border-gray-500/30 border text-xs hidden sm:inline-flex">
|
||||||
|
{product.category}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell"></TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white font-semibold text-sm">{product.plannedQty}</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white font-semibold text-sm">{product.actualQty}</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className={`font-semibold text-sm ${
|
||||||
|
product.defectQty > 0 ? "text-red-400" : "text-white"
|
||||||
|
}`}>
|
||||||
|
{product.defectQty}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="text-white">
|
||||||
|
<div className="font-medium text-sm">
|
||||||
|
{formatCurrency(calculateProductTotal(product))}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-white/60 hidden sm:block">
|
||||||
|
{formatCurrency(product.productPrice)} за шт.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="hidden lg:table-cell" colSpan={2}>
|
||||||
|
{getEfficiencyBadge(product.plannedQty, product.actualQty, product.defectQty)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<span className="text-white font-semibold text-sm">
|
||||||
|
{formatCurrency(calculateProductTotal(product))}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell colSpan={2}></TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
{/* Параметры товара */}
|
||||||
|
{isProductExpanded && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={12} className="p-0">
|
||||||
|
<div className="bg-white/5 border-t border-white/10">
|
||||||
|
<div className="p-4">
|
||||||
|
<h4 className="text-white font-medium mb-3 flex items-center space-x-2">
|
||||||
|
<span className="text-xs text-white/60">📋 Параметры товара:</span>
|
||||||
|
</h4>
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
{product.parameters.map((param) => (
|
||||||
|
<div key={param.id} className="bg-white/5 rounded-lg p-3">
|
||||||
|
<div className="text-white/80 text-xs font-medium mb-1">
|
||||||
|
{param.name}
|
||||||
|
</div>
|
||||||
|
<div className="text-white text-sm">
|
||||||
|
{param.value} {param.unit || ""}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* Базовая детализация для поставок без маршрутов */}
|
||||||
|
{isSupplyExpanded && supply.items && !supply.routes && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={12} className="bg-white/5 border-white/5">
|
||||||
|
<div className="p-4 space-y-4">
|
||||||
|
<h4 className="text-white font-medium">Детализация товаров:</h4>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
{supply.items.map((item) => (
|
||||||
|
<div key={item.id} className="flex justify-between items-center py-2 px-3 bg-white/5 rounded-lg">
|
||||||
|
<div className="flex-1">
|
||||||
|
<span className="text-white text-sm">{item.name}</span>
|
||||||
|
{item.category && (
|
||||||
|
<span className="text-white/60 text-xs ml-2">({item.category})</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 text-sm">
|
||||||
|
<span className="text-white/80">{item.quantity} шт</span>
|
||||||
|
<span className="text-white/80">{formatCurrency(item.price)}</span>
|
||||||
|
<span className="text-white font-medium">{formatCurrency(item.price * item.quantity)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
// Убираем Tabs - используем кнопочную логику как в fulfillment-supplies
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { Sidebar } from "@/components/dashboard/sidebar";
|
import { Sidebar } from "@/components/dashboard/sidebar";
|
||||||
import { useSidebar } from "@/hooks/useSidebar";
|
import { useSidebar } from "@/hooks/useSidebar";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams, useRouter } from "next/navigation";
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
@ -18,12 +17,11 @@ import {
|
|||||||
FileText,
|
FileText,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { GET_PENDING_SUPPLIES_COUNT } from "@/graphql/queries";
|
import { GET_PENDING_SUPPLIES_COUNT } from "@/graphql/queries";
|
||||||
import { FulfillmentGoodsTab } from "./fulfillment-supplies/fulfillment-goods-tab";
|
|
||||||
import { RealSupplyOrdersTab } from "./fulfillment-supplies/real-supply-orders-tab";
|
import { RealSupplyOrdersTab } from "./fulfillment-supplies/real-supply-orders-tab";
|
||||||
import { SellerSupplyOrdersTab } from "./fulfillment-supplies/seller-supply-orders-tab";
|
import { SellerSupplyOrdersTab } from "./fulfillment-supplies/seller-supply-orders-tab";
|
||||||
import { AllSuppliesTab } from "./fulfillment-supplies/all-supplies-tab";
|
import { AllSuppliesTab } from "./fulfillment-supplies/all-supplies-tab";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
// Убираем DropdownMenu - больше не используется
|
import { SuppliesStatistics } from "./supplies-statistics";
|
||||||
|
|
||||||
// Компонент для отображения бейджа с уведомлениями
|
// Компонент для отображения бейджа с уведомлениями
|
||||||
function NotificationBadge({ count }: { count: number }) {
|
function NotificationBadge({ count }: { count: number }) {
|
||||||
@ -39,10 +37,12 @@ function NotificationBadge({ count }: { count: number }) {
|
|||||||
export function SuppliesDashboard() {
|
export function SuppliesDashboard() {
|
||||||
const { getSidebarMargin } = useSidebar();
|
const { getSidebarMargin } = useSidebar();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
const router = useRouter();
|
||||||
const [activeTab, setActiveTab] = useState("fulfillment");
|
const [activeTab, setActiveTab] = useState("fulfillment");
|
||||||
const [activeSubTab, setActiveSubTab] = useState("goods");
|
const [activeSubTab, setActiveSubTab] = useState("goods");
|
||||||
const [activeThirdTab, setActiveThirdTab] = useState("cards");
|
const [activeThirdTab, setActiveThirdTab] = useState("cards");
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const [statisticsData, setStatisticsData] = useState<any>(null);
|
||||||
|
|
||||||
// Загружаем счетчик поставок, требующих одобрения
|
// Загружаем счетчик поставок, требующих одобрения
|
||||||
const { data: pendingData } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
|
const { data: pendingData } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
|
||||||
@ -95,7 +95,7 @@ export function SuppliesDashboard() {
|
|||||||
<main
|
<main
|
||||||
className={`flex-1 ${getSidebarMargin()} px-2 py-2 overflow-hidden transition-all duration-300`}
|
className={`flex-1 ${getSidebarMargin()} px-2 py-2 overflow-hidden transition-all duration-300`}
|
||||||
>
|
>
|
||||||
<div className="h-full">
|
<div className="h-full flex flex-col">
|
||||||
{/* Уведомляющий баннер */}
|
{/* Уведомляющий баннер */}
|
||||||
{hasPendingItems && (
|
{hasPendingItems && (
|
||||||
<Alert className="mb-4 bg-blue-500/20 border-blue-400/30 text-blue-300 animate-pulse">
|
<Alert className="mb-4 bg-blue-500/20 border-blue-400/30 text-blue-300 animate-pulse">
|
||||||
@ -132,8 +132,8 @@ export function SuppliesDashboard() {
|
|||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* БЛОК ВСЕХ ТАБОВ */}
|
{/* БЛОК 1: ТАБЫ (навигация) */}
|
||||||
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6">
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6 flex-shrink-0">
|
||||||
{/* УРОВЕНЬ 1: Главные табы */}
|
{/* УРОВЕНЬ 1: Главные табы */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<div className="grid w-full grid-cols-2 bg-white/15 backdrop-blur border-white/30 rounded-xl h-11 p-2">
|
<div className="grid w-full grid-cols-2 bg-white/15 backdrop-blur border-white/30 rounded-xl h-11 p-2">
|
||||||
@ -218,7 +218,7 @@ export function SuppliesDashboard() {
|
|||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
window.location.href = "/supplies/create-consumables";
|
router.push("/supplies/create-consumables");
|
||||||
}}
|
}}
|
||||||
className="h-6 px-2 py-1 bg-white/10 border border-white/20 hover:bg-white/20 text-xs font-medium text-white/80 hover:text-white rounded-md transition-all duration-150 flex items-center gap-1 cursor-pointer"
|
className="h-6 px-2 py-1 bg-white/10 border border-white/20 hover:bg-white/20 text-xs font-medium text-white/80 hover:text-white rounded-md transition-all duration-150 flex items-center gap-1 cursor-pointer"
|
||||||
>
|
>
|
||||||
@ -257,7 +257,7 @@ export function SuppliesDashboard() {
|
|||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
window.location.href = "/supplies/create-wildberries";
|
router.push("/supplies/create-wildberries");
|
||||||
}}
|
}}
|
||||||
className="h-6 px-2 py-1 bg-white/10 border border-white/20 hover:bg-white/20 text-xs font-medium text-white/80 hover:text-white rounded-md transition-all duration-150 flex items-center gap-1 cursor-pointer"
|
className="h-6 px-2 py-1 bg-white/10 border border-white/20 hover:bg-white/20 text-xs font-medium text-white/80 hover:text-white rounded-md transition-all duration-150 flex items-center gap-1 cursor-pointer"
|
||||||
>
|
>
|
||||||
@ -285,7 +285,7 @@ export function SuppliesDashboard() {
|
|||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
window.location.href = "/supplies/create-ozon";
|
router.push("/supplies/create-ozon");
|
||||||
}}
|
}}
|
||||||
className="h-6 px-2 py-1 bg-white/10 border border-white/20 hover:bg-white/20 text-xs font-medium text-white/80 hover:text-white rounded-md transition-all duration-150 flex items-center gap-1 cursor-pointer"
|
className="h-6 px-2 py-1 bg-white/10 border border-white/20 hover:bg-white/20 text-xs font-medium text-white/80 hover:text-white rounded-md transition-all duration-150 flex items-center gap-1 cursor-pointer"
|
||||||
>
|
>
|
||||||
@ -324,7 +324,7 @@ export function SuppliesDashboard() {
|
|||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
window.location.href = "/supplies/create-cards";
|
router.push("/supplies/create-cards");
|
||||||
}}
|
}}
|
||||||
className="h-5 px-1.5 py-0.5 bg-white/8 border border-white/15 hover:bg-white/15 text-xs font-normal text-white/60 hover:text-white/80 rounded-sm transition-all duration-150 flex items-center gap-0.5 cursor-pointer"
|
className="h-5 px-1.5 py-0.5 bg-white/8 border border-white/15 hover:bg-white/15 text-xs font-normal text-white/60 hover:text-white/80 rounded-sm transition-all duration-150 flex items-center gap-0.5 cursor-pointer"
|
||||||
>
|
>
|
||||||
@ -352,7 +352,7 @@ export function SuppliesDashboard() {
|
|||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
window.location.href = "/supplies/create-suppliers";
|
router.push("/supplies/create-suppliers");
|
||||||
}}
|
}}
|
||||||
className="h-5 px-1.5 py-0.5 bg-white/8 border border-white/15 hover:bg-white/15 text-xs font-normal text-white/60 hover:text-white/80 rounded-sm transition-all duration-150 flex items-center gap-0.5 cursor-pointer"
|
className="h-5 px-1.5 py-0.5 bg-white/8 border border-white/15 hover:bg-white/15 text-xs font-normal text-white/60 hover:text-white/80 rounded-sm transition-all duration-150 flex items-center gap-0.5 cursor-pointer"
|
||||||
>
|
>
|
||||||
@ -367,65 +367,77 @@ export function SuppliesDashboard() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* РАБОЧЕЕ ПРОСТРАНСТВО */}
|
{/* БЛОК 2: СТАТИСТИКА (метрики) */}
|
||||||
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl flex-1 overflow-hidden p-6">
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6 mt-4 flex-shrink-0">
|
||||||
{/* СОДЕРЖИМОЕ ПОСТАВОК НА ФУЛФИЛМЕНТ */}
|
<SuppliesStatistics
|
||||||
{activeTab === "fulfillment" && (
|
activeTab={activeTab}
|
||||||
<div className="h-full">
|
activeSubTab={activeSubTab}
|
||||||
{/* ТОВАР */}
|
activeThirdTab={activeThirdTab}
|
||||||
{activeSubTab === "goods" && (
|
data={statisticsData}
|
||||||
<div className="h-full">
|
loading={false}
|
||||||
{/* КАРТОЧКИ */}
|
/>
|
||||||
{activeThirdTab === "cards" && <FulfillmentGoodsTab />}
|
</div>
|
||||||
|
|
||||||
{/* ПОСТАВЩИКИ */}
|
{/* БЛОК 3: ОСНОВНОЙ КОНТЕНТ (сохраняем весь функционал) */}
|
||||||
{activeThirdTab === "suppliers" && (
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl mt-4 flex-1 min-h-0">
|
||||||
<AllSuppliesTab
|
<div className="h-full overflow-y-auto p-6">
|
||||||
pendingSupplyOrders={pendingCount?.supplyOrders || 0}
|
{/* СОДЕРЖИМОЕ ПОСТАВОК НА ФУЛФИЛМЕНТ */}
|
||||||
/>
|
{activeTab === "fulfillment" && (
|
||||||
)}
|
<div className="h-full">
|
||||||
</div>
|
{/* ТОВАР */}
|
||||||
)}
|
{activeSubTab === "goods" && (
|
||||||
|
<div className="h-full">
|
||||||
|
{/* ✅ ЕДИНАЯ ЛОГИКА для табов "Карточки" и "Поставщики" согласно rules2.md 9.5.3 */}
|
||||||
|
{(activeThirdTab === "cards" || activeThirdTab === "suppliers") && (
|
||||||
|
<AllSuppliesTab
|
||||||
|
pendingSupplyOrders={pendingCount?.supplyOrders || 0}
|
||||||
|
goodsSupplies={[]} // TODO: Подключить реальные данные поставок товаров из всех источников
|
||||||
|
loading={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* РАСХОДНИКИ СЕЛЛЕРА */}
|
{/* РАСХОДНИКИ СЕЛЛЕРА - сохраняем весь функционал */}
|
||||||
{activeSubTab === "consumables" && (
|
{activeSubTab === "consumables" && (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
{isWholesale ? (
|
{isWholesale ? (
|
||||||
<RealSupplyOrdersTab />
|
<RealSupplyOrdersTab />
|
||||||
) : (
|
) : (
|
||||||
<SellerSupplyOrdersTab />
|
<SellerSupplyOrdersTab />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* СОДЕРЖИМОЕ ПОСТАВОК НА МАРКЕТПЛЕЙСЫ */}
|
{/* СОДЕРЖИМОЕ ПОСТАВОК НА МАРКЕТПЛЕЙСЫ */}
|
||||||
{activeTab === "marketplace" && (
|
{activeTab === "marketplace" && (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
{/* WILDBERRIES */}
|
{/* WILDBERRIES - плейсхолдер */}
|
||||||
{activeSubTab === "wildberries" && (
|
{activeSubTab === "wildberries" && (
|
||||||
<div className="text-white/70 text-center py-8">
|
<div className="text-white/70 text-center py-8">
|
||||||
<Package className="h-16 w-16 mx-auto mb-4 text-white/30" />
|
<Package className="h-16 w-16 mx-auto mb-4 text-white/30" />
|
||||||
<h3 className="text-xl font-semibold mb-2">
|
<h3 className="text-xl font-semibold mb-2">
|
||||||
Поставки на Wildberries
|
Поставки на Wildberries
|
||||||
</h3>
|
</h3>
|
||||||
<p>Раздел находится в разработке</p>
|
<p>Раздел находится в разработке</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* OZON */}
|
{/* OZON - плейсхолдер */}
|
||||||
{activeSubTab === "ozon" && (
|
{activeSubTab === "ozon" && (
|
||||||
<div className="text-white/70 text-center py-8">
|
<div className="text-white/70 text-center py-8">
|
||||||
<Package className="h-16 w-16 mx-auto mb-4 text-white/30" />
|
<Package className="h-16 w-16 mx-auto mb-4 text-white/30" />
|
||||||
<h3 className="text-xl font-semibold mb-2">
|
<h3 className="text-xl font-semibold mb-2">
|
||||||
Поставки на Ozon
|
Поставки на Ozon
|
||||||
</h3>
|
</h3>
|
||||||
<p>Раздел находится в разработке</p>
|
<p>Раздел находится в разработке</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
211
src/components/supplies/supplies-statistics.tsx
Normal file
211
src/components/supplies/supplies-statistics.tsx
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Card } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Package,
|
||||||
|
TrendingUp,
|
||||||
|
DollarSign,
|
||||||
|
Truck,
|
||||||
|
AlertTriangle,
|
||||||
|
BarChart,
|
||||||
|
ShoppingCart,
|
||||||
|
Undo2
|
||||||
|
} from "lucide-react";
|
||||||
|
import { formatCurrency } from "@/lib/utils";
|
||||||
|
|
||||||
|
interface StatisticCardProps {
|
||||||
|
title: string;
|
||||||
|
value: string | number;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
trend?: {
|
||||||
|
value: number;
|
||||||
|
isPositive: boolean;
|
||||||
|
};
|
||||||
|
loading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatisticCard({ title, value, icon, trend, loading }: StatisticCardProps) {
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Card className="bg-white/5 backdrop-blur border-white/10 p-4">
|
||||||
|
<div className="animate-pulse">
|
||||||
|
<div className="h-3 bg-white/10 rounded w-24 mb-2"></div>
|
||||||
|
<div className="h-6 bg-white/10 rounded w-32"></div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="bg-white/5 backdrop-blur border-white/10 p-4 hover:border-white/20 transition-all">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-xs text-white/60 mb-1">{title}</p>
|
||||||
|
<p className="text-xl font-semibold text-white">
|
||||||
|
{typeof value === 'number' && title.includes('Сумма')
|
||||||
|
? formatCurrency(value)
|
||||||
|
: value}
|
||||||
|
</p>
|
||||||
|
{trend && (
|
||||||
|
<div className={`flex items-center mt-1 text-xs ${
|
||||||
|
trend.isPositive ? 'text-green-400' : 'text-red-400'
|
||||||
|
}`}>
|
||||||
|
<TrendingUp className={`h-3 w-3 mr-1 ${!trend.isPositive && 'rotate-180'}`} />
|
||||||
|
<span>{Math.abs(trend.value)}%</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="text-white/40">
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SuppliesStatisticsProps {
|
||||||
|
activeTab: string;
|
||||||
|
activeSubTab: string;
|
||||||
|
activeThirdTab?: string;
|
||||||
|
data?: any; // Данные будут приходить из родительского компонента
|
||||||
|
loading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SuppliesStatistics({
|
||||||
|
activeTab,
|
||||||
|
activeSubTab,
|
||||||
|
activeThirdTab,
|
||||||
|
data,
|
||||||
|
loading = false
|
||||||
|
}: SuppliesStatisticsProps) {
|
||||||
|
|
||||||
|
// Определяем какие метрики показывать в зависимости от активных табов
|
||||||
|
const getStatistics = () => {
|
||||||
|
// ✅ Фулфилмент → Товар → Карточки/Поставщики - ОБЩИЕ МЕТРИКИ согласно rules2.md 9.5.2
|
||||||
|
if (activeTab === "fulfillment" && activeSubTab === "goods") {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Всего поставок товаров",
|
||||||
|
value: data?.totalGoodsSupplies || 0,
|
||||||
|
icon: <Package className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Активных поставок",
|
||||||
|
value: data?.activeGoodsSupplies || 0,
|
||||||
|
icon: <TrendingUp className="h-5 w-5" />,
|
||||||
|
trend: data?.activeGoodsSuppliesTrend
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Сумма активных",
|
||||||
|
value: data?.activeGoodsSuppliesSum || 0,
|
||||||
|
icon: <DollarSign className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Товаров в пути",
|
||||||
|
value: data?.goodsInTransit || 0,
|
||||||
|
icon: <Truck className="h-5 w-5" />,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Фулфилмент → Расходники селлера
|
||||||
|
if (activeTab === "fulfillment" && activeSubTab === "consumables") {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Всего поставок",
|
||||||
|
value: data?.totalSupplies || 0,
|
||||||
|
icon: <Package className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Активных поставок",
|
||||||
|
value: data?.activeSupplies || 0,
|
||||||
|
icon: <TrendingUp className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Видов расходников",
|
||||||
|
value: data?.consumableTypes || 0,
|
||||||
|
icon: <BarChart className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Критические остатки",
|
||||||
|
value: data?.criticalStock || 0,
|
||||||
|
icon: <AlertTriangle className="h-5 w-5" />,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Маркетплейсы → Wildberries
|
||||||
|
if (activeTab === "marketplace" && activeSubTab === "wildberries") {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Поставок на WB",
|
||||||
|
value: data?.totalWbSupplies || 0,
|
||||||
|
icon: <ShoppingCart className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Товаров отправлено",
|
||||||
|
value: data?.sentProducts || 0,
|
||||||
|
icon: <Package className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Возвраты за неделю",
|
||||||
|
value: data?.weeklyReturns || 0,
|
||||||
|
icon: <Undo2 className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Эффективность",
|
||||||
|
value: `${data?.efficiency || 0}%`,
|
||||||
|
icon: <TrendingUp className="h-5 w-5" />,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Маркетплейсы → Ozon
|
||||||
|
if (activeTab === "marketplace" && activeSubTab === "ozon") {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Поставок на Ozon",
|
||||||
|
value: data?.totalOzonSupplies || 0,
|
||||||
|
icon: <ShoppingCart className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Товаров отправлено",
|
||||||
|
value: data?.sentProducts || 0,
|
||||||
|
icon: <Package className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Возвраты за неделю",
|
||||||
|
value: data?.weeklyReturns || 0,
|
||||||
|
icon: <Undo2 className="h-5 w-5" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Эффективность",
|
||||||
|
value: `${data?.efficiency || 0}%`,
|
||||||
|
icon: <TrendingUp className="h-5 w-5" />,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const statistics = getStatistics();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
|
{statistics.map((stat, index) => (
|
||||||
|
<StatisticCard
|
||||||
|
key={index}
|
||||||
|
title={stat.title}
|
||||||
|
value={stat.value}
|
||||||
|
icon={stat.icon}
|
||||||
|
trend={stat.trend}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -595,6 +595,51 @@ export const GET_ALL_PRODUCTS = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Запрос товаров конкретной организации (для формы создания поставки)
|
||||||
|
export const GET_ORGANIZATION_PRODUCTS = gql`
|
||||||
|
query GetOrganizationProducts($organizationId: ID!, $search: String, $category: String, $type: String) {
|
||||||
|
organizationProducts(organizationId: $organizationId, search: $search, category: $category, type: $type) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
article
|
||||||
|
description
|
||||||
|
price
|
||||||
|
quantity
|
||||||
|
type
|
||||||
|
category {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
brand
|
||||||
|
color
|
||||||
|
size
|
||||||
|
weight
|
||||||
|
dimensions
|
||||||
|
material
|
||||||
|
images
|
||||||
|
mainImage
|
||||||
|
isActive
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
organization {
|
||||||
|
id
|
||||||
|
inn
|
||||||
|
name
|
||||||
|
fullName
|
||||||
|
type
|
||||||
|
address
|
||||||
|
phones
|
||||||
|
emails
|
||||||
|
users {
|
||||||
|
id
|
||||||
|
avatar
|
||||||
|
managerName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const GET_MY_CART = gql`
|
export const GET_MY_CART = gql`
|
||||||
query GetMyCart {
|
query GetMyCart {
|
||||||
myCart {
|
myCart {
|
||||||
|
@ -1748,6 +1748,76 @@ export const resolvers = {
|
|||||||
return products;
|
return products;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Товары конкретной организации (для формы создания поставки)
|
||||||
|
organizationProducts: async (
|
||||||
|
_: unknown,
|
||||||
|
args: { organizationId: string; search?: string; category?: string; type?: string },
|
||||||
|
context: Context
|
||||||
|
) => {
|
||||||
|
console.log("🏢 ORGANIZATION_PRODUCTS RESOLVER - ВЫЗВАН:", {
|
||||||
|
userId: context.user?.id,
|
||||||
|
organizationId: args.organizationId,
|
||||||
|
search: args.search,
|
||||||
|
category: args.category,
|
||||||
|
type: args.type,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!context.user) {
|
||||||
|
throw new GraphQLError("Требуется авторизация", {
|
||||||
|
extensions: { code: "UNAUTHENTICATED" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const where: Record<string, unknown> = {
|
||||||
|
isActive: true, // Показываем только активные товары
|
||||||
|
organizationId: args.organizationId, // Фильтруем по конкретной организации
|
||||||
|
type: args.type || "ТОВАР", // Показываем только товары по умолчанию, НЕ расходники согласно development-checklist.md
|
||||||
|
};
|
||||||
|
|
||||||
|
if (args.search) {
|
||||||
|
where.OR = [
|
||||||
|
{ name: { contains: args.search, mode: "insensitive" } },
|
||||||
|
{ article: { contains: args.search, mode: "insensitive" } },
|
||||||
|
{ description: { contains: args.search, mode: "insensitive" } },
|
||||||
|
{ brand: { contains: args.search, mode: "insensitive" } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.category) {
|
||||||
|
where.categoryId = args.category;
|
||||||
|
}
|
||||||
|
|
||||||
|
const products = await prisma.product.findMany({
|
||||||
|
where,
|
||||||
|
include: {
|
||||||
|
category: true,
|
||||||
|
organization: {
|
||||||
|
include: {
|
||||||
|
users: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: "desc" },
|
||||||
|
take: 100, // Ограничиваем количество результатов
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("🔥 ORGANIZATION_PRODUCTS RESOLVER DEBUG:", {
|
||||||
|
organizationId: args.organizationId,
|
||||||
|
searchArgs: args,
|
||||||
|
whereCondition: where,
|
||||||
|
totalProducts: products.length,
|
||||||
|
productTypes: products.map((p) => ({
|
||||||
|
id: p.id,
|
||||||
|
name: p.name,
|
||||||
|
type: p.type,
|
||||||
|
isActive: p.isActive,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
return products;
|
||||||
|
},
|
||||||
|
|
||||||
// Все категории
|
// Все категории
|
||||||
categories: async (_: unknown, __: unknown, context: Context) => {
|
categories: async (_: unknown, __: unknown, context: Context) => {
|
||||||
if (!context.user && !context.admin) {
|
if (!context.user && !context.admin) {
|
||||||
|
@ -70,6 +70,9 @@ export const typeDefs = gql`
|
|||||||
# Все товары всех поставщиков для маркета
|
# Все товары всех поставщиков для маркета
|
||||||
allProducts(search: String, category: String): [Product!]!
|
allProducts(search: String, category: String): [Product!]!
|
||||||
|
|
||||||
|
# Товары конкретной организации (для формы создания поставки)
|
||||||
|
organizationProducts(organizationId: ID!, search: String, category: String, type: String): [Product!]!
|
||||||
|
|
||||||
# Все категории
|
# Все категории
|
||||||
categories: [Category!]!
|
categories: [Category!]!
|
||||||
|
|
||||||
@ -467,10 +470,8 @@ export const typeDefs = gql`
|
|||||||
OZON
|
OZON
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ProductType {
|
# ProductType теперь String, чтобы поддерживать кириллические значения из БД
|
||||||
PRODUCT
|
# Возможные значения: "ТОВАР", "БРАК", "РАСХОДНИКИ", "ПРОДУКТ"
|
||||||
CONSUMABLE
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CounterpartyRequestStatus {
|
enum CounterpartyRequestStatus {
|
||||||
PENDING
|
PENDING
|
||||||
@ -746,7 +747,7 @@ export const typeDefs = gql`
|
|||||||
inTransit: Int
|
inTransit: Int
|
||||||
stock: Int
|
stock: Int
|
||||||
sold: Int
|
sold: Int
|
||||||
type: ProductType
|
type: String
|
||||||
category: Category
|
category: Category
|
||||||
brand: String
|
brand: String
|
||||||
color: String
|
color: String
|
||||||
@ -774,7 +775,7 @@ export const typeDefs = gql`
|
|||||||
inTransit: Int
|
inTransit: Int
|
||||||
stock: Int
|
stock: Int
|
||||||
sold: Int
|
sold: Int
|
||||||
type: ProductType
|
type: String
|
||||||
categoryId: ID
|
categoryId: ID
|
||||||
brand: String
|
brand: String
|
||||||
color: String
|
color: String
|
||||||
|
@ -48,25 +48,58 @@ const authLink = setContext((operation, { headers }) => {
|
|||||||
// Error Link для обработки ошибок с детальным логированием
|
// Error Link для обработки ошибок с детальным логированием
|
||||||
const errorLink = onError(
|
const errorLink = onError(
|
||||||
({ graphQLErrors, networkError, operation, forward }) => {
|
({ graphQLErrors, networkError, operation, forward }) => {
|
||||||
if (graphQLErrors) {
|
try {
|
||||||
graphQLErrors.forEach(({ message, locations, path, extensions }) => {
|
// Расширенная отладочная информация для всех ошибок
|
||||||
console.error("🚨 GraphQL Error:", {
|
const debugInfo = {
|
||||||
message,
|
hasGraphQLErrors: !!graphQLErrors,
|
||||||
locations,
|
graphQLErrorsLength: graphQLErrors?.length || 0,
|
||||||
path,
|
hasNetworkError: !!networkError,
|
||||||
extensions,
|
operationName: operation?.operationName || "Unknown",
|
||||||
operation: operation.operationName,
|
operationType: operation?.query?.definitions?.[0]?.operation || "Unknown",
|
||||||
variables: operation.variables,
|
variables: operation?.variables || {},
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("🎯 APOLLO ERROR LINK TRIGGERED:", debugInfo);
|
||||||
|
|
||||||
|
// Безопасная обработка GraphQL ошибок
|
||||||
|
if (graphQLErrors && Array.isArray(graphQLErrors) && graphQLErrors.length > 0) {
|
||||||
|
console.log("📊 GRAPHQL ERRORS COUNT:", graphQLErrors.length);
|
||||||
|
|
||||||
|
graphQLErrors.forEach((error, index) => {
|
||||||
|
try {
|
||||||
|
// Безопасная деструктуризация
|
||||||
|
const message = error?.message || "No message";
|
||||||
|
const locations = error?.locations || [];
|
||||||
|
const path = error?.path || [];
|
||||||
|
const extensions = error?.extensions || {};
|
||||||
|
|
||||||
|
console.log(`🚨 GraphQL Error #${index + 1}:`, {
|
||||||
|
message,
|
||||||
|
locations,
|
||||||
|
path,
|
||||||
|
extensions,
|
||||||
|
operation: operation?.operationName || "Unknown",
|
||||||
|
});
|
||||||
|
} catch (innerError) {
|
||||||
|
console.log(`❌ Error processing GraphQL error #${index + 1}:`, innerError);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (networkError) {
|
// Безопасная обработка Network ошибок
|
||||||
console.error("🌐 Network Error:", {
|
if (networkError) {
|
||||||
error: networkError,
|
try {
|
||||||
operation: operation.operationName,
|
console.log("🌐 Network Error:", {
|
||||||
variables: operation.variables,
|
message: networkError.message || "No message",
|
||||||
});
|
statusCode: networkError.statusCode || "No status",
|
||||||
|
operation: operation?.operationName || "Unknown",
|
||||||
|
});
|
||||||
|
} catch (innerError) {
|
||||||
|
console.log("❌ Error processing network error:", innerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (outerError) {
|
||||||
|
console.log("❌ Critical error in Apollo error link:", outerError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -23,3 +23,13 @@ export function formatPhone(phone: string): string {
|
|||||||
// Форматируем как +7 (999) 999-99-99
|
// Форматируем как +7 (999) 999-99-99
|
||||||
return `+7 (${normalizedDigits.slice(1, 4)}) ${normalizedDigits.slice(4, 7)}-${normalizedDigits.slice(7, 9)}-${normalizedDigits.slice(9, 11)}`
|
return `+7 (${normalizedDigits.slice(1, 4)}) ${normalizedDigits.slice(4, 7)}-${normalizedDigits.slice(7, 9)}-${normalizedDigits.slice(9, 11)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Функция для форматирования валюты
|
||||||
|
export function formatCurrency(amount: number, currency: string = 'RUB'): string {
|
||||||
|
return new Intl.NumberFormat('ru-RU', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: currency,
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
}).format(amount)
|
||||||
|
}
|
||||||
|
@ -495,9 +495,111 @@ text-white/50 /* Приглушенный текст */
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 **11. ЧЕКЛИСТ ПРИМЕНЕНИЯ**
|
## 📐 **11. ПРАВИЛО ВЫРАВНИВАНИЯ БЛОКОВ С САЙДБАРОМ**
|
||||||
|
|
||||||
### 10.1 Перед внедрением компонента
|
### 11.1 Принцип выравнивания
|
||||||
|
|
||||||
|
**🎯 ОСНОВНОЙ ПРИНЦИП:**
|
||||||
|
|
||||||
|
> Все блоки контента справа от сайдбара должны быть выровнены точно по верхнему и нижнему краю сайдбара без отступов.
|
||||||
|
|
||||||
|
**📐 ВИЗУАЛЬНАЯ СХЕМА:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ SIDEBAR │ ← блоки начинаются │ ← ТОП САЙДБАРА
|
||||||
|
│ │ точно здесь │
|
||||||
|
│ │ │
|
||||||
|
│ │ [КОНТЕНТ БЛОКИ] │
|
||||||
|
│ │ │
|
||||||
|
│ │ ← блоки заканчиваются │ ← НИЗ САЙДБАРА
|
||||||
|
│ │ точно здесь │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.2 Техническая реализация
|
||||||
|
|
||||||
|
**✅ ПРАВИЛЬНО:**
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
<div className="h-screen flex overflow-hidden">
|
||||||
|
<Sidebar />
|
||||||
|
<main className={`flex-1 ${getSidebarMargin()} overflow-hidden transition-all duration-300`}>
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
{/* Блоки занимают всю доступную область */}
|
||||||
|
<div className="flex-1 flex gap-2 min-h-0">
|
||||||
|
{/* Блоки без внешних отступов */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**❌ НЕПРАВИЛЬНО:**
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// НЕ ИСПОЛЬЗОВАТЬ px-2 py-2 в main контейнере!
|
||||||
|
<main className={`flex-1 ${getSidebarMargin()} px-2 py-2 overflow-hidden`}>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.3 Требования к структуре
|
||||||
|
|
||||||
|
**MAIN КОНТЕЙНЕР:**
|
||||||
|
|
||||||
|
- ✅ БЕЗ `px-2 py-2` отступов
|
||||||
|
- ✅ Только `overflow-hidden` и `transition-all duration-300`
|
||||||
|
- ✅ Использовать `${getSidebarMargin()}` для отступа от сайдбара
|
||||||
|
|
||||||
|
**БЛОКИ КОНТЕНТА:**
|
||||||
|
|
||||||
|
- ✅ Занимают всю высоту: `h-full flex flex-col`
|
||||||
|
- ✅ Минимальные gap между блоками: `gap-2`
|
||||||
|
- ✅ Начинаются от самого верха экрана
|
||||||
|
- ✅ Заканчиваются у самого низа экрана
|
||||||
|
|
||||||
|
**SPACING МЕЖДУ БЛОКАМИ:**
|
||||||
|
|
||||||
|
- ✅ Горизонтальный gap: `gap-2` (8px)
|
||||||
|
- ✅ Вертикальный gap: `gap-2` (8px)
|
||||||
|
- ❌ НЕ использовать внешние отступы `px-* py-*`
|
||||||
|
|
||||||
|
### 11.4 Области применения
|
||||||
|
|
||||||
|
**✅ ПРИМЕНЯЕТСЯ К:**
|
||||||
|
|
||||||
|
- Все страницы с сайдбаром
|
||||||
|
- Дашборды и кабинеты пользователей
|
||||||
|
- Модальные окна полной высоты
|
||||||
|
- Списки, таблицы и каталоги
|
||||||
|
- Формы создания и редактирования
|
||||||
|
|
||||||
|
**❌ ИСКЛЮЧЕНИЯ:**
|
||||||
|
|
||||||
|
- Модальные окна (центрированные)
|
||||||
|
- Всплывающие уведомления (toast)
|
||||||
|
- Dropdown меню
|
||||||
|
- Контекстные меню
|
||||||
|
|
||||||
|
### 11.5 Проверка соответствия
|
||||||
|
|
||||||
|
**КРИТЕРИИ СООТВЕТСТВИЯ:**
|
||||||
|
|
||||||
|
1. **Верхний край**: Первый блок начинается на уровне верха сайдбара
|
||||||
|
2. **Нижний край**: Последний блок заканчивается на уровне низа сайдбара
|
||||||
|
3. **Без отступов**: Отсутствуют `px-* py-*` отступы в main
|
||||||
|
4. **Только gap**: Используется только `gap-2` между блоками
|
||||||
|
|
||||||
|
**ИНСТРУМЕНТЫ ПРОВЕРКИ:**
|
||||||
|
|
||||||
|
- Инспектор браузера для проверки CSS
|
||||||
|
- Визуальное сравнение с краями сайдбара
|
||||||
|
- Проверка на разных размерах экрана
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 **12. ЧЕКЛИСТ ПРИМЕНЕНИЯ**
|
||||||
|
|
||||||
|
### 12.1 Перед внедрением компонента
|
||||||
|
|
||||||
- [ ] Соответствует цветовой палитре системы
|
- [ ] Соответствует цветовой палитре системы
|
||||||
- [ ] Использует правильную типографику
|
- [ ] Использует правильную типографику
|
||||||
@ -506,20 +608,22 @@ text-white/50 /* Приглушенный текст */
|
|||||||
- [ ] Соответствует требованиям доступности
|
- [ ] Соответствует требованиям доступности
|
||||||
- [ ] Использует стандартные анимации
|
- [ ] Использует стандартные анимации
|
||||||
- [ ] Оптимизирован для производительности
|
- [ ] Оптимизирован для производительности
|
||||||
|
- [ ] **Блоки выровнены с сайдбаром (правило 11)**
|
||||||
|
|
||||||
### 10.2 При создании новых компонентов
|
### 12.2 При создании новых компонентов
|
||||||
|
|
||||||
- [ ] Базируется на существующих паттернах
|
- [ ] Базируется на существующих паттернах
|
||||||
- [ ] Совместим с UI Kit системы
|
- [ ] Совместим с UI Kit системы
|
||||||
- [ ] Документирован в Storybook/UI Kit демо
|
- [ ] Документирован в Storybook/UI Kit демо
|
||||||
- [ ] Протестирован на различных устройствах
|
- [ ] Протестирован на различных устройствах
|
||||||
- [ ] Соответствует принципам дизайн-системы
|
- [ ] Соответствует принципам дизайн-системы
|
||||||
|
- [ ] **Применяет правило выравнивания с сайдбаром**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎨 **11. ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ**
|
## 🎨 **13. ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ**
|
||||||
|
|
||||||
### 11.1 Базовая карточка
|
### 13.1 Базовая карточка
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
<Card className="glass-card border-white/10 hover:border-white/20 transition-all">
|
<Card className="glass-card border-white/10 hover:border-white/20 transition-all">
|
||||||
@ -533,7 +637,7 @@ text-white/50 /* Приглушенный текст */
|
|||||||
</Card>
|
</Card>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 11.2 Интерактивная кнопка
|
### 13.2 Интерактивная кнопка
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
<Button
|
<Button
|
||||||
@ -546,7 +650,7 @@ text-white/50 /* Приглушенный текст */
|
|||||||
</Button>
|
</Button>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 11.3 Форма с валидацией
|
### 13.3 Форма с валидацией
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@ -559,6 +663,43 @@ text-white/50 /* Приглушенный текст */
|
|||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 13.4 Правильное выравнивание с сайдбаром
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// ✅ ПРАВИЛЬНАЯ структура страницы с сайдбаром
|
||||||
|
function PageWithSidebar() {
|
||||||
|
const { getSidebarMargin } = useSidebar();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-screen flex overflow-hidden">
|
||||||
|
<Sidebar />
|
||||||
|
<main className={`flex-1 ${getSidebarMargin()} overflow-hidden transition-all duration-300`}>
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
{/* Структура из 3 блоков согласно rules1.md */}
|
||||||
|
<div className="flex-1 flex gap-2 min-h-0">
|
||||||
|
{/* Левые блоки */}
|
||||||
|
<div className="flex-1 flex flex-col gap-2 min-h-0">
|
||||||
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl">
|
||||||
|
{/* Блок 1: Основной контент */}
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl flex-1">
|
||||||
|
{/* Блок 2: Дополнительный контент */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Правый блок */}
|
||||||
|
<div className="w-96 flex-shrink-0">
|
||||||
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl h-full">
|
||||||
|
{/* Блок 3: Боковая панель */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**📊 СТАТУС**: Действующие правила v1.0
|
**📊 СТАТУС**: Действующие правила v1.0
|
||||||
|
Reference in New Issue
Block a user