diff --git a/src/food-market.public/public/og/for-grocery.png b/src/food-market.public/public/og/for-grocery.png index b8d79be..d654f48 100644 Binary files a/src/food-market.public/public/og/for-grocery.png and b/src/food-market.public/public/og/for-grocery.png differ diff --git a/src/food-market.public/public/og/home.png b/src/food-market.public/public/og/home.png index 0d265ba..cefa40a 100644 Binary files a/src/food-market.public/public/og/home.png and b/src/food-market.public/public/og/home.png differ diff --git a/src/food-market.public/public/og/import.png b/src/food-market.public/public/og/import.png index 99f0e82..5656809 100644 Binary files a/src/food-market.public/public/og/import.png and b/src/food-market.public/public/og/import.png differ diff --git a/src/food-market.public/public/robots.txt b/src/food-market.public/public/robots.txt index ebfd7fc..5a2dd0f 100644 --- a/src/food-market.public/public/robots.txt +++ b/src/food-market.public/public/robots.txt @@ -1,3 +1,3 @@ User-agent: * Allow: / -Sitemap: https://test.food-market.kz/sitemap-index.xml +Sitemap: https://test.food-market.kz/sitemap.xml diff --git a/src/food-market.public/scripts/og.mjs b/src/food-market.public/scripts/og.mjs index 6b178b3..2796ba6 100644 --- a/src/food-market.public/scripts/og.mjs +++ b/src/food-market.public/scripts/og.mjs @@ -16,12 +16,12 @@ const logoPng = await fs.readFile(path.join(__dirname, 'allmarket-logo-color.png const logoUri = `data:image/png;base64,${logoPng.toString('base64')}` const pages = [ - { file: 'home.png', title: 'Программа учёта и касса для розничных магазинов в Казахстане', sub: 'Касса с торговыми весами, импорт каталога, ОФД РК. Бесплатно 90 дней.' }, + { file: 'home.png', title: 'Программа учёта и касса для розничных магазинов в Казахстане', sub: 'Касса с торговыми весами и импортом каталога. Бесплатно 90 дней.' }, { file: 'pos.png', title: 'Касса для Windows с поддержкой торговых весов', sub: 'Установка за 5 минут. Офлайн-режим. Стандартное оборудование.' }, { file: 'pricing.png', title: 'Прозрачные тарифы ALL MARKET', sub: 'От 5 000 ₸/мес. Все возможности на любом тарифе. 90 дней триал.' }, - { file: 'import.png', title: 'Перенесите каталог за один клик', sub: 'Excel, CSV, REST API. До 100 000 товаров. Бесплатно.' }, + { file: 'import.png', title: 'Перенесите каталог за один клик', sub: 'Excel, CSV, 1С, МойСклад. До 100 000 товаров. Бесплатно.' }, { file: 'about.png', title: 'О ALL MARKET', sub: 'Программа учёта розничной торговли, созданная в Казахстане.' }, - { file: 'for-grocery.png', title: 'ALL MARKET для продуктового магазина', sub: 'Весовой товар, скоропорт, штрихкоды, ABC-анализ.' }, + { file: 'for-grocery.png', title: 'ALL MARKET для продуктового магазина', sub: 'Весовой товар, скоропорт, штрихкоды, учёт остатков.' }, { file: 'for-pharmacy.png', title: 'ALL MARKET для аптеки', sub: 'Серии, сроки годности, рецептурный отпуск, контроль ЛП.' }, { file: 'for-cafe.png', title: 'ALL MARKET для кафе и общепита', sub: 'Модификаторы блюд, столики, печать на кухню.' }, { file: 'for-alcohol.png', title: 'ALL MARKET для алкомаркета', sub: 'Акцизные марки, контроль времени продаж, маркировка.' }, diff --git a/src/food-market.public/src/components/BusinessTariffBuilder.tsx b/src/food-market.public/src/components/BusinessTariffBuilder.tsx index 06c233e..5997875 100644 --- a/src/food-market.public/src/components/BusinessTariffBuilder.tsx +++ b/src/food-market.public/src/components/BusinessTariffBuilder.tsx @@ -1,9 +1,10 @@ import { useMemo, useState } from 'react' +import { PRICING, businessPrice, fmtKzt } from '@/lib/pricing' -const BASE = 10000 -const PER_SHOP = 2000 -const PER_CASH = 500 -const PER_WAREHOUSE = 500 +const BASE = PRICING.base +const PER_SHOP = PRICING.perShop +const PER_CASH = PRICING.perCash +const PER_WAREHOUSE = PRICING.perWarehouse interface SliderProps { label: string; value: number; min: number; max: number; onChange: (v: number) => void; suffix?: string @@ -31,12 +32,7 @@ export default function BusinessTariffBuilder() { const [warehouses, setWarehouses] = useState(1) const [employees, setEmployees] = useState(5) - const total = useMemo(() => { - return BASE - + Math.max(0, shops - 1) * PER_SHOP - + Math.max(0, cashes - 1) * PER_CASH - + Math.max(0, warehouses - 1) * PER_WAREHOUSE - }, [shops, cashes, warehouses]) + const total = useMemo(() => businessPrice({ shops, cashes, warehouses }), [shops, cashes, warehouses]) const fmt = new Intl.NumberFormat('ru') const signupHref = `/signup?plan=business&shops=${shops}&cashes=${cashes}&warehouses=${warehouses}&employees=${employees}` diff --git a/src/food-market.public/src/components/Footer.astro b/src/food-market.public/src/components/Footer.astro index 9489795..fa9187c 100644 --- a/src/food-market.public/src/components/Footer.astro +++ b/src/food-market.public/src/components/Footer.astro @@ -12,33 +12,32 @@ import Logo from './Logo.astro'

Продукт

Ресурсы

Компания

diff --git a/src/food-market.public/src/components/Header.astro b/src/food-market.public/src/components/Header.astro index 7e55cf4..8db61cb 100644 --- a/src/food-market.public/src/components/Header.astro +++ b/src/food-market.public/src/components/Header.astro @@ -1,24 +1,81 @@ --- import Logo from './Logo.astro' const APP_URL = import.meta.env.PUBLIC_APP_URL || 'https://admin.food-market.kz' +const nav = [ + { href: '/features', label: 'Возможности' }, + { href: '/pos', label: 'Касса' }, + { href: '/for-grocery', label: 'Для продуктовых' }, + { href: '/import', label: 'Импорт' }, + { href: '/pricing', label: 'Тарифы' }, + { href: '/kb', label: 'База знаний' }, + { href: '/blog', label: 'Блог' }, +] ---
+ + +
+ + diff --git a/src/food-market.public/src/components/SignupForm.tsx b/src/food-market.public/src/components/SignupForm.tsx index 64874d6..c046465 100644 --- a/src/food-market.public/src/components/SignupForm.tsx +++ b/src/food-market.public/src/components/SignupForm.tsx @@ -1,6 +1,34 @@ import { useState } from 'react' import { validateEmail, validatePassword, validatePhone } from '@/lib/validation' import { PhoneInput } from '@/components/PhoneInput' +import { PRICING, businessPrice, clamp, fmtKzt, type BusinessConfig } from '@/lib/pricing' + +const VALID_PLANS = ['start', 'business', 'network'] as const +type PlanId = (typeof VALID_PLANS)[number] + +/** Читаем ?plan= и параметры конфигуратора из URL на клиенте (сайт статический — + * серверного query нет). Неизвестный план → «Старт». Параметры конфигурации + * клампуются к границам конструктора. */ +function readUrlSelection(fallbackPlan: string): { plan: PlanId; config: BusinessConfig; hasConfig: boolean } { + const planFallback: PlanId = (VALID_PLANS as readonly string[]).includes(fallbackPlan) ? (fallbackPlan as PlanId) : 'start' + let plan = planFallback + const cfg: BusinessConfig = { shops: PRICING.shops.min, cashes: PRICING.cashes.min, warehouses: PRICING.warehouses.min, employees: PRICING.employees.min } + let hasConfig = false + if (typeof window !== 'undefined') { + const q = new URLSearchParams(window.location.search) + const p = q.get('plan') + plan = p && (VALID_PLANS as readonly string[]).includes(p) ? (p as PlanId) : 'start' + const num = (k: string, rng: { min: number; max: number }) => { + const v = q.get(k); if (v == null || v === '') return undefined + hasConfig = true; return clamp(Number(v), rng.min, rng.max) + } + cfg.shops = num('shops', PRICING.shops) ?? cfg.shops + cfg.cashes = num('cashes', PRICING.cashes) ?? cfg.cashes + cfg.warehouses = num('warehouses', PRICING.warehouses) ?? cfg.warehouses + cfg.employees = num('employees', PRICING.employees) ?? cfg.employees + } + return { plan, config: cfg, hasConfig } +} const APP_URL = (import.meta.env.PUBLIC_APP_URL as string | undefined) ?? 'https://admin.food-market.kz' const API_URL = APP_URL @@ -15,11 +43,16 @@ interface Props { type FieldErrors = Partial> export default function SignupForm({ defaultPlan = 'start' }: Props) { + // Разбор ?plan= и конфигурации из URL — один раз при инициализации (клиент). + const [sel] = useState(() => readUrlSelection(defaultPlan)) + const [config] = useState(sel.config) + const hasConfig = sel.hasConfig const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [orgName, setOrgName] = useState('') const [phone, setPhone] = useState('') - const [plan, setPlan] = useState(defaultPlan) + const [plan, setPlan] = useState(sel.plan) + const businessTotal = businessPrice(config) const [agree, setAgree] = useState(false) const [busy, setBusy] = useState(false) const [error, setError] = useState(null) @@ -45,7 +78,15 @@ export default function SignupForm({ defaultPlan = 'start' }: Props) { const res = await fetch(`${API_URL}/api/auth/signup`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email, password, organizationName: orgName, phone: phone || null, plan }), + // plan сервер принимает (SignupInput.Plan). Параметры конфигурации «Бизнес» + // отправляем вперёд-совместимо; текущий контракт их игнорирует (решение сервера — + // принимать ли — отдельно, см. отчёт site-fixes). + body: JSON.stringify({ + email, password, organizationName: orgName, phone: phone || null, plan, + ...(plan === 'business' && hasConfig + ? { shops: config.shops, cashes: config.cashes, warehouses: config.warehouses, employees: config.employees } + : {}), + }), }) if (!res.ok) { const j = await res.json().catch(() => ({})) @@ -154,8 +195,8 @@ export default function SignupForm({ defaultPlan = 'start' }: Props) {
Тариф
{[ - { id: 'start', name: 'Старт', price: '5 000 ₸' }, - { id: 'business', name: 'Бизнес', price: 'от 10 000 ₸' }, + { id: 'start', name: 'Старт', price: fmtKzt(PRICING.startPrice) }, + { id: 'business', name: 'Бизнес', price: hasConfig ? `${fmtKzt(businessTotal)}/мес` : 'от 10 000 ₸' }, { id: 'network', name: 'Сеть', price: 'по запросу' }, ].map((p) => ( ))}
+ {plan === 'business' && hasConfig && ( +
+
+ Ваша конфигурация «Бизнес» + {fmtKzt(businessTotal)}/мес +
+
+ Магазинов: {config.shops} + Касс: {config.cashes} + Складов: {config.warehouses} + Сотрудников: {config.employees} +
+

Изменить — в конструкторе тарифов.

+
+ )}
diff --git a/src/food-market.public/src/content/blog/cash-with-scales.md b/src/food-market.public/src/content/blog/cash-with-scales.md index e484432..6561368 100644 --- a/src/food-market.public/src/content/blog/cash-with-scales.md +++ b/src/food-market.public/src/content/blog/cash-with-scales.md @@ -40,4 +40,4 @@ cover_image: /photos/blog-scales.jpg - **Дешевле в эксплуатации** — не платите за сторонние модули интеграции - **Проще обучение кассиров** — они работают как привыкли, никакой «сложной системы» -[Скачать кассу →](/pos) +[Подробнее о кассе →](/pos) diff --git a/src/food-market.public/src/content/blog/launch.md b/src/food-market.public/src/content/blog/launch.md index af7ac4f..177269f 100644 --- a/src/food-market.public/src/content/blog/launch.md +++ b/src/food-market.public/src/content/blog/launch.md @@ -35,10 +35,9 @@ cover_image: /photos/blog-launch.jpg Сейчас мы открываем регистрацию и принимаем первых клиентов. Будем активно собирать обратную связь и улучшать продукт каждую неделю. В ближайших планах: -- Интеграция с Kaspi Magazin (Q3 2026) +- Интеграция с Kaspi Magazin - Marketplace интеграции (Wildberries, Ozon — Q4 2026) - Мобильное приложение для руководителя -- Готовая архитектура для будущей маркировки в РК [Зарегистрироваться →](/signup) diff --git a/src/food-market.public/src/content/blog/quick-start.md b/src/food-market.public/src/content/blog/quick-start.md index 8116a5b..7bdc11b 100644 --- a/src/food-market.public/src/content/blog/quick-start.md +++ b/src/food-market.public/src/content/blog/quick-start.md @@ -19,7 +19,7 @@ cover_image: /photos/blog-quickstart.jpg - Email — это будет ваш логин - Пароль (минимум 8 символов) - Название магазина -- Выберите тариф (можно оставить «Триал» — 90 дней бесплатно) +- Выберите тариф (Старт, Бизнес или Сеть) — первые 90 дней бесплатно Через 30 секунд после нажатия «Зарегистрироваться» вы окажетесь в админке ALL MARKET с готовым к работе пустым магазином. @@ -42,7 +42,7 @@ cover_image: /photos/blog-quickstart.jpg ## Шаг 3. Установите кассу (5 минут) -В админке: «Касса» → «Скачать установщик». +Приложение кассы для Windows — скоро (появится в разделе «Касса»). 1. Запустите .msi файл — нажмите «Далее» три раза 2. Введите ключ организации (показан в админке) @@ -57,7 +57,7 @@ cover_image: /photos/blog-quickstart.jpg В кассе: 1. «Открыть смену» — введите начальный остаток наличных 2. Сосканируйте штрихкод любого товара — он появится в чеке -3. «Оплата» → выберите тип (Наличные / Kaspi / Карта) +3. «Оплата» → выберите тип (наличные или карта) 4. Введите сумму полученных денег 5. Подтвердите — чек распечатается @@ -68,7 +68,6 @@ cover_image: /photos/blog-quickstart.jpg После первой продажи: - Добавьте остальных кассиров — «Сотрудники» → «Добавить» - Настройте программу лояльности — «Лояльность» → «Создать программу» -- Подключите Kaspi Pay для приёма безнала — «Интеграции» → «Kaspi» - Запустите акции и скидки — «Маркетинг» → «Акции» Полная документация в нашей [Базе знаний](/kb). diff --git a/src/food-market.public/src/content/kb/billing.md b/src/food-market.public/src/content/kb/billing.md index 61d61ec..0b1fd17 100644 --- a/src/food-market.public/src/content/kb/billing.md +++ b/src/food-market.public/src/content/kb/billing.md @@ -4,16 +4,6 @@ category: Биллинг order: 4 --- -```markdown ---- -title: Тарифы, оплата, изменение тарифа -slug: billing -section: account -order: 1 ---- - -# Тарифы, оплата, изменение тарифа - Полная информация о подписках ALL MARKET и порядке оплаты. ## Тарифы @@ -21,8 +11,9 @@ order: 1 См. полную таблицу на [странице тарифов](/pricing). Кратко: -- **Триал** — 90 дней, бесплатно, без карты, все возможности -- **Старт** — 5 000 тг/мес, 1 магазин, 1 касса, 1 склад +Все тарифы — первые 90 дней бесплатно, без банковской карты. + +- **Старт** — 5 000 тг/мес, 1 магазин, 1 касса, 1 склад, до 2 сотрудников - **Бизнес** — от 10 000 тг/мес, конструктор (до 3 магазинов / 5 касс / 5 складов) - **Сеть** — по запросу, без ограничений @@ -30,11 +21,7 @@ order: 1 ### Для физических лиц и ИП -- Kaspi Pay (карта или счёт) -- Halyk Bank, Jusan, Forte и другие казахстанские банки -- Visa / Mastercard - -Оплата проходит мгновенно. Доступ продлевается на оплаченный период. +Способ оплаты подписки уточняется — свяжитесь с нами: support@food-market.kz. Приём оплат картой онлайн — в разработке. ### Для юридических лиц @@ -43,15 +30,6 @@ order: 1 - Срок оплаты — 5 рабочих дней с даты выставления счёта - Доступ продлевается после поступления средств на расчётный счёт -## Скидки за длительные периоды - -При оплате авансом: -- 3 месяца: −5% -- 6 месяцев: −10% -- 12 месяцев: −20% - -Скидки применяются автоматически при выборе периода в форме оплаты. - ## Изменение тарифа В любой момент через «Настройки» → «Подписка». @@ -78,17 +56,14 @@ order: 1 - Импорт и экспорт работают Через 30 дней без оплаты: -- Учётная запись помечается на удаление - Email с финальным предупреждением -- Через 60 дней без оплаты — данные удаляются +- Контент сохраняется 30 календарных дней (см. оферту, п. 5.3/11.3), после чего может быть удалён ## Возврат средств -Возврат за неиспользованный период возможен: -- В течение первых 14 дней с момента оплаты — без обоснования -- После 14 дней — пропорционально неиспользованному периоду +Возврат денежных средств за неиспользованный период осуществляется в случаях, предусмотренных законодательством РК, по письменному заявлению (см. оферту, п. 6.5). -Заявка на возврат: support@food-market.kz с указанием причины и реквизитов для возврата. +Заявка: support@food-market.kz с указанием причины и реквизитов для возврата. ## История платежей и счёта @@ -106,5 +81,3 @@ order: 1 - Выделенный менеджер Заявка: sales@food-market.kz -``` - diff --git a/src/food-market.public/src/content/kb/faq.md b/src/food-market.public/src/content/kb/faq.md index 013a8ee..002d7e6 100644 --- a/src/food-market.public/src/content/kb/faq.md +++ b/src/food-market.public/src/content/kb/faq.md @@ -4,16 +4,6 @@ category: Общее order: 5 --- -```markdown ---- -title: Частые вопросы -slug: faq -section: general -order: 99 ---- - -# Частые вопросы - ## Регистрация и триал ### Сколько длится триал? @@ -31,7 +21,7 @@ order: 99 ## Тарифы и оплата ### Сколько стоит самый дешёвый тариф? -Старт — 5 000 тг/мес. Включает 1 магазин, 1 кассу, 1 склад, без лимита товаров и пользователей. +Старт — 5 000 тг/мес. Включает 1 магазин, 1 кассу, 1 склад, до 2 сотрудников, без лимита товаров. ### Что входит в тариф Бизнес? Конструктор: до 3 магазинов, до 5 касс, до 5 складов. Цена считается по формуле base 10 000 + 2 000 за магазин + 500 за кассу + 500 за склад. @@ -76,7 +66,7 @@ order: 99 ## Импорт и миграция ### Можно перенести данные из другой системы учёта? -Да. Поддерживаем импорт через Excel/CSV-файл, через стандартный REST API и через выгрузку 1С. Подробнее — на странице [/import](/import). +Да. Поддерживаем импорт из файла Excel/CSV, выгрузку 1С (сохранённую в CSV) и перенос из МойСклад по токену. Подробнее — на странице [/import](/import). ### Какой объём каталога переносится? До 100 000 товаров за один импорт. Каталог 10 000 позиций обычно переносится за 5–10 минут. @@ -98,5 +88,4 @@ order: 99 Базовая поддержка бесплатна для всех тарифов. Приоритетная поддержка с SLA — в тарифах Бизнес и Сеть. ### Есть ли обучение и внедрение? -Платные консультации 5 000 тг/час. Большинство клиентов справляются самостоятельно с помощью нашей базы знаний и видео-уроков. -``` +Большинство клиентов справляются самостоятельно с помощью базы знаний. Если нужна помощь с настройкой или импортом — напишите в поддержку, поможем. diff --git a/src/food-market.public/src/content/kb/pos-setup.md b/src/food-market.public/src/content/kb/pos-setup.md index 47a2dce..7cb9d91 100644 --- a/src/food-market.public/src/content/kb/pos-setup.md +++ b/src/food-market.public/src/content/kb/pos-setup.md @@ -4,16 +4,6 @@ category: Касса order: 3 --- -```markdown ---- -title: Настройка кассы для Windows -slug: pos-setup -section: cash-register -order: 1 ---- - -# Настройка кассы для Windows - Полная инструкция установки и настройки кассы ALL MARKET для Windows. Время — 15-20 минут. ## Системные требования @@ -28,7 +18,7 @@ order: 1 1. Войдите в админку ALL MARKET 2. Откройте раздел «Касса» в боковом меню -3. Нажмите «Скачать кассу для Windows» +3. Приложение кассы для Windows — скоро (кнопка появится в разделе «Касса») 4. Сохраните файл `food-market-pos-setup.msi` (~80 МБ) ## Установка @@ -82,7 +72,7 @@ order: 1 1. Нажмите «Открыть смену» 2. Введите начальный остаток наличных в кассе 3. Найдите товар поиском или сканером -4. Нажмите «Оплата» → выберите тип оплаты (Наличные / Kaspi / Карта) +4. Нажмите «Оплата» → выберите тип оплаты (наличные или карта) 5. Введите сумму полученных денег → подтвердите 6. Чек распечатается, сделка пройдёт в учёт @@ -107,5 +97,3 @@ order: 1 ## Поддержка support@food-market.kz — отвечаем в течение часа в рабочее время. -``` - diff --git a/src/food-market.public/src/content/kb/quickstart.md b/src/food-market.public/src/content/kb/quickstart.md index 6c39098..1fff1be 100644 --- a/src/food-market.public/src/content/kb/quickstart.md +++ b/src/food-market.public/src/content/kb/quickstart.md @@ -4,16 +4,6 @@ category: Начало работы order: 1 --- -```markdown ---- -title: Быстрый старт за 15 минут -slug: quickstart -section: getting-started -order: 1 ---- - -# Быстрый старт за 15 минут - После регистрации в ALL MARKET вам нужно сделать 4 простых шага, чтобы начать продавать. ## Шаг 1. Заполните данные магазина (1 мин) @@ -36,7 +26,7 @@ order: 1 ## Шаг 3. Установите кассу (5 мин) -«Касса» → «Скачать установщик». Запустите .msi файл, нажмите «Далее» три раза. После установки введите ключ организации (показан в разделе «Касса»). +Приложение кассы для Windows — скоро (следите за разделом «Касса» в админке). После установки нужно будет ввести ключ организации из раздела «Касса». Подключите оборудование: - Электронные торговые весы — через RS-232 или USB-COM @@ -51,8 +41,5 @@ order: 1 ## Что дальше? -- Добавить остальных кассиров и сотрудников: [Управление пользователями](/kb/users) -- Настроить скидки и программу лояльности: [Лояльность и скидки](/kb/loyalty) +- Добавить остальных кассиров и сотрудников — в админке «Настройки» → «Сотрудники» - Перенести каталог из старой системы: [Импорт каталога](/import) -``` - diff --git a/src/food-market.public/src/layouts/BaseLayout.astro b/src/food-market.public/src/layouts/BaseLayout.astro index 351dbf4..11b3943 100644 --- a/src/food-market.public/src/layouts/BaseLayout.astro +++ b/src/food-market.public/src/layouts/BaseLayout.astro @@ -57,11 +57,9 @@ const ymId = import.meta.env.PUBLIC_YM_ID ?? 'REPLACE_ME' offers: { '@type': 'Offer', price: '5000', priceCurrency: 'KZT' }, })} /> - {/* Sprint 20: analytics placeholders. См. docs/analytics.md. - Скрипты подключаются только при заданных env-vars; иначе - в HTML остаётся data-id="REPLACE_ME" — служит маркером - «не настроено» при ручной проверке view-source. */} - {gaId !== 'REPLACE_ME' ? ( + {/* Аналитика подключается ТОЛЬКО при реальных env-ID (PUBLIC_GA_ID / PUBLIC_YM_ID). + Без них НИЧЕГО не выводим — никаких заглушек data-id="REPLACE_ME" в HTML. */} + {gaId !== 'REPLACE_ME' && ( <>