@feedmepos/order-plugin-gallery 0.0.9 → 0.0.10-beta.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,3 @@
1
+ import { PluginFunction } from "@feedmepos/ordering-sdk";
2
+ declare const compulsoryItemsPlugin: PluginFunction;
3
+ export default compulsoryItemsPlugin;
@@ -0,0 +1,113 @@
1
+ const formatQty = (qty) => {
2
+ if (Number.isInteger(qty))
3
+ return `${qty}`;
4
+ return qty.toFixed(2).replace(/\.?0+$/, "");
5
+ };
6
+ const addQuantity = (quantityMap, itemId, quantity) => {
7
+ if (!itemId || typeof itemId !== "string")
8
+ return;
9
+ if (typeof quantity !== "number" || !Number.isFinite(quantity))
10
+ return;
11
+ if (quantity <= 0)
12
+ return;
13
+ quantityMap.set(itemId, (quantityMap.get(itemId) || 0) + quantity);
14
+ };
15
+ const getQuantityMap = (items) => {
16
+ const quantityMap = new Map();
17
+ for (const item of items) {
18
+ addQuantity(quantityMap, item.productId, item.quantity);
19
+ }
20
+ return quantityMap;
21
+ };
22
+ const getNormalizedRules = (rawRules) => {
23
+ if (!Array.isArray(rawRules))
24
+ return [];
25
+ const normalizedRules = [];
26
+ for (const rule of rawRules) {
27
+ const itemId = typeof rule?.itemId === "string"
28
+ ? rule.itemId.trim()
29
+ : "";
30
+ const minQty = rule?.minQty;
31
+ const maxQty = rule?.maxQty;
32
+ if (!itemId || !Number.isFinite(minQty) || !Number.isFinite(maxQty)) {
33
+ console.warn("[compulsory-items] Skip invalid rule. Expect { itemId, minQty, maxQty } with finite numbers.", rule);
34
+ continue;
35
+ }
36
+ if (minQty < 0 || maxQty < minQty) {
37
+ console.warn("[compulsory-items] Skip invalid rule. Expect minQty >= 0 and maxQty >= minQty.", rule);
38
+ continue;
39
+ }
40
+ normalizedRules.push({ itemId, minQty, maxQty });
41
+ }
42
+ return normalizedRules;
43
+ };
44
+ const getItemTitleMap = (sdk) => {
45
+ const titleMap = new Map();
46
+ const menuItems = (sdk.menuManager.state.value.menu?.items ||
47
+ []);
48
+ for (const menuItem of menuItems) {
49
+ const itemId = menuItem.id?.trim();
50
+ const title = menuItem.title?.trim();
51
+ if (!itemId || !title)
52
+ continue;
53
+ titleMap.set(itemId, title);
54
+ }
55
+ const rawItems = (sdk.menuManager.state.value.overridedMenu?.modules?.item ||
56
+ []);
57
+ for (const rawItem of rawItems) {
58
+ const itemId = rawItem._id?.trim();
59
+ const title = rawItem.name?.trim();
60
+ if (!itemId || !title || titleMap.has(itemId))
61
+ continue;
62
+ titleMap.set(itemId, title);
63
+ }
64
+ return titleMap;
65
+ };
66
+ const compulsoryItemsPlugin = ({ sdk, onBeforeSubmitOrder, pluginParams, }) => {
67
+ onBeforeSubmitOrder(() => {
68
+ const params = (pluginParams || {});
69
+ const rules = getNormalizedRules(params.itemLimitConfig);
70
+ if (rules.length === 0)
71
+ return true;
72
+ const submittedItems = (sdk.orderManager.state.value.slotActiveBills?.[0]?.items ||
73
+ []);
74
+ const draftItems = (sdk.orderManager.state.value.order?.draft || []);
75
+ const submittedQtyByItemId = getQuantityMap(submittedItems);
76
+ const draftQtyByItemId = getQuantityMap(draftItems);
77
+ const itemTitleMap = getItemTitleMap(sdk);
78
+ const violationMessages = [];
79
+ for (const rule of rules) {
80
+ const submittedQty = submittedQtyByItemId.get(rule.itemId) || 0;
81
+ const draftQty = draftQtyByItemId.get(rule.itemId) || 0;
82
+ const totalQty = submittedQty + draftQty;
83
+ const itemTitle = itemTitleMap.get(rule.itemId) || rule.itemId;
84
+ if (totalQty < rule.minQty) {
85
+ const delta = rule.minQty - totalQty;
86
+ violationMessages.push(`Add ${formatQty(delta)} more "${itemTitle}" (minimum ${formatQty(rule.minQty)}, current total ${formatQty(totalQty)}).`);
87
+ }
88
+ if (submittedQty > rule.maxQty) {
89
+ if (draftQty > 0) {
90
+ violationMessages.push(`"${itemTitle}" is already above the limit in submitted orders. Remove ${formatQty(draftQty)} from your current cart to continue.`);
91
+ }
92
+ continue;
93
+ }
94
+ if (totalQty > rule.maxQty) {
95
+ const delta = totalQty - rule.maxQty;
96
+ violationMessages.push(`Remove ${formatQty(delta)} "${itemTitle}" from your current cart (maximum ${formatQty(rule.maxQty)}, current total ${formatQty(totalQty)}).`);
97
+ }
98
+ }
99
+ if (violationMessages.length === 0) {
100
+ return true;
101
+ }
102
+ sdk.ui.toast.show({
103
+ title: [
104
+ "Please update your cart before submitting:",
105
+ ...violationMessages.map((message) => `- ${message}`),
106
+ ].join("\n"),
107
+ type: "error",
108
+ duration: 6000,
109
+ });
110
+ return false;
111
+ });
112
+ };
113
+ export default compulsoryItemsPlugin;
@@ -1,29 +1,28 @@
1
1
  const setMealValidationPlugin = ({ sdk, onBeforeAddToCart, onBeforeSubmitOrder, pluginParams, }) => {
2
- console.log('[ItemRestriction Plugin] Loaded');
2
+ console.log('[ItemRestriction Plugin] Loaded (Simplified Version)');
3
3
  const REQUIRED_CATEGORY_IDS = pluginParams?.requiredCategoryIds || [];
4
4
  const RESTRICTED_CATEGORY_IDS = pluginParams?.restrictedCategoryIds || [];
5
5
  const errorMessage = pluginParams?.message || 'Please order items from required category first';
6
6
  const getMenu = () => sdk?.menuManager?.state?.value?.overridedMenu;
7
7
  const getCategoryIdForItem = (item, menu) => {
8
- if (!menu?.modules?.item)
9
- return null;
10
- // Try direct categoryId first
11
- if (item.categoryId)
12
- return item.categoryId;
13
- // Also check category property which exists in some models
14
- if (item.category && typeof item.category === 'string')
15
- return item.category;
16
- // Look up by productId (in cart) or id/_id (in menu/item)
17
- const itemProductId = item.productId || item.id || item._id;
18
- if (!itemProductId)
19
- return null;
20
- // Find item in menu modules
21
- const menuItem = menu.modules.item.find((i) => i._id === itemProductId || i.id === itemProductId);
22
- return menuItem?.category || null;
8
+ // Trust the enriched item from plugin-loader, or check the normalized menu modules
9
+ if (item.categoryId || item.category) {
10
+ return item.categoryId || item.category;
11
+ }
12
+ // Fallback: Check if the item exists in the normalized modules.item list
13
+ // This handles cases where 'item' is a fresh cart object that hasn't been enriched yet,
14
+ // but the menu has the enriched version.
15
+ if (menu?.modules?.item) {
16
+ const itemProductId = item.productId || item.id || item._id;
17
+ const menuItem = menu.modules.item.find((i) => i._id === itemProductId || i.id === itemProductId);
18
+ if (menuItem) {
19
+ return menuItem.categoryId || menuItem.category || null;
20
+ }
21
+ }
22
+ return null;
23
23
  };
24
24
  onBeforeAddToCart(async (item) => {
25
25
  console.log('[ItemRestriction Plugin] Checking item:', item.title || item.name);
26
- console.log('[ItemRestriction Plugin] Item ID:', item.id || item._id);
27
26
  const menu = getMenu();
28
27
  if (!menu) {
29
28
  console.log('[ItemRestriction Plugin] No menu found');
@@ -31,33 +30,24 @@ const setMealValidationPlugin = ({ sdk, onBeforeAddToCart, onBeforeSubmitOrder,
31
30
  }
32
31
  const itemCategoryId = getCategoryIdForItem(item, menu);
33
32
  const isRestricted = itemCategoryId && RESTRICTED_CATEGORY_IDS.includes(itemCategoryId);
34
- if (!isRestricted) {
35
- console.log('[ItemRestriction Plugin] Item not in restricted category, allowing');
33
+ console.log('[ItemRestriction Plugin] Category check:', {
34
+ itemName: item.title || item.name,
35
+ itemId: item.id || item._id,
36
+ resolvedCategoryId: itemCategoryId,
37
+ isRestricted
38
+ });
39
+ if (!isRestricted)
36
40
  return true;
37
- }
38
41
  const slotActiveBills = sdk?.orderManager?.state?.value?.slotActiveBills || [];
39
- // Use flatMap if available, or fallback to reduce/concat
40
42
  const orderedItems = slotActiveBills.flatMap
41
43
  ? slotActiveBills.flatMap((bill) => bill?.items || [])
42
44
  : slotActiveBills.reduce((acc, bill) => acc.concat(bill?.items || []), []);
43
45
  const draft = sdk?.orderManager?.state?.value?.order?.draft || [];
44
- console.log('[ItemRestriction Plugin] Checking order state:', {
45
- draftCount: draft.length,
46
- billsCount: slotActiveBills.length,
47
- orderedItemsCount: orderedItems.length,
48
- totalItems: draft.length + orderedItems.length
49
- });
50
46
  const allItems = [...orderedItems, ...draft];
51
47
  const requiredCategoryItemsFound = allItems.some((orderItem) => {
52
48
  const catId = getCategoryIdForItem(orderItem, menu);
53
- console.log('[ItemRestriction Plugin] Item check:', {
54
- productId: orderItem.productId || orderItem.id || orderItem._id,
55
- foundCategoryId: catId,
56
- isRequired: catId && REQUIRED_CATEGORY_IDS.includes(catId)
57
- });
58
49
  return catId && REQUIRED_CATEGORY_IDS.includes(catId);
59
50
  });
60
- console.log('[ItemRestriction Plugin] Required category items found?', requiredCategoryItemsFound);
61
51
  if (requiredCategoryItemsFound) {
62
52
  console.log('[ItemRestriction Plugin] Required category items already ordered, allowing item');
63
53
  return true;
@@ -67,13 +57,11 @@ const setMealValidationPlugin = ({ sdk, onBeforeAddToCart, onBeforeSubmitOrder,
67
57
  return false;
68
58
  });
69
59
  onBeforeSubmitOrder(async () => {
70
- console.log('[ItemRestriction Plugin] onBeforeSubmitOrder called');
71
60
  const slotActiveBills = sdk?.orderManager?.state?.value?.slotActiveBills || [];
72
61
  const orderedItems = slotActiveBills.flatMap
73
62
  ? slotActiveBills.flatMap((bill) => bill?.items || [])
74
63
  : slotActiveBills.reduce((acc, bill) => acc.concat(bill?.items || []), []);
75
64
  const draft = sdk?.orderManager?.state?.value?.order?.draft || [];
76
- console.log('[ItemRestriction Plugin] Checking order with', orderedItems.length + draft.length, 'items');
77
65
  if (orderedItems.length === 0 && draft.length === 0)
78
66
  return true;
79
67
  const menu = getMenu();
@@ -84,16 +72,13 @@ const setMealValidationPlugin = ({ sdk, onBeforeAddToCart, onBeforeSubmitOrder,
84
72
  const catId = getCategoryIdForItem(item, menu);
85
73
  return catId && RESTRICTED_CATEGORY_IDS.includes(catId);
86
74
  });
87
- console.log('[ItemRestriction Plugin] Has restricted category items in order?', hasRestrictedCategoryItems);
88
75
  if (!hasRestrictedCategoryItems)
89
76
  return true;
90
77
  const requiredCategoryItemsFound = allItems.some((item) => {
91
78
  const catId = getCategoryIdForItem(item, menu);
92
79
  return catId && REQUIRED_CATEGORY_IDS.includes(catId);
93
80
  });
94
- console.log('[ItemRestriction Plugin] Required category items found?', requiredCategoryItemsFound);
95
81
  if (!requiredCategoryItemsFound) {
96
- console.log('[ItemRestriction Plugin] No required category items in order, blocking checkout');
97
82
  sdk?.ui?.toast?.show?.({ title: errorMessage, type: "error", duration: 5000 });
98
83
  return false;
99
84
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feedmepos/order-plugin-gallery",
3
- "version": "0.0.9",
3
+ "version": "0.0.10-beta.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -24,6 +24,10 @@
24
24
  "./menu-debug-logger": {
25
25
  "import": "./dist/plugins/menu-debug-logger.js",
26
26
  "types": "./dist/plugins/menu-debug-logger.d.ts"
27
+ },
28
+ "./compulsory-items": {
29
+ "import": "./dist/plugins/compulsory-items.js",
30
+ "types": "./dist/plugins/compulsory-items.d.ts"
27
31
  }
28
32
  },
29
33
  "peerDependencies": {