From a0d995c1f13808a80b42eb47778c3af791709bd7 Mon Sep 17 00:00:00 2001 From: ochenstarik Date: Fri, 11 Sep 2026 09:36:45 +0700 Subject: [PATCH] feat(catalog): universal first-screen modules catalog, procurement demo page and e2e scenario 11 --- e2e/10-indexeddb-snapshots.spec.ts | 4 +- e2e/11-modules-catalog.spec.ts | 162 +++++ playwright.config.ts | 28 +- src/app/router/index.tsx | 2 + src/app/shell/Sidebar.tsx | 11 +- src/core/modules/registry.ts | 12 + src/features/catalog/ModuleCard.tsx | 151 +++++ src/features/catalog/ModuleInfoModal.tsx | 175 ++++++ src/features/catalog/ModulesCatalog.tsx | 486 +++++++++++++++ src/features/catalog/catalogData.ts | 747 +++++++++++++++++++++++ src/features/settings/SettingsModule.tsx | 1 + src/mock/store.ts | 28 + src/pages/DashboardPage.tsx | 12 +- src/pages/ProcurementPage.tsx | 240 ++++++++ src/pages/index.ts | 1 + src/shared/ui/MaturityBadge.tsx | 11 +- 16 files changed, 2046 insertions(+), 25 deletions(-) create mode 100644 e2e/11-modules-catalog.spec.ts create mode 100644 src/features/catalog/ModuleCard.tsx create mode 100644 src/features/catalog/ModuleInfoModal.tsx create mode 100644 src/features/catalog/ModulesCatalog.tsx create mode 100644 src/features/catalog/catalogData.ts create mode 100644 src/pages/ProcurementPage.tsx diff --git a/e2e/10-indexeddb-snapshots.spec.ts b/e2e/10-indexeddb-snapshots.spec.ts index dd8708d..c267f31 100644 --- a/e2e/10-indexeddb-snapshots.spec.ts +++ b/e2e/10-indexeddb-snapshots.spec.ts @@ -2,8 +2,8 @@ import { test, expect } from '@playwright/test'; test.describe('Scenario 10: IndexedDB Snapshots (Export, Reset, Import)', () => { test('exports database snapshot, resets demo data, imports snapshot, and verifies restored state', async ({ page }) => { - await page.goto('/settings'); - await page.waitForLoadState('domcontentloaded'); + test.setTimeout(60000); + await page.goto('/settings', { waitUntil: 'domcontentloaded' }); // 1. Export database snapshot const exportBtn = page.locator('[data-testid="export-snapshot-btn"]'); diff --git a/e2e/11-modules-catalog.spec.ts b/e2e/11-modules-catalog.spec.ts new file mode 100644 index 0000000..dd59aac --- /dev/null +++ b/e2e/11-modules-catalog.spec.ts @@ -0,0 +1,162 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Scenario 11: Universal Modules Catalog & Profiles', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/'); + await page.waitForLoadState('domcontentloaded'); + }); + + test('1. First open immediately displays all 18 modules with "Все модули" active by default', async ({ page }) => { + const container = page.locator('[data-testid="modules-catalog-container"]'); + await expect(container).toBeVisible({ timeout: 15000 }); + + await expect(page.locator('h1')).toContainText('Единый каталог модулей Krona'); + + const allProfileBtn = page.locator('[data-testid="profile-filter-all"]'); + await expect(allProfileBtn).toBeVisible(); + await expect(allProfileBtn).toContainText('Все модули (18)'); + + const cards = page.locator('[data-testid^="module-card-"]'); + await expect(cards).toHaveCount(18); + + await expect(page.locator('[data-testid="module-card-crm"]')).toBeVisible(); + await expect(page.locator('[data-testid="module-card-procurement"]')).toBeVisible(); + await expect(page.locator('[data-testid="module-card-operations"]')).toBeVisible(); + await expect(page.locator('[data-testid="module-card-finance"]')).toBeVisible(); + }); + + test('2. Industry profile highlights recommended modules without hiding the rest', async ({ page }) => { + const restaurantBtn = page.locator('[data-testid="profile-filter-restaurant_hall_delivery"]'); + await expect(restaurantBtn).toBeVisible(); + await restaurantBtn.click(); + + const recommendedSection = page.locator('[data-testid="recommended-section"]'); + const otherSection = page.locator('[data-testid="other-modules-section"]'); + + await expect(recommendedSection).toBeVisible(); + await expect(otherSection).toBeVisible(); + + // Recommended section must contain restaurant modules (e.g. operations) + await expect(recommendedSection.locator('[data-testid="module-card-operations"]')).toBeVisible(); + + // Other section must contain non-restaurant recommended modules (e.g. helpdesk, not hidden!) + await expect(otherSection.locator('[data-testid="module-card-helpdesk"]')).toBeVisible(); + + // Total count of modules across both sections must still be 18 + const totalCards = page.locator('[data-testid^="module-card-"]'); + await expect(totalCards).toHaveCount(18); + + // Reset button returns to "Все модули" + const resetBtn = page.locator('[data-testid="reset-profile-filter-btn"]'); + await expect(resetBtn).toBeVisible(); + await resetBtn.click(); + await expect(page.locator('[data-testid="all-modules-grid"]')).toBeVisible(); + }); + + test('3. Search and category filters work properly', async ({ page }) => { + const goodsCategoryBtn = page.locator('[data-testid="category-filter-inventory"]'); + await expect(goodsCategoryBtn).toBeVisible(); + await goodsCategoryBtn.click(); + + const filteredCards = page.locator('[data-testid^="module-card-"]'); + await expect(filteredCards).toHaveCount(2); + await expect(page.locator('[data-testid="module-card-inventory"]')).toBeVisible(); + await expect(page.locator('[data-testid="module-card-procurement"]')).toBeVisible(); + + const searchInput = page.locator('[data-testid="catalog-search-input"]'); + await searchInput.fill('закупки'); + + await expect(filteredCards).toHaveCount(1); + await expect(page.locator('[data-testid="module-card-procurement"]')).toBeVisible(); + + const resetAllBtn = page.locator('[data-testid="reset-all-filters-btn"]'); + await expect(resetAllBtn).toBeVisible(); + await resetAllBtn.click(); + + await expect(page.locator('[data-testid^="module-card-"]')).toHaveCount(18); + }); + + test('4. Module cards navigate directly to module route', async ({ page }) => { + const crmOpenBtn = page.locator('[data-testid="module-open-btn-crm"]'); + await expect(crmOpenBtn).toBeVisible(); + await crmOpenBtn.click(); + + await page.waitForURL('**/crm'); + await expect(page.locator('h1, h2').first()).toBeVisible(); + + await page.goto('/'); + await page.waitForLoadState('domcontentloaded'); + + const procOpenBtn = page.locator('[data-testid="module-open-btn-procurement"]'); + await expect(procOpenBtn).toBeVisible(); + await procOpenBtn.click(); + + await page.waitForURL('**/procurement'); + await expect(page.locator('h1')).toContainText('Закупки и управление поставщиками'); + await expect(page.getByTestId('maturity-badge-planned')).toBeVisible(); + }); + + test('5. "О модуле" opens informative details modal', async ({ page }) => { + const detailsBtn = page.locator('[data-testid="module-details-btn-procurement"]'); + await expect(detailsBtn).toBeVisible(); + await detailsBtn.click(); + + const modal = page.locator('[data-testid="module-info-modal"]'); + await expect(modal).toBeVisible(); + await expect(modal).toContainText('Закупки и поставщики'); + await expect(modal).toContainText('Назначение модуля'); + await expect(modal).toContainText('Ключевые функциональные возможности'); + await expect(modal).toContainText('Статус реализации'); + await expect(modal).toContainText('Запланированные интеграции'); + + const closeBtn = page.locator('[data-testid="close-module-info-modal"]'); + await closeBtn.click(); + await expect(modal).not.toBeVisible(); + }); + + test('6. Database reset in Settings restores "Все модули" filter mode', async ({ page }) => { + const retailBtn = page.locator('[data-testid="profile-filter-retail"]'); + await expect(retailBtn).toBeVisible(); + await retailBtn.click(); + await expect(page.locator('[data-testid="recommended-section"]')).toBeVisible(); + + await page.goto('/settings/entities'); + await page.waitForLoadState('domcontentloaded'); + + const resetBtn = page.getByRole('button', { name: /Сбросить базу данных/i }); + await expect(resetBtn).toBeVisible({ timeout: 10000 }); + await resetBtn.click(); + + const confirmBtn = page.getByRole('button', { name: /Да, сбросить/i }); + if (await confirmBtn.isVisible()) { + await confirmBtn.click(); + } + + await page.waitForTimeout(1000); + + await page.goto('/'); + await page.waitForLoadState('domcontentloaded'); + + const allProfileBtn = page.locator('[data-testid="profile-filter-all"]'); + await expect(allProfileBtn).toBeVisible(); + await expect(page.locator('[data-testid="all-modules-grid"]')).toBeVisible(); + await expect(page.locator('[data-testid^="module-card-"]')).toHaveCount(18); + }); + + test('7. Catalog renders with zero horizontal overflow across viewport', async ({ page }) => { + const isOverflowing = await page.evaluate(() => { + return document.documentElement.scrollWidth > document.documentElement.clientWidth; + }); + expect(isOverflowing).toBe(false); + + const kpiTab = page.locator('[data-testid="view-tab-dashboard"]'); + await expect(kpiTab).toBeVisible(); + await kpiTab.click(); + + await expect(page.locator('text=Выручка').first()).toBeVisible({ timeout: 10000 }); + + const catalogTab = page.locator('[data-testid="view-tab-catalog"]'); + await catalogTab.click(); + await expect(page.locator('[data-testid="modules-catalog-container"]')).toBeVisible(); + }); +}); diff --git a/playwright.config.ts b/playwright.config.ts index 22dc32d..88c66e9 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from '@playwright/test'; export default defineConfig({ testDir: './e2e', - testMatch: ['**/0[1-9]-*.spec.ts', '**/10-*.spec.ts'], + testMatch: ['**/0[1-9]-*.spec.ts', '**/1[0-9]-*.spec.ts'], testIgnore: ['**/*control-center*'], timeout: 30000, fullyParallel: false, @@ -11,33 +11,45 @@ export default defineConfig({ workers: 1, reporter: 'list', use: { - baseURL: 'http://localhost:5173', + baseURL: 'http://127.0.0.1:5173', trace: 'on-first-retry', }, projects: [ { - name: 'desktop', + name: 'desktop-1440', use: { viewport: { width: 1440, height: 900 }, }, }, { - name: 'tablet', + name: 'desktop-1280', + use: { + viewport: { width: 1280, height: 800 }, + }, + }, + { + name: 'tablet-1024', + use: { + viewport: { width: 1024, height: 768 }, + }, + }, + { + name: 'tablet-768', use: { viewport: { width: 768, height: 1024 }, }, }, { - name: 'mobile', + name: 'mobile-390', use: { viewport: { width: 390, height: 844 }, }, }, ], webServer: { - command: 'npm run dev', - url: 'http://localhost:5173', + command: 'npx vite preview --port 5173 --host 127.0.0.1', + url: 'http://127.0.0.1:5173', reuseExistingServer: !process.env.CI, - timeout: 120000, + timeout: 30000, }, }); diff --git a/src/app/router/index.tsx b/src/app/router/index.tsx index 3dd545c..1786b7e 100644 --- a/src/app/router/index.tsx +++ b/src/app/router/index.tsx @@ -25,6 +25,7 @@ import { CardLayoutBuilderPage, ViewBuilderPage, CustomEntityRoutePage, + ProcurementPage, ForbiddenPage, NotFoundPage, } from '../../pages'; @@ -47,6 +48,7 @@ export const router = createBrowserRouter([ { path: 'crm/*', element: }, { path: 'sales/*', element: }, { path: 'inventory/*', element: }, + { path: 'procurement/*', element: }, { path: 'finance/*', element: }, { path: 'employees/*', element: }, { path: 'communications/*', element: }, diff --git a/src/app/shell/Sidebar.tsx b/src/app/shell/Sidebar.tsx index f9a9c89..17a100e 100644 --- a/src/app/shell/Sidebar.tsx +++ b/src/app/shell/Sidebar.tsx @@ -22,6 +22,7 @@ import { Sparkles, Grid, Settings, + Truck, ChevronLeft, ChevronRight, } from 'lucide-react'; @@ -32,6 +33,7 @@ const ICON_MAP: Record = { Users: , ShoppingBag: , Boxes: , + Truck: , BadgeDollarSign: , UserCheck: , MessageSquareText: , @@ -48,22 +50,17 @@ const ICON_MAP: Record = { export const Sidebar: React.FC = () => { const [collapsed, setCollapsed] = useState(false); - const { currentRoleId, getActiveCapabilities } = useDemoStore(); + const { currentRoleId } = useDemoStore(); const { entities } = useMetadata(); const customEntities = entities.filter((e) => !e.isSystem); - const capabilities = getActiveCapabilities(); const role = SYSTEM_ROLES[currentRoleId]; - // Filter modules by role permissions and active capabilities + // Filter modules by role permissions (industry profile does not hide modules) const visibleModules = SYSTEM_MODULES.filter((m) => { // Role check if (!role.allowedModules.includes('*') && !role.allowedModules.includes(m.id)) { return false; } - // Capability check - if (m.requiredCapability && !capabilities[m.requiredCapability]) { - return false; - } return true; }); diff --git a/src/core/modules/registry.ts b/src/core/modules/registry.ts index 0187c05..6f94449 100644 --- a/src/core/modules/registry.ts +++ b/src/core/modules/registry.ts @@ -66,6 +66,18 @@ export const SYSTEM_MODULES: ModuleDefinition[] = [ { id: 'audit', label: 'Инвентаризация', route: '/inventory/audit' }, ], }, + { + id: 'procurement', + label: 'Закупки', + iconName: 'Truck', + route: '/procurement', + sections: [ + { id: 'orders', label: 'Заказы поставщикам', route: '/procurement' }, + { id: 'vendors', label: 'База поставщиков', route: '/procurement?tab=vendors' }, + { id: 'contracts', label: 'Договоры & Условия', route: '/procurement?tab=contracts' }, + { id: 'invoices', label: 'Входящие накладные', route: '/procurement?tab=invoices' }, + ], + }, { id: 'finance', label: 'Финансы', diff --git a/src/features/catalog/ModuleCard.tsx b/src/features/catalog/ModuleCard.tsx new file mode 100644 index 0000000..0dee831 --- /dev/null +++ b/src/features/catalog/ModuleCard.tsx @@ -0,0 +1,151 @@ +import React from 'react'; +import { CatalogModuleItem, READINESS_CONFIG, AVAILABILITY_CONFIG } from './catalogData'; +import { MaturityBadge } from '../../shared/ui/MaturityBadge'; +import { Badge } from '../../shared/ui/Badge'; +import { Button } from '../../shared/ui/Button'; +import { + Users, + ShoppingBag, + Boxes, + Truck, + Workflow, + UserCheck, + MessageSquareText, + Headset, + CheckSquare, + BadgeDollarSign, + Megaphone, + BarChart3, + Cpu, + Sparkles, + Globe, + Settings, + Grid, + ArrowRight, + Info, + Check, +} from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; + +const ICON_COMPONENTS: Record = { + Users: , + ShoppingBag: , + Boxes: , + Truck: , + Workflow: , + UserCheck: , + MessageSquareText: , + Headset: , + CheckSquare: , + BadgeDollarSign: , + Megaphone: , + BarChart3: , + Cpu: , + Sparkles: , + Globe: , + Settings: , + Grid: , +}; + +export interface ModuleCardProps { + module: CatalogModuleItem; + onOpenDetails: (module: CatalogModuleItem) => void; + isRecommended?: boolean; +} + +export const ModuleCard: React.FC = ({ + module, + onOpenDetails, + isRecommended = false, +}) => { + const navigate = useNavigate(); + const readiness = READINESS_CONFIG[module.readiness]; + const availability = AVAILABILITY_CONFIG[module.availability]; + + const handleOpen = () => { + navigate(module.route); + }; + + return ( +
+
+ {/* Top meta bar: category + badges */} +
+
+
+ {ICON_COMPONENTS[module.iconName] || } +
+ {isRecommended && ( + + ★ Рекомендовано + + )} +
+ +
+ + + {availability.label} + +
+
+ + {/* Title and description */} +
+

+ {module.title} +

+

+ {module.description} +

+
+ + {/* Features list */} +
+ + Ключевые возможности: + + {module.features.slice(0, 3).map((f, i) => ( +
+ + {f} +
+ ))} +
+
+ + {/* Action buttons */} +
+ + + +
+
+ ); +}; diff --git a/src/features/catalog/ModuleInfoModal.tsx b/src/features/catalog/ModuleInfoModal.tsx new file mode 100644 index 0000000..7909b37 --- /dev/null +++ b/src/features/catalog/ModuleInfoModal.tsx @@ -0,0 +1,175 @@ +import React, { useEffect } from 'react'; +import { CatalogModuleItem, READINESS_CONFIG, AVAILABILITY_CONFIG } from './catalogData'; +import { Button } from '../../shared/ui/Button'; +import { Badge } from '../../shared/ui/Badge'; +import { MaturityBadge } from '../../shared/ui/MaturityBadge'; +import { + X, + ArrowRight, + CheckCircle2, + Layers, + Link2, + ShieldCheck, + Target, +} from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; + +export interface ModuleInfoModalProps { + module: CatalogModuleItem | null; + isOpen: boolean; + onClose: () => void; +} + +export const ModuleInfoModal: React.FC = ({ module, isOpen, onClose }) => { + const navigate = useNavigate(); + + // Escape key handler + useEffect(() => { + if (!isOpen) return; + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [isOpen, onClose]); + + if (!isOpen || !module) return null; + + const readinessInfo = READINESS_CONFIG[module.readiness]; + const availabilityInfo = AVAILABILITY_CONFIG[module.availability]; + + const handleOpenModule = () => { + onClose(); + navigate(module.route); + }; + + return ( +
+ {/* Backdrop */} +
+ + {/* Modal Card */} +
+ {/* Header */} +
+
+
+ + Информация о модуле + + + + {availabilityInfo.label} + +
+

+ {module.title} +

+

+ {module.description} +

+
+ + +
+ + {/* Section 1: Purpose */} +
+
+ + 1. Назначение модуля +
+
+ {module.details.purpose} +
+
+ + {/* Section 2: Expected Capabilities */} +
+
+ + 2. Предполагаемые и реализованные возможности +
+
+ {module.details.expectedCapabilities.map((cap, idx) => ( +
+ + {cap} +
+ ))} +
+
+ + {/* Section 3: Current Status */} +
+
+ + 3. Текущий статус готовности в платформе +
+
+ {readinessInfo.label}: {module.details.currentStatus} +
+
+ + {/* Section 4: Planned Integrations */} +
+
+ + 4. Планируемые интеграции и API-коннекторы +
+
+ {module.details.plannedIntegrations.map((integ, idx) => ( + + {integ} + + ))} +
+
+ + {/* Footer Actions */} +
+ + Маршрут в системе: {module.route} + + +
+ + +
+
+
+
+ ); +}; diff --git a/src/features/catalog/ModulesCatalog.tsx b/src/features/catalog/ModulesCatalog.tsx new file mode 100644 index 0000000..ca692db --- /dev/null +++ b/src/features/catalog/ModulesCatalog.tsx @@ -0,0 +1,486 @@ +import React, { useState, useMemo } from 'react'; +import { useDemoStore } from '../../mock/store'; +import { BUSINESS_PROFILES } from '../../core/profiles/profiles'; +import { + CATALOG_MODULES, + CATALOG_CATEGORIES, + CatalogModuleItem, +} from './catalogData'; +import { ModuleCard } from './ModuleCard'; +import { ModuleInfoModal } from './ModuleInfoModal'; +import { + Search, + X, + Sparkles, + Layers, + Filter, + Building, + RotateCcw, + BarChart3, + Grid, +} from 'lucide-react'; +import { Button } from '../../shared/ui/Button'; +import { DynamicDashboard } from '../dashboard/DynamicDashboard'; + +export const ModulesCatalog: React.FC = () => { + const { + currentProfileId, + catalogProfileFilter, + setCatalogProfileFilter, + catalogCategoryFilter, + setCatalogCategoryFilter, + catalogStatusFilter, + setCatalogStatusFilter, + catalogSearchQuery, + setCatalogSearchQuery, + resetCatalogFilters, + } = useDemoStore(); + + const [activeViewMode, setActiveViewMode] = useState<'catalog' | 'dashboard'>('catalog'); + const [selectedModuleForModal, setSelectedModuleForModal] = useState(null); + + const activeProfile = BUSINESS_PROFILES[currentProfileId] || BUSINESS_PROFILES.restaurant_hall_delivery; + + // Search and Category filtering + const filteredModules = useMemo(() => { + return CATALOG_MODULES.filter((m) => { + // Category filter + if (catalogCategoryFilter !== 'all' && m.category !== catalogCategoryFilter) { + return false; + } + + // Status filter + if (catalogStatusFilter !== 'all' && m.readiness !== catalogStatusFilter) { + return false; + } + + // Search filter + if (catalogSearchQuery.trim()) { + const q = catalogSearchQuery.toLowerCase(); + const matchTitle = m.title.toLowerCase().includes(q); + const matchDesc = m.description.toLowerCase().includes(q); + const matchFeatures = m.features.some((f) => f.toLowerCase().includes(q)); + const matchCategory = CATALOG_CATEGORIES.find((c) => c.id === m.category)?.label.toLowerCase().includes(q); + if (!matchTitle && !matchDesc && !matchFeatures && !matchCategory) { + return false; + } + } + + return true; + }); + }, [catalogCategoryFilter, catalogStatusFilter, catalogSearchQuery]); + + // Split into recommended vs other when a specific profile or "recommended" is active + const isProfileFilterActive = catalogProfileFilter !== 'all'; + const effectiveProfileId = + catalogProfileFilter === 'recommended' ? currentProfileId : catalogProfileFilter; + + const { recommendedList, otherList } = useMemo(() => { + if (!isProfileFilterActive) { + return { recommendedList: [], otherList: filteredModules }; + } + + const rec: CatalogModuleItem[] = []; + const oth: CatalogModuleItem[] = []; + + for (const m of filteredModules) { + if (m.recommendedProfiles.includes(effectiveProfileId)) { + rec.push(m); + } else { + oth.push(m); + } + } + + return { recommendedList: rec, otherList: oth }; + }, [isProfileFilterActive, effectiveProfileId, filteredModules]); + + const stats = useMemo(() => { + const ready = CATALOG_MODULES.filter((m) => m.readiness === 'ready').length; + const target = CATALOG_MODULES.filter((m) => m.readiness === 'ui_prepared').length; + const planned = CATALOG_MODULES.filter((m) => m.readiness === 'planned').length; + const service = CATALOG_MODULES.filter((m) => m.readiness === 'service_required').length; + return { total: CATALOG_MODULES.length, ready, target, planned, service }; + }, []); + + return ( +
+ {/* View Mode Switcher Header: Catalog vs KPI Dashboard */} +
+
+
+ +
+
+ + Обзор платформы Krona Business OS + +
+ Единый каталог всех 18 модулей и функциональных блоков +
+
+
+ + {/* Tab switch between Catalog and Dashboard */} +
+ + +
+
+ + {/* If Dashboard mode is active, render DynamicDashboard */} + {activeViewMode === 'dashboard' ? ( + + ) : ( + /* Catalog Mode */ +
+ {/* Main Hero Header */} +
+
+
+
+ + Все возможности бизнес-платформы +
+

+ Единый каталог модулей Krona +

+

+ Платформа открыта для изучения целиком без предварительного выбора отрасли. Исследуйте Pilot 1 транзакции, интерактивные прототипы и перспективные блоки экосистемы. +

+
+ + {/* Stats badges */} +
+
+
{stats.ready}
+
Работает в демо
+
+
+
{stats.target}
+
Интерфейс готов
+
+
+
{stats.planned}
+
Запланирован
+
+
+
{stats.service}
+
Сервисные
+
+
+
+ + {/* Live Search Input */} +
+ + setCatalogSearchQuery(e.target.value)} + placeholder="Быстрый поиск по названию, возможностям или описанию модуля..." + data-testid="catalog-search-input" + className="w-full pl-10 pr-10 py-2.5 text-xs bg-krona-workspace border border-krona-border rounded-xl text-krona-primary placeholder:text-krona-secondary focus:outline-none focus:border-krona-gold shadow-inner" + /> + {catalogSearchQuery && ( + + )} +
+
+ + {/* Industry Profiles Filter Bar */} +
+
+
+ + + Отраслевой фокус (не скрывает остальные модули): + +
+ {isProfileFilterActive && ( + + )} +
+ +
+ {/* Все модули (По умолчанию) */} + + + {/* Рекомендовано для текущей отрасли */} + + + {/* Specific profiles */} + {Object.values(BUSINESS_PROFILES).map((p) => ( + + ))} +
+
+ + {/* Categories and Status Filters Bar */} +
+ {/* Categories horizontal selector */} +
+ {CATALOG_CATEGORIES.map((cat) => { + const isSelected = catalogCategoryFilter === cat.id; + const count = + cat.id === 'all' + ? CATALOG_MODULES.length + : CATALOG_MODULES.filter((m) => m.category === cat.id).length; + + return ( + + ); + })} +
+ + {/* Status & Reset filter row */} +
+
+
+ + Статус готовности: +
+ +
+ {[ + { id: 'all', label: 'Все статусы' }, + { id: 'ready', label: 'Работает в демо' }, + { id: 'ui_prepared', label: 'Интерфейс подготовлен' }, + { id: 'planned', label: 'Запланирован' }, + { id: 'service_required', label: 'Требует подключения' }, + ].map((st) => ( + + ))} +
+
+ + {(catalogCategoryFilter !== 'all' || + catalogStatusFilter !== 'all' || + catalogSearchQuery || + catalogProfileFilter !== 'all') && ( + + )} +
+
+ + {/* Module Cards Sections */} + {isProfileFilterActive ? ( + /* When an industry profile is selected: 2 sections */ +
+ {/* Section 1: Recommended */} +
+
+
+ +

+ Рекомендовано для вашей отрасли ({ + catalogProfileFilter === 'recommended' + ? activeProfile.name + : BUSINESS_PROFILES[catalogProfileFilter]?.name || 'Выбранная отрасль' + }) +

+ + {recommendedList.length} + +
+
+ + {recommendedList.length > 0 ? ( +
+ {recommendedList.map((mod) => ( + + ))} +
+ ) : ( +
+ По текущим фильтрам нет рекомендованных модулей +
+ )} +
+ + {/* Section 2: Other platform capabilities */} +
+
+
+ +

+ Все остальные возможности платформы (доступны для подключения) +

+ + {otherList.length} + +
+
+ + {otherList.length > 0 ? ( +
+ {otherList.map((mod) => ( + + ))} +
+ ) : ( +
+ По текущим фильтрам нет дополнительных модулей +
+ )} +
+
+ ) : ( + /* Unified Grid of All Modules */ +
+
+
+ +

+ Все модули платформы Krona +

+ + Найдено: {filteredModules.length} + +
+
+ + {filteredModules.length > 0 ? ( +
+ {filteredModules.map((mod) => ( + + ))} +
+ ) : ( +
+

По запросу «{catalogSearchQuery}» ничего не найдено.

+ +
+ )} +
+ )} +
+ )} + + {/* Module Info Modal */} + setSelectedModuleForModal(null)} + /> +
+ ); +}; diff --git a/src/features/catalog/catalogData.ts b/src/features/catalog/catalogData.ts new file mode 100644 index 0000000..ff2b628 --- /dev/null +++ b/src/features/catalog/catalogData.ts @@ -0,0 +1,747 @@ +export type ModuleCategory = + | 'sales' // Клиенты и продажи + | 'operations' // Операционная работа + | 'inventory' // Товары и закупки + | 'employees' // Сотрудники + | 'communications' // Коммуникации + | 'finance' // Финансы + | 'marketing' // Продвижение + | 'analytics' // Управление и аналитика + | 'settings'; // Настройки платформы + +export type ReadinessStatus = 'ready' | 'ui_prepared' | 'planned' | 'service_required'; +export type AvailabilityStatus = 'connected' | 'demo' | 'in_development' | 'requires_setup'; + +export interface ModuleInfoDetails { + purpose: string; + expectedCapabilities: string[]; + currentStatus: string; + plannedIntegrations: string[]; +} + +export interface CatalogModuleItem { + id: string; + title: string; + category: ModuleCategory; + description: string; + features: string[]; + readiness: ReadinessStatus; + availability: AvailabilityStatus; + route: string; + iconName: string; + recommendedProfiles: string[]; + details: ModuleInfoDetails; +} + +export const CATALOG_CATEGORIES: { id: ModuleCategory | 'all'; label: string }[] = [ + { id: 'all', label: 'Все категории' }, + { id: 'sales', label: 'Клиенты и продажи' }, + { id: 'operations', label: 'Операционная работа' }, + { id: 'inventory', label: 'Товары и закупки' }, + { id: 'employees', label: 'Сотрудники' }, + { id: 'communications', label: 'Коммуникации' }, + { id: 'finance', label: 'Финансы' }, + { id: 'marketing', label: 'Продвижение' }, + { id: 'analytics', label: 'Управление и аналитика' }, + { id: 'settings', label: 'Настройки платформы' }, +]; + +export const READINESS_CONFIG: Record = { + ready: { + label: 'Работает в демо', + maturity: 'pilot1', + color: 'emerald', + description: 'Полный интерактивный сценарий с хранением в IndexedDB и транзакционной логикой', + }, + ui_prepared: { + label: 'Интерфейс подготовлен', + maturity: 'target', + color: 'sky', + description: 'Интерактивный целевой интерфейс с бизнес-логикой и демонстрационными данными', + }, + planned: { + label: 'Запланирован', + maturity: 'planned', + color: 'amber', + description: 'Архитектура спроектирована, разработка в дорожной карте (Q4)', + }, + service_required: { + label: 'Требует подключения', + maturity: 'demo', + color: 'purple', + description: 'Работает в режиме эмуляции, требует подключения внешнего провайдера/LLM', + }, +}; + +export const AVAILABILITY_CONFIG: Record = { + connected: { label: 'Подключён', variant: 'green' }, + demo: { label: 'Демо-режим', variant: 'blue' }, + in_development: { label: 'В разработке', variant: 'warning' }, + requires_setup: { label: 'Требует настройки', variant: 'gray' }, +}; + +export const CATALOG_MODULES: CatalogModuleItem[] = [ + // 1. CRM + { + id: 'crm', + title: 'CRM и клиенты', + category: 'sales', + description: 'Управление базой контактов, компаний, лидов и сквозная воронка продаж в Kanban-виде.', + features: [ + 'Воронка сделок с этапами и вероятностью (Kanban Drag & Drop)', + 'Карточка «Клиент 360°» с таймлайном, сделками и заказами', + 'Конвертация лидов в контакты и связанные сделки в один клик', + 'Мастер импорта клиентских баз из CSV с маппингом полей', + ], + readiness: 'ready', + availability: 'connected', + route: '/crm', + iconName: 'Users', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'restaurant_delivery_only', + 'auto_service', + 'retail_store', + 'beauty_salon', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Централизованное управление отношениями с контрагентами, ведение лидов до победной сделки и полная история взаимодействия.', + expectedCapabilities: [ + 'Омниканальный сбор входящих лидов из сайтов и мессенджеров', + 'Интерактивная канбан-доска с расчетом взвешенной суммы этапов', + 'Сквозное создание заказа в Коммерции сразу при переводе в Won', + 'Фильтрация, кастомные сегменты и экспорт в Excel/CSV', + ], + currentStatus: 'Pilot 1: полностью реализован, хранение в IndexedDB, сквозной транзакционный контур.', + plannedIntegrations: ['Телефония (Asterisk/Mango)', 'WhatsApp Business API', 'Telegram Bot API', 'Сервисы обогащения по ИНН (DaData)'], + }, + }, + + // 2. Sales + { + id: 'sales', + title: 'Продажи и заказы', + category: 'sales', + description: 'Управление коммерческими заказами, счетами, оплатами и статусами отгрузки со склада.', + features: [ + 'Жизненный цикл заказа: Подтверждение → Оплата → Отгрузка', + 'Автоматическое резервирование товара на складе при подтверждении', + 'Фиксация оплат и автоформирование входящего денежного потока (ДДС)', + 'Обработка возвратов с восстановлением остатка и корректировкой кассы', + ], + readiness: 'ready', + availability: 'connected', + route: '/sales', + iconName: 'ShoppingBag', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'restaurant_delivery_only', + 'auto_service', + 'retail_store', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Обеспечение безошибочного исполнения сделок, взаиморасчетов с покупателями и контроля складских резервов.', + expectedCapabilities: [ + 'Создание и редактирование многопозиционных заказов с НДС и скидками', + 'Генерация фискальных чеков и печатных форм счетов (УПД/ТОРГ-12)', + 'Синхронизация складских резервов в реальном времени', + 'Контроль дебиторской задолженности и отсрочек платежей', + ], + currentStatus: 'Pilot 1: полностью реализован, связан со складом и финансами.', + plannedIntegrations: ['Банковский эквайринг (Сбер, Тинькофф)', 'Онлайн-кассы (54-ФЗ, Атол, Эвотор)', 'Службы доставки (СДЭК, Яндекс Доставка)'], + }, + }, + + // 3. Inventory + { + id: 'inventory', + title: 'Товары и склад', + category: 'inventory', + description: 'Многоскладской партионный учет, адресное хранение, приемка, перемещения и инвентаризация.', + features: [ + 'Раздельный учет: физический остаток, резервы и доступно к продаже', + 'Оформление доприемки товаров с пересчетом себестоимости', + 'Журнал складских перемещений между торговыми точками', + 'Акты инвентаризации и списаний с фиксацией в аудит-логе', + ], + readiness: 'ready', + availability: 'connected', + route: '/inventory', + iconName: 'Boxes', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'restaurant_delivery_only', + 'auto_service', + 'retail_store', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Предотвращение пересортицы и кассовых разрывов за счет прозрачного учета складских запасов в реальном времени.', + expectedCapabilities: [ + 'Адресное хранение по ячейкам, зонам и стеллажам', + 'Партионный учет по срокам годности (FIFO) и серийным номерам', + 'Мобильный сканер штрихкодов для быстрой инвентаризации', + 'Автоматический расчет средней себестоимости списаний', + ], + currentStatus: 'Pilot 1: полностью реализован, хранение в IndexedDB, реактивная синхронизация.', + plannedIntegrations: ['ТСД (Терминалы сбора данных)', 'Национальная система маркировки Честный ЗНАК', 'Весовое оборудование'], + }, + }, + + // 4. Procurement + { + id: 'procurement', + title: 'Закупки и поставщики', + category: 'inventory', + description: 'Управление базой поставщиков, договорами, графиками поставок и автозаказом по неснижаемым остаткам.', + features: [ + 'Реестр поставщиков с реквизитами, прайс-листами и договорами', + 'Заявки на закупку (PO) с контролем согласования бюджетов', + 'Автоматическое формирование заказов по точкам неснижаемого остатка', + 'Входящие накладные и сверка расхождений цен и объемов', + ], + readiness: 'planned', + availability: 'in_development', + route: '/procurement', + iconName: 'Truck', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'retail_store', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Бесперебойное снабжение бизнеса сырьем и товарами по лучшим ценам с исключением ошибок ручного заказа.', + expectedCapabilities: [ + 'Автозаказ по алгоритму прогнозирования темпов расхода', + 'Контроль задолженности перед поставщиками и графиков оплат', + 'Автоматический прием электронных накладных через ЭДО', + 'Оценка надежности и рейтинга поставщиков по срокам поставок', + ], + currentStatus: 'Запланирован: архитектурная спецификация утверждена, интерфейс прототипируется, релиз Q4.', + plannedIntegrations: ['Диадок / СБИС (ЭДО)', '1С:Предприятие 8.3', 'Банк-Клиент (DirectBank)', 'ГИС Меркурий (ВетИС)'], + }, + }, + + // 5. Operations + { + id: 'operations', + title: 'Операционная работа', + category: 'operations', + description: 'Рабочие места кассиров (POS), экраны кухни (KDS), бронирование столов и заказ-наряды автосервиса.', + features: [ + 'Сенсорный кассовый терминал с быстрой модификацией позиций и чеками', + 'Кухонный монитор (KDS) со станциями готовки и таймерами ожидания', + 'Интерактивная карта залов, бронирование столов и предчеки', + 'Управление постами, заказ-нарядами и мастерами автосервиса', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/operations', + iconName: 'Workflow', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'restaurant_delivery_only', + 'auto_service', + 'retail_store', + 'beauty_salon', + ], + details: { + purpose: 'Автоматизация фронт-офиса и операционного персонала на передовой линии обслуживания клиентов.', + expectedCapabilities: [ + 'Работа в офлайн-режиме с последующей синхронизацией чеков', + 'Разделение чеков по гостям и смешанная оплата (нал + карта)', + 'Звуковые оповещения KDS о поступлении новых блюд в цех', + 'Учет выработки мастеров и нормо-часов в заказ-нарядах', + ], + currentStatus: 'Target prototype: интерактивные POS, KDS и карта столов с живой бизнес-логикой.', + plannedIntegrations: ['Фискальные регистраторы (Штрих-М, Атол)', 'Банковские пин-пады', 'Кухонные принтеры чеков (ESC/POS)'], + }, + }, + + // 6. Employees + { + id: 'employees', + title: 'Сотрудники и смены', + category: 'employees', + description: 'Управление персоналом, интерактивный график смен, контроль переработок и учет рабочего времени.', + features: [ + 'Планировщик смен с удобной недельной и месячной сеткой', + 'Автоматическое обнаружение конфликтов графика (>12 ч подряд)', + 'Учет ролей, квалификаций, часовых ставок и выходов', + 'Табель учета фактически отработанного времени (Т-13)', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/employees', + iconName: 'UserCheck', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'auto_service', + 'retail_store', + 'beauty_salon', + 'production_workshop', + ], + details: { + purpose: 'Оптимизация фонда оплаты труда, справедливое планирование смен и соблюдение норм трудового распорядка.', + expectedCapabilities: [ + 'Самозапись сотрудников на открытые смены через чат-бот', + 'Фиксация прихода/ухода по пин-коду или биометрии планшета', + 'Автоматический расчет авансов и премий от личной выручки', + 'Контроль медицинских книжек и допусков к работе', + ], + currentStatus: 'Target prototype: интерактивный планировщик смен с защитой от переработок.', + plannedIntegrations: ['1С:ЗУП (Зарплата и управление персоналом)', 'СКУД (Турникеты)', 'Telegram-бот для персонала'], + }, + }, + + // 7. Communications + { + id: 'communications', + title: 'Коммуникации и Inbox', + category: 'communications', + description: 'Единый омниканальный центр диалогов: Telegram, WhatsApp, онлайн-чат сайта и внутренние чаты команды.', + features: [ + 'Единая лента входящих сообщений (Unified Inbox)', + 'Интерактивная симуляция ответа клиенту с моментальной доставкой', + 'Фильтрация диалогов по каналам, статусу и ответственным', + 'Передача переписки между операторами без потери контекста', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/communications', + iconName: 'MessageSquareText', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'auto_service', + 'retail_store', + 'beauty_salon', + 'wholesale_trade', + ], + details: { + purpose: 'Ни одно обращение клиента не теряется: объединение всех цифровых каналов в одном удобном окне оператора.', + expectedCapabilities: [ + 'Быстрые шаблоны ответов с автоподстановкой имени и заказа', + 'Автоматическое создание лида в CRM из нового диалога', + 'Поддержка медиафайлов: фото чеков, голосовые, накладные', + 'Маршрутизация чатов по квалификации дежурных менеджеров', + ], + currentStatus: 'Target prototype: омниканальный inbox с тестовой симуляцией отправки сообщений.', + plannedIntegrations: ['Telegram Bot API', 'WhatsApp Cloud API / WABA', 'ВКонтакте Сообщения', 'Web-виджет чата'], + }, + }, + + // 8. HelpDesk + { + id: 'helpdesk', + title: 'HelpDesk и поддержка', + category: 'communications', + description: 'Система тикетов поддержки клиентов, контроль соблюдения SLA, база знаний и внутренние заметки.', + features: [ + 'Очередь тикетов с таймерами дедлайнов и контролем SLA', + 'Разделение публичной переписки с клиентом и приватных заметок', + 'Категоризация инцидентов по приоритету (Critical, High, Medium)', + 'Аналитика скорости первого ответа и решения проблем', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/helpdesk', + iconName: 'Headset', + recommendedProfiles: [ + 'wholesale_trade', + 'retail_store', + 'auto_service', + 'production_workshop', + ], + details: { + purpose: 'Стандартизация сервисного обслуживания, устранение жалоб и повышение индекса удовлетворенности клиентов (CSAT).', + expectedCapabilities: [ + 'Автоэскалация просроченных тикетов старшему администратору', + 'База знаний готовых регламентов и инструкций для операторов', + 'Оценка качества обслуживания клиентом после закрытия заявки', + 'Привязка тикета к конкретному заказу, сделке или отгрузке', + ], + currentStatus: 'Target prototype: рабочая очередь обращений с расчетом SLA и заметками.', + plannedIntegrations: ['E-mail (IMAP/SMTP)', 'Клиентский портал самообслуживания', 'Telegram HelpDesk бот'], + }, + }, + + // 9. Tasks + { + id: 'tasks', + title: 'Задачи и проекты', + category: 'analytics', + description: 'Командный таск-трекер с канбан-доской, списками подзадач, дедлайнами и связкой с клиентами.', + features: [ + 'Канбан-доска задач по стадиям (К выполнению, В работе, Готово)', + 'Интерактивные чек-листы подзадач внутри каждой карточки', + 'Контроль дедлайнов с подсветкой просроченных поручений', + 'Привязка задач к клиентам CRM и коммерческим сделкам', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/tasks', + iconName: 'CheckSquare', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'auto_service', + 'wholesale_trade', + 'production_workshop', + 'beauty_salon', + ], + details: { + purpose: 'Прозрачное распределение обязанностей в команде и строгий контроль выполнения регулярных регламентов.', + expectedCapabilities: [ + 'Шаблоны регулярных повторяющихся задач (чек-листы открытия смены)', + 'Диаграмма Ганта для крупных проектов запуска новых точек', + 'Учет трудозатрат (тайм-трекер) по каждой задаче', + 'Push-уведомления исполнителям о назначении и приближении дедлайна', + ], + currentStatus: 'Target prototype: функциональная канбан-доска задач с чек-листами подзадач.', + plannedIntegrations: ['Календари (Google, Yandex, Apple iCal)', 'Telegram-уведомления', 'Krona Automation'], + }, + }, + + // 10. Finance + { + id: 'finance', + title: 'Финансы и ДДС', + category: 'finance', + description: 'Управленческий учет движения денег (ДДС), расчет прибылей и убытков (P&L) и контроль кассовых смен.', + features: [ + 'Отчет о ДДС (Cash Flow) в реальном времени по счетам и статьям', + 'Расчет операционной прибыли (P&L) с маржинальностью бизнеса', + 'Автоматическое зачисление платежей из оплаченных заказов', + 'Контроль кассовых смен, инкассаций и остатков в сейфах', + ], + readiness: 'ready', + availability: 'connected', + route: '/finance', + iconName: 'BadgeDollarSign', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'restaurant_delivery_only', + 'auto_service', + 'retail_store', + 'beauty_salon', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Защита от кассовых разрывов, понимание чистой прибыли и абсолютный финансовый контроль каждого рубля бизнеса.', + expectedCapabilities: [ + 'Многоуровневый справочник статей доходов и расходов (DDS)', + 'Платежный календарь для согласования плановых расходов', + 'Учет нескольких юрлиц, расчетных счетов и касс', + 'Автоматический импорт банковских выписок (1С/Банк-Клиент)', + ], + currentStatus: 'Pilot 1: полностью реализован, связан с заказами продаж и возвратами.', + plannedIntegrations: ['Прямой шлюз Банк-Клиент (DirectBank)', 'Онлайн-бухгалтерия (Контур.Эльба, Моё Дело)', 'Эквайринг'], + }, + }, + + // 11. Marketing + { + id: 'marketing', + title: 'Маркетинг и промо', + category: 'marketing', + description: 'Сегментация клиентов, акции, скидки, промокоды и симуляция запуска промо-кампаний.', + features: [ + 'Динамические когорты клиентов (Новые, Постоянные, В зоне риска)', + 'Конструктор промо-акций: проценты, фиксированные суммы, подарки', + 'Симулятор отправки рассылок с метриками доставки и конверсии', + 'Аналитика влияния акций на средний чек и повторные визиты', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/marketing', + iconName: 'Megaphone', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'retail_store', + 'beauty_salon', + 'auto_service', + ], + details: { + purpose: 'Привлечение новых клиентов и удержание существующей базы с помощью персонализированных предложений.', + expectedCapabilities: [ + 'RFM-анализ базы клиентов с автоматическим присвоением статусов', + 'Триггерные механики: поздравление с днем рождения, «мы скучаем»', + 'Интеграция с бонусной программой лояльности (кешбэк баллами)', + 'Оценка ROI каждого маркетингового канала и затрат на привлечение', + ], + currentStatus: 'Target prototype: сегменты, промокоды и симулятор промо-кампаний.', + plannedIntegrations: ['SMS-шлюзы', 'Telegram / WhatsApp рассылки', 'Программы лояльности', 'Яндекс Метрика'], + }, + }, + + // 12. Analytics + { + id: 'analytics', + title: 'Аналитика и BI', + category: 'analytics', + description: 'Бизнес-аналитика, наглядные дашборды выручки, динамика чеков и ABC/XYZ матрица номенклатуры.', + features: [ + 'Интерактивные графики выручки и среднего чека по периодам', + 'Матрица ABC-анализа: выявление ключевых драйверов прибыли', + 'Когортный анализ возвращаемости клиентов (Retention)', + 'Сравнение показателей по торговым точкам и сменам', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/analytics', + iconName: 'BarChart3', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'restaurant_delivery_only', + 'auto_service', + 'retail_store', + 'beauty_salon', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Принятие управленческих решений на основе точных цифр, графиков и предиктивных трендов.', + expectedCapabilities: [ + 'Настраиваемый конструктор виджетов и ключевых показателей (KPI)', + 'Выявление скрытых потерь, списаний и убыточных позиций меню', + 'План-фактный анализ выполнения целей продаж', + 'Экспорт управленческих отчетов в PDF и Excel', + ], + currentStatus: 'Target prototype: графики Recharts, фильтр периодов и расчет ABC-матрицы.', + plannedIntegrations: ['Внешние BI-системы (PowerBI, Apache Superset)', 'Data Warehouse (ClickHouse, BigQuery)'], + }, + }, + + // 13. Automation + { + id: 'automation', + title: 'Автоматизация Workflows', + category: 'analytics', + description: 'No-Code конструктор сценариев: автоматические цепочки действий при событиях в платформе.', + features: [ + 'Визуальный конструктор сценариев (Триггер → Условие → Действие)', + 'Готовые отраслевые шаблоны (уведомления об остатках, автозадачи)', + 'Интерактивный запуск тестового прогона с логом выполнения', + 'История срабатываний и аудит автоматических изменений', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/automation', + iconName: 'Cpu', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'auto_service', + 'retail_store', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Освобождение сотрудников от рутины за счет автоматизации стандартных бизнес-процессов.', + expectedCapabilities: [ + 'Триггеры по изменению статусов заказов, времени, порогам остатков', + 'Действия: отправка SMS/Telegram, смена статуса сделки, создание задачи', + 'Ветвление логики IF/ELSE с проверкой любых полей сущностей', + 'Webhook-триггеры для интеграции с внешними микросервисами', + ], + currentStatus: 'Target prototype: визуальный конструктор цепочек с интерактивным логом шагов.', + plannedIntegrations: ['Webhooks API', 'Zapier / Make совместимость', 'Krona EventBus'], + }, + }, + + // 14. AI Center + { + id: 'ai', + title: 'Krona AI — ИИ-центр', + category: 'analytics', + description: 'Интеллектуальный бизнес-оркестратор и специализированные агенты: Аналитик, Склад, Маркетолог.', + features: [ + 'Контекстный чат с ИИ, знающим актуальные остатки и выручку', + 'Команда агентов: Бизнес-аналитик, Складской агент, Маркетолог', + 'Прозрачный лог вызова инструментов (Tool Calls) и расчетов', + 'Генерация артефактов, отчетов и рекомендаций в модальном окне', + ], + readiness: 'service_required', + availability: 'demo', + route: '/ai', + iconName: 'Sparkles', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'auto_service', + 'retail_store', + 'beauty_salon', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Второй пилот для руководителя: мгновенные инсайты по операционным данным без ручного копания в таблицах.', + expectedCapabilities: [ + 'Автономный мониторинг критических отклонений выручки и запасов', + 'Генерация привлекательных текстов промо-рассылок и постов', + 'Голосовой ввод поручений и создание задач на естественном языке', + 'Строгие границы безопасности без утечки конфиденциальных данных', + ], + currentStatus: 'Демо-режим: интерактивная симуляция работы ИИ-агентов и вызова инструментов.', + plannedIntegrations: ['OpenAI / Anthropic API', 'Google Gemini API', 'Локальные модели vLLM / Ollama'], + }, + }, + + // 15. CMS + { + id: 'cms', + title: 'CMS и Конструктор сайтов', + category: 'marketing', + description: 'Визуальный конструктор посадочных страниц, интернет-витрин и лид-форм с адаптивным превью.', + features: [ + '6 готовых адаптивных блоков: Hero, Каталог/Меню, Преимущества, Отзывы, Форма, Контакты', + 'Переключение экранов предпросмотра (Desktop 1440, Tablet 768, Mobile 390)', + 'Интерактивное редактирование текста, заголовков и цен', + 'Сохранение структуры страницы в локальное состояние', + ], + readiness: 'ui_prepared', + availability: 'demo', + route: '/cms', + iconName: 'Globe', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'restaurant_delivery_only', + 'retail_store', + 'beauty_salon', + 'auto_service', + ], + details: { + purpose: 'Быстрый запуск продающих лендингов и меню доставки без привлечения веб-разработчиков.', + expectedCapabilities: [ + 'Прямая интеграция каталога товаров и цен из складского модуля', + 'Автоматический прием заказов с сайта напрямую в кассу (POS)', + 'Привязка собственного доменного имени и бесплатный SSL', + 'SEO-оптимизация мета-тегов и OpenGraph для соцсетей', + ], + currentStatus: 'Target prototype: 6 типов блоков, переключение адаптивности и экспорт структуры.', + plannedIntegrations: ['DNS-провайдеры', 'CDN для быстрой загрузки медиа', 'Яндекс Метрика'], + }, + }, + + // 16. Entity Builder + { + id: 'entity_builder', + title: 'Конструкторы сущностей и полей', + category: 'settings', + description: 'No-Code студия проектирования данных: создание справочников, 39 типов полей, форм и карточек.', + features: [ + 'Создание кастомных справочников на лету без изменения программного кода', + 'Поддержка 39 специализированных типов полей (включая AST-формулы без eval)', + 'Drag & Drop Form Builder с адаптивным превью и 50 шагами Undo/Redo', + 'Конструктор карточек и табличных представлений с фильтрами', + ], + readiness: 'ready', + availability: 'connected', + route: '/settings/entities', + iconName: 'Settings', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'auto_service', + 'retail_store', + 'beauty_salon', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Полная адаптация платформы под специфику любого бизнеса без ожидания доработок от программистов.', + expectedCapabilities: [ + 'Мгновенная регистрация новых сущностей в навигации и роутинге', + 'Защита системных инвариантов ядра (нельзя сломать базовые поля)', + 'Условная видимость и доступность полей по ролям сотрудников', + 'Экспорт и импорт метамоделей в JSON-формате', + ], + currentStatus: 'Pilot 1: полностью реализован, динамические рендереры форм, карточек и таблиц.', + plannedIntegrations: ['Krona Metadata Registry', 'IndexedDB schema engine', 'REST API Adapter'], + }, + }, + + // 17. Settings & Profiles + { + id: 'settings', + title: 'Настройки и профили бизнеса', + category: 'settings', + description: 'Параметры компании, отраслевые профили (Ресторан, Авто, Розница), роли (RBAC) и бэкапы.', + features: [ + 'Переключение отраслевых конфигураций с автонастройкой терминологии', + 'Ролевая модель (RBAC): Владелец, Управляющий, Кассир, Кладовщик', + 'Резервное копирование: экспорт, сброс и импорт снимков IndexedDB', + 'Управление торговыми точками, локациями и темами оформления', + ], + readiness: 'ready', + availability: 'connected', + route: '/settings', + iconName: 'Settings', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'restaurant_delivery_only', + 'auto_service', + 'retail_store', + 'beauty_salon', + 'wholesale_trade', + 'production_workshop', + ], + details: { + purpose: 'Центральный пульт администрирования компании, управление доступом персонала и безопасность данных.', + expectedCapabilities: [ + 'Настройка прав доступа вплоть до отдельных кнопок и полей', + 'Аудит действий пользователей с фиксацией IP и времени', + 'Персонализация интерфейса под корпоративный брендбук', + 'Экспорт полных резервных копий базы в защищенный архив', + ], + currentStatus: 'Pilot 1: полностью реализован, ролевой контроль доступа (403), снэпшоты IndexedDB.', + plannedIntegrations: ['SSO (Single Sign-On / SAML / OIDC)', 'Active Directory / LDAP', 'Cloud Backup'], + }, + }, + + // 18. Apps + { + id: 'apps', + title: 'Каталог приложений и API', + category: 'settings', + description: 'Каталог расширений, готовые коннекторы к кассам, платежным шлюзам, доставке и внешним сервисам.', + features: [ + 'Каталог готовых отраслевых расширений и интеграций', + 'Управление статусом установленных плагинов организации', + 'Настройка API-ключей, токенов доступа и вебхуков', + 'Модульная архитектура расширения функционала платформы', + ], + readiness: 'service_required', + availability: 'demo', + route: '/apps', + iconName: 'Grid', + recommendedProfiles: [ + 'restaurant_hall_delivery', + 'retail_store', + 'auto_service', + 'wholesale_trade', + ], + details: { + purpose: 'Открытая экосистема: легкое подключение внешних IT-сервисов и сервисов партнеров к бизнес-платформе.', + expectedCapabilities: [ + 'Маркетплейс проверенных плагинов от сторонних разработчиков', + 'Песочница для безопасного тестирования API-запросов', + 'Журнал доставки событий Webhook с авто-повтором при сбоях', + 'Контроль разрешений каждого установленного приложения', + ], + currentStatus: 'Демо-каталог: витрина расширений с симуляцией подключения сервисов.', + plannedIntegrations: ['Krona App Store SDK', 'OpenAPI 3.1 Gateway', 'OAuth 2.0 Authorization Server'], + }, + }, +]; diff --git a/src/features/settings/SettingsModule.tsx b/src/features/settings/SettingsModule.tsx index bb19b17..703c185 100644 --- a/src/features/settings/SettingsModule.tsx +++ b/src/features/settings/SettingsModule.tsx @@ -73,6 +73,7 @@ export const SettingsModule: React.FC = () => { try { setIsResetting(true); await resetDatabase(); + useDemoStore.getState().resetCatalogFilters(); setSnapshotStats('База данных сброшена к начальному состоянию'); showToast('База данных успешно сброшена к начальному состоянию!', 'info'); } catch (err) { diff --git a/src/mock/store.ts b/src/mock/store.ts index 325b717..ee12754 100644 --- a/src/mock/store.ts +++ b/src/mock/store.ts @@ -155,6 +155,17 @@ interface DemoStore { dismissNotification: (id: string) => void; markAllNotificationsRead: () => void; + // Catalog filters (Default: 'all' - Все модули) + catalogProfileFilter: string; + setCatalogProfileFilter: (filter: string) => void; + catalogCategoryFilter: string; + setCatalogCategoryFilter: (category: string) => void; + catalogStatusFilter: string; + setCatalogStatusFilter: (status: string) => void; + catalogSearchQuery: string; + setCatalogSearchQuery: (query: string) => void; + resetCatalogFilters: () => void; + // Helper getters getActiveProfile: () => BusinessProfile; getActiveCapabilities: () => Record; @@ -544,6 +555,23 @@ export const useDemoStore = create((set, get) => ({ notifications: state.notifications.map((n) => ({ ...n, read: true })), })), + // Catalog filters (Default: 'all' - Все модули) + catalogProfileFilter: 'all', + setCatalogProfileFilter: (catalogProfileFilter) => set({ catalogProfileFilter }), + catalogCategoryFilter: 'all', + setCatalogCategoryFilter: (catalogCategoryFilter) => set({ catalogCategoryFilter }), + catalogStatusFilter: 'all', + setCatalogStatusFilter: (catalogStatusFilter) => set({ catalogStatusFilter }), + catalogSearchQuery: '', + setCatalogSearchQuery: (catalogSearchQuery) => set({ catalogSearchQuery }), + resetCatalogFilters: () => + set({ + catalogProfileFilter: 'all', + catalogCategoryFilter: 'all', + catalogStatusFilter: 'all', + catalogSearchQuery: '', + }), + // Getters getActiveProfile: () => { const s = get(); diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx index ecd08e5..d7006fd 100644 --- a/src/pages/DashboardPage.tsx +++ b/src/pages/DashboardPage.tsx @@ -3,16 +3,16 @@ import { PageSkeleton } from '../shared/ui/Skeleton'; import { ErrorBoundary } from '../shared/ui/ErrorBoundary'; import { RouteGuard } from '../app/guards/RouteGuard'; -const DynamicDashboard = lazy(() => - import('../features/dashboard/DynamicDashboard').then((m) => ({ default: m.DynamicDashboard })) +const ModulesCatalog = lazy(() => + import('../features/catalog/ModulesCatalog').then((m) => ({ default: m.ModulesCatalog })) ); export const DashboardPage: React.FC = () => { return ( - - - }> - + + + }> + diff --git a/src/pages/ProcurementPage.tsx b/src/pages/ProcurementPage.tsx new file mode 100644 index 0000000..e0f4972 --- /dev/null +++ b/src/pages/ProcurementPage.tsx @@ -0,0 +1,240 @@ +import React, { useState } from 'react'; +import { RouteGuard } from '../app/guards/RouteGuard'; +import { ErrorBoundary } from '../shared/ui/ErrorBoundary'; +import { MaturityBadge } from '../shared/ui/MaturityBadge'; +import { Badge } from '../shared/ui/Badge'; +import { Button } from '../shared/ui/Button'; +import { useToast } from '../shared/ui/useToast'; +import { + Truck, + ArrowLeft, + CheckCircle2, + FileText, + Building2, + Clock, + Link2, + Calculator, +} from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; + +export const ProcurementPage: React.FC = () => { + const navigate = useNavigate(); + const { showToast } = useToast(); + const [requestedEmail, setRequestedEmail] = useState(''); + const [monthlySpend, setMonthlySpend] = useState(350000); + + const estimatedSavings = Math.round(monthlySpend * 0.085); // ~8.5% procurement optimization + + const handleRequestEarlyAccess = (e: React.FormEvent) => { + e.preventDefault(); + if (!requestedEmail.trim()) return; + showToast(`Заявка на ранний доступ для ${requestedEmail} успешно зарегистрирована!`, 'success'); + setRequestedEmail(''); + }; + + return ( + + +
+ {/* Top navigation back to catalog */} +
+ + +
+ + В разработке (Q4) +
+
+ + {/* Hero Banner */} +
+
+ + Дорожная карта платформы • Релиз Q4 +
+ +
+
+

+
+ +
+ Закупки и управление поставщиками +

+

+ Централизованная система снабжения для ресторанов, ритейла, автосервисов и производств. Автозаказ по точкам неснижаемого остатка, сверка электронных накладных через ЭДО и прозрачный контроль цен контрагентов. +

+
+ +
+
Плановый срок запуска
+
IV квартал 2026
+
Интегрирован со «Складом»
+
+
+
+ + {/* 4 Architectural Columns */} +
+ {/* 1. Назначение */} +
+
+ + 1. Бизнес-назначение модуля +
+

+ Исключение перебоев в поставках ключевого сырья, устранение человеческого фактора при расчете объема закупок и защита бюджета компании от несанкционированного завышения цен поставщиками. +

+
    +
  • + + Единый реестр контрагентов, договоров и спецификаций +
  • +
  • + + Контроль минимальных квантов отгрузки и графиков доставки +
  • +
  • + + Рейтинг поставщиков по показателю точности сроков (OTIF) +
  • +
+
+ + {/* 2. Возможности */} +
+
+ + 2. Запланированные возможности +
+
    +
  • + + Автозаказ (MRP lite): автоматическое формирование заявок поставщикам при падении складского остатка ниже страхового минимума. +
  • +
  • + + Согласование бюджетов: многоуровневый маршрут утверждения заявок руководителями до отправки вендору. +
  • +
  • + + Сверка накладных: сравнение цен в заказе с ценами во входящем УПД с фиксацией расхождений. +
  • +
+
+ + {/* 3. Статус */} +
+
+ + 3. Текущий статус разработки +
+
+ Спецификация API взаимодействия с ядром «Склад» и «Финансы» успешно утверждена. Подготовлены макеты экранных форм и схемы базы данных для партионного сопоставления. +
+

+ В демо-версии платформы данные закупок эмулируются через приходные накладные в модуле «Склад» и расходные транзакции в «Финансах». +

+
+ + {/* 4. Интеграции */} +
+
+ + 4. Планируемые внешние интеграции +
+
+
+
ЭДО (Диадок / СБИС)
+
Прием входящих УПД
+
+
+
1С:Предприятие 8.3
+
Синхронизация счетов
+
+
+
Честный ЗНАК
+
Приемка кодов DataMatrix
+
+
+
ГИС Меркурий (ВетИС)
+
Гашение ВСД общепита
+
+
+
+
+ + {/* Interactive ROI Calculator & Early Access Box */} +
+ {/* ROI Calculator */} +
+
+ + Интерактивный калькулятор экономии снабжения +
+

+ Оцените потенциальную экономию за счет устранения завышения цен поставщиков и оптимизации страховых запасов: +

+ +
+
+ Среднемесячный объем закупок: + {monthlySpend.toLocaleString('ru-RU')} ₽ / мес +
+ setMonthlySpend(Number(e.target.value))} + className="w-full accent-krona-green" + /> +
+ +
+
+ Прогнозируемая экономия в год: (~8.5% оптимизации) +
+
+ +{(estimatedSavings * 12).toLocaleString('ru-RU')} ₽ / год +
+
+
+ + {/* Early Access Form */} +
+
+
Заявка на ранний доступ
+

+ Оставьте контакт для участия в закрытом бета-тестировании модуля Закупок: +

+
+ +
+ setRequestedEmail(e.target.value)} + className="w-full px-3 py-1.5 text-xs bg-krona-surface border border-krona-border rounded-lg text-krona-primary focus:outline-none focus:border-krona-green" + /> + +
+
+
+
+
+
+ ); +}; + +export default ProcurementPage; diff --git a/src/pages/index.ts b/src/pages/index.ts index 7b28441..4b29871 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -21,5 +21,6 @@ export * from './FormBuilderPage'; export * from './CardLayoutBuilderPage'; export * from './ViewBuilderPage'; export * from './CustomEntityRoutePage'; +export * from './ProcurementPage'; export * from './error/ForbiddenPage'; export * from './error/NotFoundPage'; diff --git a/src/shared/ui/MaturityBadge.tsx b/src/shared/ui/MaturityBadge.tsx index 32fb988..b0d4013 100644 --- a/src/shared/ui/MaturityBadge.tsx +++ b/src/shared/ui/MaturityBadge.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; -import { CheckCircle2, Layers, FlaskConical } from 'lucide-react'; +import { CheckCircle2, Layers, FlaskConical, Clock } from 'lucide-react'; -export type MaturityLevel = 'pilot1' | 'target' | 'demo'; +export type MaturityLevel = 'pilot1' | 'target' | 'demo' | 'planned'; export interface MaturityBadgeProps extends React.HTMLAttributes { level: MaturityLevel; @@ -40,6 +40,13 @@ export const MaturityBadge: React.FC = ({ icon: FlaskConical, iconClass: 'text-purple-600 dark:text-purple-400', }, + planned: { + label: 'Запланирован', + description: 'В разработке (дорожная карта Q4)', + badgeClass: 'bg-amber-50 text-amber-800 border-amber-300 dark:bg-amber-950/40 dark:text-amber-300 dark:border-amber-700', + icon: Clock, + iconClass: 'text-amber-600 dark:text-amber-400', + }, }[level]; const Icon = config.icon;