Commit Graph

123 Commits

Author SHA1 Message Date
soroush.asadi 62bd7a12f5 Build Next.js apps with Webpack instead of Turbopack
Next 16 defaults `next build` to Turbopack, which requires native SWC
bindings unavailable for Alpine musl from our npm mirror (only the WASM
fallback loads). Pass --webpack so the build uses the WASM SWC fallback
and succeeds inside the Docker images.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:35:28 +03:30
soroush.asadi 634ebe4856 Add .gitignore for Koja app
Ignore build artifacts (tsbuildinfo, .next) and Capacitor native
output directories.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:25:31 +03:30
soroush.asadi 377ef109cd Make Koja native deps optional to unblock web Docker build
Move Capacitor and cordova-plugin-pushe to optionalDependencies. They are
only needed for the native mobile shell, are never imported by the Next.js
source, and are not served by the Liara npm mirror — so installing them as
hard dependencies broke the web image build. As optional deps, npm skips
them when the mirror can't resolve them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:21:40 +03:30
soroush.asadi c68cca4f17 Add OTP login flow and multi-cafe role switching
Introduce an OTP input box on login/register, surface user roles and a
cafe chooser, add a dashboard switch button in the POS screen, and
register OTP validators explicitly to survive Docker layer caching.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:14:46 +03:30
soroush.asadi 923a00b113 Redesign thermal receipts with Vazirmatn font and cafe branding
Embed Vazirmatn web font in printed bills, add branded header with logo
and tagline, and wait for fonts to load before printing for clean output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:14:32 +03:30
soroush.asadi 963d02a265 Add push notifications (Pushe) + Capacitor shell for Koja
Iran-safe push for the Koja Android app (Cafe Bazaar / Myket / direct APK):

Backend
- PushDevice entity + EF migration; idempotent device register/unregister.
- IPushSender / PusheNotificationSender (Pushe REST) — SendToTopic for
  marketing (city-{slug}) and saved-café (cafe-{slug}) pushes, SendToTokens
  for targeted order/reservation updates.
- Public register/unregister endpoints + authorized topic broadcast.

App
- capacitor.config.ts (native WebView loads the live PWA via server.url).
- push.ts Pushe glue: topic helpers + backend device registration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:06:42 +03:30
soroush.asadi 289c808257 Rename public discovery app from "finder" to "koja"
Rebrand the public café-discovery app: directories web/finder→web/koja and
docker/finder→docker/koja, plus all service wiring (docker-compose, Caddy
subdomain koja.meezi.ir, env vars KOJA_PORT / NEXT_PUBLIC_KOJA_URL, CI
workflows) and the app's display name (Koja / کجا).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:02:22 +03:30
soroush.asadi 16cff8730b feat : kavenegar otp added 2026-05-29 10:18:47 +03:30
soroush.asadi fc21471131 Native thermal printing via iframe — 80mm, RTL, no blank tail
Problem: window.print() on the main page used A4 height (blank paper
after receipt), no RTL direction, and Tailwind styles leaked into print.

Solution — iframe isolation:
- lib/thermal-print.ts: builds a self-contained HTML document
  (@page { size: 80mm auto; margin: 0 }, html { direction: rtl })
  and fires it through a hidden off-screen <iframe>. The iframe
  document contains only the receipt so height == content height.
- pos-slip-modal.tsx: Print button calls printThermal(buildThermalDocument())
  instead of window.print(). Preview panel is unchanged (screen only).
- pos-receipt-print.css: updated @page + direction as fallback for any
  remaining window.print() callers.

Works with USB driver (Atom A300) as default printer — OS print spooler
receives the job exactly as if it were any other document.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 02:52:46 +03:30
soroush.asadi 42d7667735 Fully implement Kavenegar SMS support
Core changes:
- ISmsService: add SendBulkAsync (batches of 200) + GetAccountInfoAsync
- KavenegarSmsService: POST requests, sender number config, bulk send
  via comma-separated receptors, account balance, full error code mapping
  (HTTP 400-432), enabled-flag check before any send
- SmsMarketingService: replaced per-recipient loop with SendBulkAsync
- SmsController: new GET /sms/balance endpoint returns Kavenegar credit
- SmsDtos: SmsBalanceDto
- IntegrationDtos + PlatformIntegrationService: SenderNumber field
- appsettings.json + docker-compose: Kavenegar__SenderNumber = 90005671

Dashboard:
- sms-screen: char counter, SMS parts indicator (Persian 70/67 chars,
  Latin 160/153), account balance card, sender line display, result banner

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 02:38:06 +03:30
soroush.asadi b78f2affb6 Move POS page to fullscreen layout for full viewport width
POS terminal needs the entire screen — the dashboard navigation
sidebar (224px) was eating into the cashier's working space.
Moving /pos from (dashboard) to (fullscreen) gives the POS the
full viewport with no chrome. Auth redirect and CafeThemeProvider
are applied directly in the new page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 02:20:43 +03:30
soroush.asadi a21cb7dd8e fix(dashboard): fallback for crypto.randomUUID on HTTP (non-secure context)
crypto.randomUUID() is only available over HTTPS. Add a timestamp+random
fallback so the dashboard works on plain HTTP during development/IP access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 02:01:21 +03:30
soroush.asadi 83758fe68a fix(admin-web): add .gitkeep to public/ so Docker COPY finds /app/public
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 01:46:56 +03:30
soroush.asadi c87c40ffe6 fix(website): add .gitkeep to public/fonts so Docker COPY finds /app/public
Empty directories are not tracked by git — without this the runner stage
COPY --from=builder /app/public ./public fails with "not found".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 01:31:56 +03:30
soroush.asadi 060f724f7c fix(website): revert generateStaticParams params to non-Promise
generateStaticParams receives plain params (not Promise).
Only page/layout default exports get Promise params in Next.js 15+.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 22:56:50 +03:30
soroush.asadi 98a7efc719 fix(website): update all route/layout params to Promise for Next.js 16
Next.js 15+ requires params to be typed as Promise<{...}> and awaited.
Fixed 17 files: all [locale] pages, layouts, and blog [slug] page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 22:46:09 +03:30
soroush.asadi 1559ee95c7 fix(website): update route params type for Next.js 16
Next.js 15+ changed dynamic route params to Promise<...>.
Update GET and POST type signatures in blog comments route.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 22:20:14 +03:30
soroush.asadi 0a33497d40 feat(admin-web): add web/admin to repo
Initial commit of the Super-Admin web panel (Next.js + TypeScript).
CI admin-web-check job was failing because the directory was never
tracked in git.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 18:46:25 +03:30
soroush.asadi 8f81a62ec9 Redesign menu admin screen with two-panel layout and modal forms
UX improvements:
- Two-panel layout: sticky category sidebar (desktop) + items grid
- Mobile: horizontal scrolling category pills above items
- Category sidebar shows item count badge per category; edit on hover
- Items search bar + instant filter (name, English name, Arabic name)
- Category filter drives items grid (click sidebar = filter items)
- Item cards: image hover reveals Edit button (progressive disclosure)
- Out-of-stock overlay + inline toggle switch (replaces text button)
- Add/Edit item opens in clean modal overlay (no jarring inline expansion)
- Add/Edit category opens in separate modal
- nameEn made optional (was blocking new item creation)
- Consolidated form state with single object per form
- Empty state with illustration and Add CTA
- Skeleton loading grid
- Branch overrides tab wired to branchId from store
- New i18n keys for search, counts, states in en/fa/ar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 08:10:25 +03:30
soroush.asadi 79deab543a Redesign POS order flow with order type picker and counter/takeaway support
- Add OrderTypePicker screen: Table / Counter / Takeaway cards shown when no
  active session, replacing the old always-visible table board
- Move PosTableBoard into a modal overlay (opens on Table selection or
  "Assign Table" for counter orders)
- Add orderType field + setOrderType action to cart store
- Counter and Takeaway orders no longer require a table to submit
- Add "Assign Table →" button in cart for counter orders with active session
- Rewrite category tabs as horizontal scrollable row (no wrapping)
- Larger product cards with 4:3 thumbnail + quantity badge overlay
- Bigger quantity controls (h-8 w-8) and "New order" back button in header
- Add i18n keys for order types in en/fa/ar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 00:07:58 +03:30
soroush.asadi 42d4cb896a feat(finder): AI-powered cafe finder PWA with Next.js 16
Public cafe discovery app:
- SEO-optimised pages: home, /cafe/[slug], /search, /city/[city]
- AI search bar with natural language queries
- Structured data (JSON-LD) for Google rich results
- City browsing, rating/filter sidebar, similar cafes
- Review listing, full menu preview, working-hours card
- Web App Manifest + offline fallback page (PWA)
- Next.js 16: params/searchParams typed as Promise<{}>
- Fix Lucide icon title→aria-label (type removed upstream)
- "use client" on offline page (onClick handler)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-27 21:34:47 +03:30
soroush.asadi d62bb8d3ad feat(website): Next.js 16 marketing website with RTL/Farsi
Marketing website for Meezi platform:
- Server-side rendered pages: home, demo, blog, pricing
- RTL/Farsi layout with Vazirmatn font
- SEO metadata and Open Graph tags
- proxy.ts for Next.js 16 middleware convention
- MEEZI_API_URL internal Docker network routing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-27 21:34:32 +03:30
soroush.asadi 131ecdbbe6 feat(dashboard): Next.js 16 merchant panel with offline POS and PWA
Complete merchant dashboard upgrade:

Next.js 16 compatibility:
- Fix params/searchParams typed as Promise<{}> throughout App Router
- Replace middleware.ts with proxy.ts (Next.js 16 convention)
- Remove unused @ts-expect-error directives caught by stricter TS
- Cast dynamic next-intl t() keys to fix TranslateArgs type errors

Offline POS:
- IndexedDB queue (meezi_pos_offline) for orders created while offline
- Zustand sync store tracking queueCount, isSyncing, isOnline
- useOfflineSync hook: auto-syncs on reconnect/visibility-change
- SyncStatusIndicator chip in topbar (amber=offline, blue=syncing)
- submitOrderToApi falls back to local order on network failure
- Local orders skip payment flow; sync on reconnect

PWA (installable):
- @ducanh2912/next-pwa with Workbox runtime caching rules
- Web App Manifest (manifest.ts) — RTL/Farsi, theme #0F6E56
- PWA icons: 192px, 512px, maskable 512px
- next.config.ts replaces next.config.mjs

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-27 21:34:12 +03:30