@fayz-ai/plugin-menu 0.10.17 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ import type { MenuCategory, MenuItem } from '../types';
2
+ import type { MenuDataProvider } from './types';
3
+ /** Uma cópia do cardápio, com a hora em que ela saiu do banco. */
4
+ export interface MenuCatalog {
5
+ items: MenuItem[];
6
+ categories: MenuCategory[];
7
+ /** Epoch ms de quando esta cópia foi lida. */
8
+ fetchedAt: number;
9
+ }
10
+ /** Depois disto a cópia continua servindo, mas pede revalidação por trás. */
11
+ export declare const MENU_CATALOG_TTL: number;
12
+ export type CatalogAge = 'missing' | 'fresh' | 'stale';
13
+ /**
14
+ * A regra pura: esta cópia serve? precisa revalidar?
15
+ *
16
+ * `missing` é o único caso em que a tela espera. `stale` serve e revalida —
17
+ * nunca volta ao esqueleto.
18
+ */
19
+ export declare function catalogAge(catalog: MenuCatalog | undefined | null, now: number, ttlMs?: number): CatalogAge;
20
+ /** A conta e a unidade mandam: um cardápio por casa, e a unidade recorta. */
21
+ export declare function menuCatalogKey(tenantId?: string | null, unitId?: string | null): string;
22
+ export declare function readCachedCatalog(key: string): MenuCatalog | undefined;
23
+ /** Joga fora a cópia — o cadastro mexeu no cardápio, ou o operador pediu. */
24
+ export declare function invalidateMenuCatalog(key?: string): void;
25
+ /**
26
+ * O cardápio INTEIRO, em páginas.
27
+ *
28
+ * `pageSize: 1000` cravado escondia 381 dos 1.381 itens do Pertinho: o
29
+ * PostgREST devolve o `total` e ninguém o lia, então o terceiro terço do
30
+ * cardápio simplesmente não existia no PDV. Aqui a primeira página diz quantas
31
+ * faltam e as restantes vão juntas.
32
+ */
33
+ export declare function fetchMenuCatalog(provider: Pick<MenuDataProvider, 'getMenuItems' | 'getCategories'>, now?: () => number): Promise<MenuCatalog>;
34
+ export interface LoadMenuCatalogOptions {
35
+ /** Ignora a cópia e vai ao banco — o gesto explícito de «atualizar». */
36
+ force?: boolean;
37
+ /** Depois de quanto tempo a cópia é velha. */
38
+ ttlMs?: number;
39
+ now?: () => number;
40
+ }
41
+ /**
42
+ * A porta única: devolve a cópia boa sem ir ao banco, ou vai buscar.
43
+ *
44
+ * Quem quer o comportamento «serve o velho e revalida por trás» lê
45
+ * `readCachedCatalog` primeiro e chama isto sem esperar — é o que o hook do
46
+ * PDV faz. Quem só precisa de um cardápio qualquer (o mapa produto→categoria
47
+ * da isenção de taxa, por exemplo) chama direto e quase sempre acerta o cache.
48
+ */
49
+ export declare function loadMenuCatalog(provider: Pick<MenuDataProvider, 'getMenuItems' | 'getCategories'>, key: string, options?: LoadMenuCatalogOptions): Promise<MenuCatalog>;
50
+ //# sourceMappingURL=catalog-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-cache.d.ts","sourceRoot":"","sources":["../../src/data/catalog-cache.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,UAAU,EAAE,YAAY,EAAE,CAAA;IAC1B,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,QAAa,CAAA;AAa1C,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;AAEtD;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,EACvC,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,MAAyB,GAC/B,UAAU,CAMZ;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEvF;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAEtE;AAED,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAExD;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,cAAc,GAAG,eAAe,CAAC,EAClE,GAAG,GAAE,MAAM,MAAiB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAmBtB;AAKD,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,cAAc,GAAG,eAAe,CAAC,EAClE,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,WAAW,CAAC,CAkBtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AA+GnD,MAAM,WAAW,2BAA2B;IAC1C,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,2BAA2B,GAAG,oBAAoB,CAuetG"}
1
+ {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAqHnD,MAAM,WAAW,2BAA2B;IAC1C,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,2BAA2B,GAAG,oBAAoB,CAkgBtG"}
package/dist/index.d.ts CHANGED
@@ -29,6 +29,8 @@ export interface MenuPluginOptions {
29
29
  }
30
30
  export declare function createMenuPlugin(options?: MenuPluginOptions): PluginManifest;
31
31
  export type { MenuDataProvider } from './data/types';
32
+ export { MENU_CATALOG_TTL, catalogAge, fetchMenuCatalog, invalidateMenuCatalog, loadMenuCatalog, menuCatalogKey, readCachedCatalog, } from './data/catalog-cache';
33
+ export type { CatalogAge, LoadMenuCatalogOptions, MenuCatalog } from './data/catalog-cache';
32
34
  export { createFayzMenuProvider } from './data/platform';
33
35
  export type { FayzMenuProviderOptions } from './data/platform';
34
36
  export type { ResolvedMenuConfig } from './context';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAElD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAEpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AAK5D,OAAO,EAAE,0BAA0B,EAAE,CAAA;AAerC,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;CAChB;AAmBD,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,OAAO,CAAA;QACnB,eAAe,CAAC,EAAE,OAAO,CAAA;KAC1B,CAAA;IACD,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAClC,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,gBAAgB,CAAA;IAC/B,cAAc,CAAC,EAAE,YAAY,CAAA;CAC9B;AAqBD,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAqI5E;AAED,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAGnD,mBAAmB,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAElD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAEpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AAK5D,OAAO,EAAE,0BAA0B,EAAE,CAAA;AAerC,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;CAChB;AAmBD,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,OAAO,CAAA;QACnB,eAAe,CAAC,EAAE,OAAO,CAAA;KAC1B,CAAA;IACD,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAClC,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,gBAAgB,CAAA;IAC/B,cAAc,CAAC,EAAE,YAAY,CAAA;CAC9B;AAqBD,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAqI5E;AAED,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAEpD,OAAO,EACL,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,EAAE,qBAAqB,EACrE,eAAe,EAAE,cAAc,EAAE,iBAAiB,GACnD,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EAAE,UAAU,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAC3F,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAGnD,mBAAmB,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { normalizeName, countExtracted } from './chunk-FVDAVCK4.js';
2
2
  import React from 'react';
3
- import { registerTranslations, createSafeDataProvider, tl, getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core';
3
+ import { stableKey, globalCache, registerTranslations, createSafeDataProvider, tl, getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core';
4
4
  import { createStore } from 'zustand/vanilla';
5
5
  import { toast } from 'sonner';
6
6
  import { PluginSettingsPanel, dedup } from '@fayz-ai/admin';
@@ -475,6 +475,58 @@ function createMockMenuProvider() {
475
475
  };
476
476
  return provider;
477
477
  }
478
+ var MENU_CATALOG_TTL = 5 * 6e4;
479
+ var STORE_TTL = 12 * 60 * 6e4;
480
+ var PAGE_SIZE = 1e3;
481
+ function catalogAge(catalog, now2, ttlMs = MENU_CATALOG_TTL) {
482
+ if (!catalog) return "missing";
483
+ const age = Math.max(0, now2 - catalog.fetchedAt);
484
+ return age >= ttlMs ? "stale" : "fresh";
485
+ }
486
+ function menuCatalogKey(tenantId, unitId) {
487
+ return `menu-catalog:${stableKey({ t: tenantId ?? "_", u: unitId ?? "_" })}`;
488
+ }
489
+ function readCachedCatalog(key) {
490
+ return globalCache.get(key);
491
+ }
492
+ function invalidateMenuCatalog(key) {
493
+ globalCache.invalidate(key ?? "menu-catalog:");
494
+ }
495
+ async function fetchMenuCatalog(provider, now2 = Date.now) {
496
+ const [categories, first] = await Promise.all([
497
+ provider.getCategories(),
498
+ // `projection: 'summary'` corta ~80% do payload: o PDV usa nome, preço,
499
+ // código, foto e categoria, e não a ficha inteira do produto.
500
+ provider.getMenuItems({ pageSize: PAGE_SIZE, page: 1, projection: "summary" })
501
+ ]);
502
+ const items = [...first.data];
503
+ const totalPages = Math.max(1, Math.ceil((first.total || items.length) / PAGE_SIZE));
504
+ if (totalPages > 1) {
505
+ const rest = await Promise.all(
506
+ Array.from({ length: totalPages - 1 }, (_, i) => provider.getMenuItems({ pageSize: PAGE_SIZE, page: i + 2, projection: "summary" }))
507
+ );
508
+ for (const page of rest) items.push(...page.data);
509
+ }
510
+ return { items, categories, fetchedAt: now2() };
511
+ }
512
+ var inFlight = /* @__PURE__ */ new Map();
513
+ function loadMenuCatalog(provider, key, options) {
514
+ const now2 = options?.now ?? Date.now;
515
+ if (!options?.force) {
516
+ const cached = readCachedCatalog(key);
517
+ if (catalogAge(cached, now2(), options?.ttlMs) === "fresh") return Promise.resolve(cached);
518
+ }
519
+ const running = inFlight.get(key);
520
+ if (running) return running;
521
+ const task = fetchMenuCatalog(provider, now2).then((catalog) => {
522
+ globalCache.set(key, catalog, STORE_TTL);
523
+ return catalog;
524
+ }).finally(() => {
525
+ inFlight.delete(key);
526
+ });
527
+ inFlight.set(key, task);
528
+ return task;
529
+ }
478
530
 
479
531
  // src/data/tables.ts
480
532
  var T = {
@@ -564,6 +616,11 @@ function rowToItem(row, currency) {
564
616
  sku: row.sku ?? void 0,
565
617
  imageUrl: row.image_url ?? void 0,
566
618
  categoryId: row.category_id ?? void 0,
619
+ // O nome da categoria vem JUNTO do item, pela relação, e não de uma
620
+ // segunda leitura. `MenuItem.categoryName` existia no tipo e nunca era
621
+ // preenchido: quem consumia caía num rótulo genérico e o segmentado do PDV
622
+ // saía com quinze abas escritas "Cardápio". Campo vazio na tela é sintoma.
623
+ categoryName: (Array.isArray(row.categories) ? row.categories[0] : row.categories)?.name ?? void 0,
567
624
  status: ext?.status ?? "available",
568
625
  isActive: row.is_active ?? true,
569
626
  prepTimeMinutes: ext?.prep_time_minutes ?? void 0,
@@ -831,7 +888,8 @@ function createSupabaseMenuProvider(options) {
831
888
  },
832
889
  // --- Items -------------------------------------------------------------
833
890
  async getMenuItems(query) {
834
- let qb = db().from(T.products).select(`*, ${T.items}!inner(*)`, { count: "exact" });
891
+ const columns = query.projection === "summary" ? `id, tenant_id, name, price, sku, image_url, category_id, is_active, created_at, updated_at, ${T.items}!inner(status, available_for_pos, available_for_delivery, sort_order, is_featured), ${T.categories}(name)` : `*, ${T.items}!inner(*), ${T.categories}(name)`;
892
+ let qb = db().from(T.products).select(columns, { count: "exact" });
835
893
  if (query.search) qb = qb.ilike("name", `%${query.search}%`);
836
894
  if (query.categoryId) qb = qb.eq("category_id", query.categoryId);
837
895
  if (query.status) {
@@ -849,7 +907,7 @@ function createSupabaseMenuProvider(options) {
849
907
  return { data: (data ?? []).map((row) => rowToItem(row, currency)), total: count ?? 0 };
850
908
  },
851
909
  async getMenuItemById(id) {
852
- const { data } = await db().from(T.products).select(`*, ${T.items}(*)`).eq("id", id).maybeSingle();
910
+ const { data } = await db().from(T.products).select(`*, ${T.items}(*), ${T.categories}(name)`).eq("id", id).maybeSingle();
853
911
  return data ? rowToItem(data, currency) : null;
854
912
  },
855
913
  async createMenuItem(input) {
@@ -1003,8 +1061,26 @@ function createSupabaseMenuProvider(options) {
1003
1061
  };
1004
1062
  }
1005
1063
  };
1064
+ for (const nome of MUTACOES_DE_CATALOGO) {
1065
+ const original = provider[nome];
1066
+ if (!original) continue;
1067
+ provider[nome] = async (...args) => {
1068
+ const out = await original.apply(provider, args);
1069
+ invalidateMenuCatalog();
1070
+ return out;
1071
+ };
1072
+ }
1006
1073
  return provider;
1007
1074
  }
1075
+ var MUTACOES_DE_CATALOGO = [
1076
+ "createMenuItem",
1077
+ "updateMenuItem",
1078
+ "deleteMenuItem",
1079
+ "toggleAvailability",
1080
+ "createCategory",
1081
+ "updateCategory",
1082
+ "deleteCategory"
1083
+ ];
1008
1084
 
1009
1085
  // src/migrations/index.ts
1010
1086
  var MIGRATION_000_BASELINE = `-- ============================================================================
@@ -2997,7 +3073,7 @@ function createFayzMenuProvider(options = {}) {
2997
3073
  }
2998
3074
 
2999
3075
  // src/index.ts
3000
- var MenuPage = React.lazy(() => import('./MenuPage-GC6ATJTT.js').then((m) => ({ default: m.MenuPage })));
3076
+ var MenuPage = React.lazy(() => import('./MenuPage-JHEEZ5BB.js').then((m) => ({ default: m.MenuPage })));
3001
3077
  var DEFAULT_LABELS = {
3002
3078
  pageTitle: "Menu",
3003
3079
  menuManager: "Menu Manager",
@@ -3146,6 +3222,6 @@ function createMenuPlugin(options) {
3146
3222
  };
3147
3223
  }
3148
3224
 
3149
- export { createFayzMenuProvider, createMenuPlugin, createSupabaseMenuProvider };
3225
+ export { MENU_CATALOG_TTL, catalogAge, createFayzMenuProvider, createMenuPlugin, createSupabaseMenuProvider, fetchMenuCatalog, invalidateMenuCatalog, loadMenuCatalog, menuCatalogKey, readCachedCatalog };
3150
3226
  //# sourceMappingURL=index.js.map
3151
3227
  //# sourceMappingURL=index.js.map