diff --git a/docs/pos-screenshots/03-sale-empty-1366x768.png b/docs/pos-screenshots/03-sale-empty-1366x768.png index cf15440..cc2e22f 100644 Binary files a/docs/pos-screenshots/03-sale-empty-1366x768.png and b/docs/pos-screenshots/03-sale-empty-1366x768.png differ diff --git a/docs/pos-screenshots/03-sale-empty-1920x1080.png b/docs/pos-screenshots/03-sale-empty-1920x1080.png index b33bf57..a6db6e2 100644 Binary files a/docs/pos-screenshots/03-sale-empty-1920x1080.png and b/docs/pos-screenshots/03-sale-empty-1920x1080.png differ diff --git a/docs/pos-screenshots/04-sale-items-1366x768.png b/docs/pos-screenshots/04-sale-items-1366x768.png index da6f915..1cc60a1 100644 Binary files a/docs/pos-screenshots/04-sale-items-1366x768.png and b/docs/pos-screenshots/04-sale-items-1366x768.png differ diff --git a/docs/pos-screenshots/04-sale-items-1920x1080.png b/docs/pos-screenshots/04-sale-items-1920x1080.png index b41637e..cb5cf96 100644 Binary files a/docs/pos-screenshots/04-sale-items-1920x1080.png and b/docs/pos-screenshots/04-sale-items-1920x1080.png differ diff --git a/docs/pos-screenshots/05-payment-cash-1366x768.png b/docs/pos-screenshots/05-payment-cash-1366x768.png index 197f420..78ab4b9 100644 Binary files a/docs/pos-screenshots/05-payment-cash-1366x768.png and b/docs/pos-screenshots/05-payment-cash-1366x768.png differ diff --git a/docs/pos-screenshots/05-payment-cash-1920x1080.png b/docs/pos-screenshots/05-payment-cash-1920x1080.png index 248148f..63cb6c9 100644 Binary files a/docs/pos-screenshots/05-payment-cash-1920x1080.png and b/docs/pos-screenshots/05-payment-cash-1920x1080.png differ diff --git a/docs/pos-screenshots/06-payment-mixed-1366x768.png b/docs/pos-screenshots/06-payment-mixed-1366x768.png index 68b2d5e..6cc1d19 100644 Binary files a/docs/pos-screenshots/06-payment-mixed-1366x768.png and b/docs/pos-screenshots/06-payment-mixed-1366x768.png differ diff --git a/docs/pos-screenshots/06-payment-mixed-1920x1080.png b/docs/pos-screenshots/06-payment-mixed-1920x1080.png index 64a282d..2b355fc 100644 Binary files a/docs/pos-screenshots/06-payment-mixed-1920x1080.png and b/docs/pos-screenshots/06-payment-mixed-1920x1080.png differ diff --git a/docs/pos-screenshots/09-shell-offline-1366x768.png b/docs/pos-screenshots/09-shell-offline-1366x768.png index 29c4569..4fd75ae 100644 Binary files a/docs/pos-screenshots/09-shell-offline-1366x768.png and b/docs/pos-screenshots/09-shell-offline-1366x768.png differ diff --git a/docs/pos-screenshots/09-shell-offline-1920x1080.png b/docs/pos-screenshots/09-shell-offline-1920x1080.png index 0e68dcd..fe63494 100644 Binary files a/docs/pos-screenshots/09-shell-offline-1920x1080.png and b/docs/pos-screenshots/09-shell-offline-1920x1080.png differ diff --git a/docs/pos-screenshots/10-shell-online-1366x768.png b/docs/pos-screenshots/10-shell-online-1366x768.png index da6f915..4d0d7df 100644 Binary files a/docs/pos-screenshots/10-shell-online-1366x768.png and b/docs/pos-screenshots/10-shell-online-1366x768.png differ diff --git a/docs/pos-screenshots/10-shell-online-1920x1080.png b/docs/pos-screenshots/10-shell-online-1920x1080.png index b4a54b0..495a564 100644 Binary files a/docs/pos-screenshots/10-shell-online-1920x1080.png and b/docs/pos-screenshots/10-shell-online-1920x1080.png differ diff --git a/docs/pos-screenshots/11-shell-dark-1366x768.png b/docs/pos-screenshots/11-shell-dark-1366x768.png index 06749dc..71893cd 100644 Binary files a/docs/pos-screenshots/11-shell-dark-1366x768.png and b/docs/pos-screenshots/11-shell-dark-1366x768.png differ diff --git a/docs/pos-screenshots/11-shell-dark-1920x1080.png b/docs/pos-screenshots/11-shell-dark-1920x1080.png index b8129e8..3723254 100644 Binary files a/docs/pos-screenshots/11-shell-dark-1920x1080.png and b/docs/pos-screenshots/11-shell-dark-1920x1080.png differ diff --git a/docs/ui-review-fixes-progress.md b/docs/ui-review-fixes-progress.md new file mode 100644 index 0000000..1d34b38 --- /dev/null +++ b/docs/ui-review-fixes-progress.md @@ -0,0 +1,20 @@ +# Правки по ревью скриншотов сайта (Mac-Claude) + +Замечания к новым скриншотам test.food-market.kz/screenshots/*.webp. Часть — ошибки +самой программы (не только скриншотов). + +## План / статус +1. [ ] Английские строки в админке: «Dashboard», «Low-stock alerts», «live» и др. — + перевести; grep по web/src на видимые латинские строки; лингва-проверка в menu-walk. +2. [ ] Каталог: дубль «Все товары» (корень дерева + системная группа). Убрать дубль. +3. [ ] Валюта: везде «₸» и формат «1 300 ₸» (сейчас розничная — «510 KZT»). +4. [ ] Касса на сайте: заменить рендер HTML-макета на НАСТОЯЩИЙ экран Avalonia-кассы + (--screenshots, витринные данные «Магазин у дома», с логотипом). Макет не использовать. +5. [ ] Витрина: packaging по товарам (Авокадо 1шт — штучный, Бешбармак 1кг — весовой и т.п.). +6. [ ] Выложить сайт+стенд, переснять затронутые скриншоты, посмотреть каждый кадр. + +## Что НЕ трогаем +Прод, shared-postgres, global.json, gateway nginx, sklad. + +## Журнал +- 08.10 — старт: план, разбор замечаний. diff --git a/src/food-market.api/Controllers/Catalog/ProductGroupsController.cs b/src/food-market.api/Controllers/Catalog/ProductGroupsController.cs index ec0107e..0b63a5e 100644 --- a/src/food-market.api/Controllers/Catalog/ProductGroupsController.cs +++ b/src/food-market.api/Controllers/Catalog/ProductGroupsController.cs @@ -45,7 +45,7 @@ public class ProductGroupsController : ControllerBase }; var items = await q .Skip(req.Skip).Take(req.Take) - .Select(g => new ProductGroupDto(g.Id, g.Name, g.ParentId, g.Path, g.SortOrder, g.MarkupPercent, g.OrganizationId)) + .Select(g => new ProductGroupDto(g.Id, g.Name, g.ParentId, g.Path, g.SortOrder, g.MarkupPercent, g.OrganizationId, g.IsSystem)) .ToListAsync(ct); return new PagedResult { Items = items, Total = total, Page = req.Page, PageSize = req.Take }; } @@ -54,7 +54,7 @@ public class ProductGroupsController : ControllerBase public async Task> Get(Guid id, CancellationToken ct) { var g = await _db.ProductGroups.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, ct); - return g is null ? NotFound() : new ProductGroupDto(g.Id, g.Name, g.ParentId, g.Path, g.SortOrder, g.MarkupPercent, g.OrganizationId); + return g is null ? NotFound() : new ProductGroupDto(g.Id, g.Name, g.ParentId, g.Path, g.SortOrder, g.MarkupPercent, g.OrganizationId, g.IsSystem); } [HttpPost, RequiresPermission("ProductGroupsManage")] diff --git a/src/food-market.api/Seed/ShowcaseSeeder.cs b/src/food-market.api/Seed/ShowcaseSeeder.cs index 8603985..a1075e6 100644 --- a/src/food-market.api/Seed/ShowcaseSeeder.cs +++ b/src/food-market.api/Seed/ShowcaseSeeder.cs @@ -60,7 +60,6 @@ public async Task SeedAsync(Guid orgId, CancellationToken ct = defa var unitKg = await _db.UnitsOfMeasure.IgnoreQueryFilters().FirstAsync(u => u.OrganizationId == null && u.Code == "166", ct); var unitSht = await _db.UnitsOfMeasure.IgnoreQueryFilters().FirstAsync(u => u.OrganizationId == null && u.Code == "796", ct); - var unitL = await _db.UnitsOfMeasure.IgnoreQueryFilters().FirstAsync(u => u.OrganizationId == null && u.Code == "112", ct); var retailPrice = await _db.PriceTypes.IgnoreQueryFilters().FirstAsync(p => p.OrganizationId == orgId && p.IsRetail, ct); var currency = await _db.Currencies.FirstAsync(c => c.Code == "KZT", ct); @@ -89,11 +88,17 @@ public async Task SeedAsync(Guid orgId, CancellationToken ct = defa long bcSeq = 487_600_000_001L; for (int g = 0; g < groups.Count; g++) { - var (priceMin, priceMax, unit, packaging) = YearDemoSeeder.GroupParams(g); - var grpUnit = unit switch { "kg" => unitKg, "l" => unitL, _ => unitSht }; + // Цену берём из диапазона группы; фасовку/единицу — из НАЗВАНИЯ товара, + // чтобы они всегда совпадали (нет «Авокадо 1шт — весовой» и «… 1кг — штучный»). + var (priceMin, priceMax, _, _) = YearDemoSeeder.GroupParams(g); for (int i = 0; i < 25; i++) { var name = YearDemoSeeder.ProductNames[g][i]; + // «кг» в названии → весовой товар (кг); всё остальное (штучные + // фасовки Nг/Nмл/Nл/Nшт, бутылки, пачки) → штучный. + var isWeight = name.Contains("кг"); + var unitObj = isWeight ? unitKg : unitSht; + var packaging = isWeight ? Packaging.Weight : Packaging.Piece; var raw = priceMin + (decimal)_rng.NextDouble() * (priceMax - priceMin); var price = Math.Round(raw / 10m, 0) * 10m; // до 10 тенге var p = new Product @@ -101,7 +106,7 @@ public async Task SeedAsync(Guid orgId, CancellationToken ct = defa OrganizationId = orgId, Name = name, Article = null, - UnitOfMeasureId = grpUnit.Id, + UnitOfMeasureId = unitObj.Id, Vat = 12m, VatEnabled = true, ProductGroupId = groups[g].Id, Packaging = packaging, diff --git a/src/food-market.application/Catalog/CatalogDtos.cs b/src/food-market.application/Catalog/CatalogDtos.cs index 2f61585..e997828 100644 --- a/src/food-market.application/Catalog/CatalogDtos.cs +++ b/src/food-market.application/Catalog/CatalogDtos.cs @@ -29,7 +29,7 @@ public record RetailPointDto( public record ProductGroupDto( Guid Id, string Name, Guid? ParentId, string Path, int SortOrder, - decimal? MarkupPercent, Guid? OrganizationId); + decimal? MarkupPercent, Guid? OrganizationId, bool IsSystem = false); public record CounterpartyDto( Guid Id, string Name, string? LegalName, CounterpartyType Type, diff --git a/src/food-market.pos/ViewModels/ViewModels.cs b/src/food-market.pos/ViewModels/ViewModels.cs index afd8ea5..514ce3b 100644 --- a/src/food-market.pos/ViewModels/ViewModels.cs +++ b/src/food-market.pos/ViewModels/ViewModels.cs @@ -47,7 +47,7 @@ public partial class MainWindowViewModel : ViewModelBase partial void OnBadgesCompactChanged(bool value) { OnPropertyChanged(nameof(ShiftBadge)); OnPropertyChanged(nameof(QueueBadge)); } /// Конструктор для дизайнера/скриншотов (демо-данные). - public MainWindowViewModel() { CurrentPage = SaleViewModel.Sample(); CashierName = "Пётр Кассиров"; ShiftNumber = "С-2026-000012 открыта с 09:00"; IsShiftOpen = true; StoreName = "Демо-магазин"; PointName = "Касса 1"; } + public MainWindowViewModel() { CurrentPage = SaleViewModel.Sample(); CashierName = "Виктория Ким"; ShiftNumber = "С-2026-000012 открыта с 09:00"; IsShiftOpen = true; StoreName = "Магазин у дома"; PointName = "Касса 1"; } public MainWindowViewModel(AppServices svc) { _svc = svc; if (svc.Session is not null) StartSession(); else ShowLogin(); } diff --git a/src/food-market.pos/Views/MainWindow.axaml b/src/food-market.pos/Views/MainWindow.axaml index 492f4ae..c449c1b 100644 --- a/src/food-market.pos/Views/MainWindow.axaml +++ b/src/food-market.pos/Views/MainWindow.axaml @@ -24,7 +24,7 @@ - + diff --git a/src/food-market.public/public/screenshots/pos-pay.webp b/src/food-market.public/public/screenshots/pos-pay.webp index 8bd6402..95b6879 100644 Binary files a/src/food-market.public/public/screenshots/pos-pay.webp and b/src/food-market.public/public/screenshots/pos-pay.webp differ diff --git a/src/food-market.public/public/screenshots/pos-pay@2x.webp b/src/food-market.public/public/screenshots/pos-pay@2x.webp index 9c2b26f..6ac9a09 100644 Binary files a/src/food-market.public/public/screenshots/pos-pay@2x.webp and b/src/food-market.public/public/screenshots/pos-pay@2x.webp differ diff --git a/src/food-market.public/public/screenshots/pos-sale.webp b/src/food-market.public/public/screenshots/pos-sale.webp index 2f9b2d4..cd2537a 100644 Binary files a/src/food-market.public/public/screenshots/pos-sale.webp and b/src/food-market.public/public/screenshots/pos-sale.webp differ diff --git a/src/food-market.public/public/screenshots/pos-sale@2x.webp b/src/food-market.public/public/screenshots/pos-sale@2x.webp index 8a3fa6c..53124bd 100644 Binary files a/src/food-market.public/public/screenshots/pos-sale@2x.webp and b/src/food-market.public/public/screenshots/pos-sale@2x.webp differ diff --git a/src/food-market.public/scripts/pos-mockup/pos-mockup.html b/src/food-market.public/scripts/pos-mockup/pos-mockup.html deleted file mode 100644 index d1eb299..0000000 --- a/src/food-market.public/scripts/pos-mockup/pos-mockup.html +++ /dev/null @@ -1,219 +0,0 @@ - - - - -Касса — макет - - - - - -
-
- -
-
Магазин у дома · Точка Касса 1
-
-
Кассир Виктория Ким
-
-
Смена С-2026-000012 открыта с 09:00
-
На связи · очередь пуста
-
- -
-
- - -
Избранное
-
-
Хлеб «Семейный»
280 ₸
-
Молоко 2,5%
450 ₸
-
Яблоки (вес)
690 ₸/кг
-
Картофель (вес)
280 ₸/кг
-
Пакет-майка
20 ₸
-
Вода 0,5 л
150 ₸
-
- -
-
Товар
Количество
Сумма
-
Хлеб «Семейный» нарезной
280 ₸ за шт
-
−2 шт+
560 ₸
-
-
Молоко 2,5% 1 л
450 ₸ за шт
-
−1 шт+
450 ₸
-
-
Яблоки «Апорт»
690 ₸ за кг
-
−1,235 кг+
852 ₸
-
-
Сахар-песок 1 кг
520 ₸ за шт · скидка 60 ₸ (1 560 → 1 500 ₸)
-
−3 шт+
1 500 ₸
-
-
-
- - -
- -
- F1ПомощьF2ПоискF4СкидкаF7Отложить - F9ВозвратF10СменаF12ОплатаDelУдалить строкуEscОтмена -
-
- -
- -
- - diff --git a/src/food-market.public/scripts/screenshots.mjs b/src/food-market.public/scripts/screenshots.mjs index 776f98d..eeee272 100644 --- a/src/food-market.public/scripts/screenshots.mjs +++ b/src/food-market.public/scripts/screenshots.mjs @@ -3,7 +3,7 @@ // Снимает СТЕНД (test.admin.food-market.kz) под витринной организацией // «Магазин у дома» (создаётся deploy/showcase-seed.sh — чистые данные, без // e2e/test-мусора). Светлая тема, 1440×900 @2x. Плюс рендерит макет новой -// светлой кассы (scripts/pos-mockup/pos-mockup.html) → продажа + оплата. +// Касса (продажа/оплата) снимается отдельно — настоящий Avalonia-экран. // // Предусловие: на стенде поднят API с ShowcaseSeeder и выполнен showcase-seed. // bash deploy/showcase-seed.sh @@ -18,7 +18,7 @@ // CHROME — путь к chrome (по умолч. /usr/bin/google-chrome) import { chromium } from 'playwright' import path from 'node:path' -import { fileURLToPath, pathToFileURL } from 'node:url' +import { fileURLToPath } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const OUT = path.resolve(__dirname, '..', 'public', 'screenshots') @@ -166,26 +166,12 @@ for (const s of shots) { } } -// ── Макет новой светлой кассы (локальный HTML) ────────────────────────────── -const posHtml = pathToFileURL(path.resolve(__dirname, 'pos-mockup', 'pos-mockup.html')).href -const posCtx = await browser.newContext({ - viewport: { width: 1920, height: 1080 }, - deviceScaleFactor: 1, - colorScheme: 'light', -}) -const posPage = await posCtx.newPage() -console.log('pos mockup →', posHtml) -// Экран оплаты (модалка видна). -await posPage.goto(posHtml, { waitUntil: 'networkidle' }) -await posPage.waitForTimeout(400) -await posPage.screenshot({ path: path.join(OUT, 'pos-pay.png') }) -console.log(' ok → pos-pay.png') -// Экран продажи (прячем модалку через body.nomodal). -await posPage.evaluate(() => document.body.classList.add('nomodal')) -await posPage.waitForTimeout(200) -await posPage.screenshot({ path: path.join(OUT, 'pos-sale.png') }) -console.log(' ok → pos-sale.png') -await posCtx.close() +// POS (касса): скриншоты НЕ здесь. Это настоящий экран Avalonia-кассы, он +// рендерится отдельно (headless Skia) и кладётся в docs/pos-screenshots/: +// dotnet run --project src/food-market.pos -c Release -- --screenshots +// На сайт берём 04-sale-items (продажа) и 05-payment-cash (оплата) → +// public/screenshots/pos-sale.png / pos-pay.png → optimize-screenshots.mjs. +// HTML-макет кассы на сайте НЕ используется. await browser.close() console.log('done') diff --git a/src/food-market.web/src/components/DashboardWidgets.tsx b/src/food-market.web/src/components/DashboardWidgets.tsx index f52fa35..114db67 100644 --- a/src/food-market.web/src/components/DashboardWidgets.tsx +++ b/src/food-market.web/src/components/DashboardWidgets.tsx @@ -109,7 +109,7 @@ export function LowStockWidget({ limit = 10 }: { limit?: number }) { return ( diff --git a/src/food-market.web/src/components/ProductGroupTree.tsx b/src/food-market.web/src/components/ProductGroupTree.tsx index af931fd..68b6daf 100644 --- a/src/food-market.web/src/components/ProductGroupTree.tsx +++ b/src/food-market.web/src/components/ProductGroupTree.tsx @@ -35,7 +35,10 @@ interface Props { export function ProductGroupTree({ selectedId, onSelect }: Props) { const { data: groups, isLoading } = useProductGroups() - const tree = useMemo(() => buildTree(groups ?? []), [groups]) + // Системную группу «Все товары» (bootstrap-дефолт для товаров без группы) в + // дереве не показываем — её роль уже играет корневой пункт «Все товары» + // (по модели МойСклада корень = все товары, без отдельной одноимённой группы). + const tree = useMemo(() => buildTree((groups ?? []).filter((g) => !g.isSystem)), [groups]) const [expanded, setExpanded] = useState>(new Set()) const toggle = (id: string) => diff --git a/src/food-market.web/src/lib/types.ts b/src/food-market.web/src/lib/types.ts index 0ab808e..feaa222 100644 --- a/src/food-market.web/src/lib/types.ts +++ b/src/food-market.web/src/lib/types.ts @@ -41,7 +41,7 @@ export interface RetailPoint { id: string; name: string; code: string | null; storeId: string; storeName: string | null; address: string | null; phone: string | null; fiscalSerial: string | null; fiscalRegNumber: string | null; isActive: boolean } -export interface ProductGroup { id: string; name: string; parentId: string | null; path: string; sortOrder: number; markupPercent: number | null; organizationId: string | null } +export interface ProductGroup { id: string; name: string; parentId: string | null; path: string; sortOrder: number; markupPercent: number | null; organizationId: string | null; isSystem?: boolean } export interface Counterparty { id: string; name: string; legalName: string | null; type: CounterpartyType; bin: string | null; iin: string | null; taxNumber: string | null; countryId: string | null; countryName: string | null; diff --git a/src/food-market.web/src/locales/ru.json b/src/food-market.web/src/locales/ru.json index 8412964..d406104 100644 --- a/src/food-market.web/src/locales/ru.json +++ b/src/food-market.web/src/locales/ru.json @@ -38,8 +38,8 @@ "yesterday": "Вчера", "today": "Сегодня", "month": "Месяц", - "live": "live", - "offline": "offline" + "live": "онлайн", + "offline": "офлайн" }, "nav": { "section_main": "Главное", @@ -98,7 +98,7 @@ "supplierDebts": "Долги поставщикам" }, "dashboard": { - "title": "Dashboard", + "title": "Сводка", "welcome": "Добро пожаловать, {{name}}", "fallbackDescription": "Сводка по продажам и каталогу", "revenueToday": "Выручка сегодня", diff --git a/src/food-market.web/src/pages/ProductsPage.tsx b/src/food-market.web/src/pages/ProductsPage.tsx index 3a5c402..3b26588 100644 --- a/src/food-market.web/src/pages/ProductsPage.tsx +++ b/src/food-market.web/src/pages/ProductsPage.tsx @@ -231,7 +231,7 @@ export function ProductsPage() { fractional ? { minimumFractionDigits: 2, maximumFractionDigits: 2 } : { maximumFractionDigits: 0 }) - return `${num} ${pr?.currencyCode ?? org.data?.defaultCurrencySymbol ?? ''}`.trim() + return `${num} ${org.data?.defaultCurrencySymbol ?? pr?.currencyCode ?? ''}`.trim() } // Sprint 19: dblclick → inline-edit. priceTypeId=systemPriceType.id; // если PriceType ещё нет в БД (priceTypes только подгружаются)