尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
PWA offline-first para comerciantes de mercados de abastos. Inventario inteligente, ventas con ticket (imprimir / WhatsApp), semáforo de frescura y registro de mermas a un tap — pensada para manos ocupadas, luz solar directa y conexión inestable. Stack: Next.js 16 (App Router, TypeScript) · Tailwind CSS · shadcn/ui · Lucide · Supabase (Postgres + Auth + RLS) · Dexie/IndexedDB · React Query · Zustand · Web Speech API.查看完整 README ↗
PWA offline-first para comerciantes de mercados de abastos. Inventario inteligente, ventas con ticket (imprimir / WhatsApp), semáforo de frescura y registro de mermas a un tap — pensada para manos ocupadas, luz solar directa y conexión inestable.
Stack: Next.js 16 (App Router, TypeScript) · Tailwind CSS · shadcn/ui · Lucide · Supabase (Postgres + Auth + RLS) · Dexie/IndexedDB · React Query · Zustand · Web Speech API.
npm install
cp .env.example .env.local # opcional: rellena tus claves de Supabase
npm run dev # http://localhost:3000
Sin backend también funciona. Si no configuras Supabase, la app corre en modo local: la primera vez crea un acceso protegido con clave en el dispositivo y guarda todo en IndexedDB (puedes cargar productos de ejemplo desde el inventario vacío). Con Supabase configurado, el login es con correo y contraseña y los datos se respaldan en la nube.
| Modo | Cuándo | Cómo ingresa |
|---|---|---|
| Supabase | .env.local con URL y anon key | Correo + contraseña (registro incluido). La sesión persiste y funciona sin internet. |
| Local | Sin .env.local | Una cuenta por dispositivo con clave (PBKDF2, con bloqueo tras 5 intentos). Protege el acceso a la app; los datos de IndexedDB no están cifrados. |
En Supabase, si activas Confirm email, el usuario debe confirmar su correo antes de ingresar.
Cada venta (rápida o desde Ventas → Nueva venta) genera un ticket numerado con detalle, descuento, método de pago (efectivo/Yape/Plin/tarjeta) y vuelto. Desde el ticket puedes imprimir (58 mm u 80 mm, o "Guardar como PDF"), enviar por WhatsApp (al celular del cliente si lo registraste), copiar o compartir. Ajusta papel, dirección, RUC/DNI y mensaje en Ajustes; ahí mismo hay un ticket de prueba. El ticket es de control interno, no reemplaza una boleta o factura electrónica SUNAT.
Formas de imprimir
| Opción | Cuándo usarla |
|---|---|
| Bluetooth directo (ESC/POS) | Impresora térmica BLE con Chrome/Edge (Android o PC, HTTPS o localhost). Empareja una vez en Ajustes → Impresora; después imprime con un toque. No funciona en iPhone/iPad. |
| RawBT (solo Android) | Impresoras Bluetooth clásicas (SPP), que Web Bluetooth no ve. Instala la app RawBT y usa el botón del ticket. |
| Diálogo del sistema / PDF | Cualquier impresora instalada en el dispositivo (USB, Wi‑Fi, servicio de impresión de Android) o «Guardar como PDF». |
El ticket ESC/POS usa la página de códigos PC858 (acentos, ñ, ¿, ¡). Activa Cortar el papel solo si tu impresora tiene cortador.
Con Supabase, el dueño agrega cajeros por correo en Ajustes → Equipo (la persona debe haberse registrado antes). El cajero puede vender, ver el inventario y cobrar fiados; no puede anular ventas, ver reportes, editar/reabastecer productos, registrar mermas, cerrar caja ni cambiar el negocio. Se aplica en la interfaz, en los repositorios y en la base de datos (RLS).
/recuperar) y cambio desde Ajustes → Seguridad.npm run build
npm run start
supabase/migrations/0001_init.sql — 6 tablas, enums, índices, RLS
(aislamiento total por comerciante) y triggers de stock, PPC, merma y timestamps.supabase/migrations/0002_sales.sql — tablas sales y sale_items (tickets).
Sin este script las ventas no se sincronizan.supabase/migrations/0003_team_credit_reports.sql — stock mínimo, costo por
línea de venta, anulaciones, fiado (clientes y abonos), equipo con roles y las
políticas RLS actualizadas. Ejecútalo antes de usar la app con Supabase: el
cliente ya envía estos campos y, sin el script, la sincronización se detiene.http://localhost:3000) a Redirect URLs.anon key (Project Settings → API) a .env.local.El semáforo de frescura se recalcula tanto en el cliente (offline) como en el
servidor vía la función fn_recalc_freshness(owner).
mercadocontrol/
├─ supabase/migrations/0001_init.sql # Esquema + RLS + triggers
├─ public/
│ ├─ manifest.json # PWA
│ └─ sw.js # Service Worker (offline + background sync)
├─ src/
│ ├─ app/ # Rutas (App Router)
│ │ ├─ layout.tsx providers.tsx # Raíz: fuente, React Query, AuthProvider
│ │ ├─ (auth)/login/ # Pantalla pública de acceso
│ │ └─ (app)/ # Pantallas privadas (protegidas por SessionGate)
│ │ ├─ page.tsx # Dashboard
│ │ └─ inventario/ ventas/ mermas/ cierre/ remates/ ajustes/
│ ├─ components/
│ │ ├─ ui/ # Primitivas (button, card, sheet, stepper, feedback…)
│ │ ├─ layout/ # AppShell, Sidebar, TopBar, BottomNav, FAB
│ │ ├─ auth/ ticket/ sales/ settings/
│ │ ├─ dashboard/ inventory/ mermas/ cierre/
│ ├─ lib/
│ │ ├─ auth/ # Contrato AuthService + Supabase / Local
│ │ ├─ supabase/client.ts # Cliente Supabase
│ │ ├─ db/ # Dexie, repositorios (productos, ventas, ajustes), sync, sesión
│ │ ├─ print/ # Impresión por iframe aislado
│ │ └─ logic/ # Lógica PURA y testeable: frescura, PPC, ticket, carrito,
│ │ # reportes, WhatsApp, parser de voz
│ ├─ hooks/ # React Query + Web Speech API
│ ├─ stores/ # Zustand (estado de UI)
│ └─ types/ # Tipos de dominio
syncQueue como mutación pendiente.lib/db/sync.ts) vacía la cola hacia Supabase con
upsert idempotente cuando hay conexión, disparado por el evento online o
por Background Sync del Service Worker.Hecho para el mercado. 🇵🇪