@feedmepos/mf-order-setting 0.0.83 → 0.0.84

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.
Files changed (75) hide show
  1. package/dist/{DisplaySystemView-BXy_OnbY.js → DisplaySystemView-4Bz8p26A.js} +1 -1
  2. package/dist/{KioskDevicesView-D3YR1e6K.js → KioskDevicesView-DdwFuenU.js} +1 -1
  3. package/dist/{KioskDevicesView.vue_vue_type_script_setup_true_lang-CuhL_Q5C.js → KioskDevicesView.vue_vue_type_script_setup_true_lang-CxtLm_oh.js} +3 -3
  4. package/dist/{KioskSettingView-C3L9_8ag.js → KioskSettingView-D57KtQ-t.js} +289 -240
  5. package/dist/{KioskView-Cs6VBCj5.js → KioskView-fwBwUGCQ.js} +113 -103
  6. package/dist/{OrderSettingsView-BdaHRXda.js → OrderSettingsView-RhlyZmzs.js} +1772 -1766
  7. package/dist/{app-CP6AFTEu.js → app-ClOkRxOZ.js} +26 -6
  8. package/dist/app.js +1 -1
  9. package/dist/common/util/index.d.ts +9 -0
  10. package/dist/frontend/mf-order/src/app.d.ts +20 -0
  11. package/dist/frontend/mf-order/src/main.d.ts +20 -0
  12. package/dist/frontend/mf-order/src/modules/order-setting/kiosk/interface.d.ts +2 -0
  13. package/dist/frontend/mf-order/src/modules/order-setting/kiosk/languages.d.ts +7 -0
  14. package/dist/frontend/mf-order/src/stores/order-setting/index.d.ts +6 -0
  15. package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/ExternalSetting.vue.d.ts +8 -0
  16. package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/IntegratedDelivery.vue.d.ts +9 -0
  17. package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +1 -1
  18. package/dist/{index-LCS8oexE.js → index-5k7kGCpJ.js} +2 -2
  19. package/dist/{index-BckeUsPc.js → index-CKEbr9Es.js} +1 -1
  20. package/dist/{index-DoJzzpyh.js → index-I2YGevce.js} +1 -1
  21. package/dist/{order.dto-Bm04C1sQ.js → order.dto-Dbn-hRrm.js} +3294 -3235
  22. package/dist/package/entity/alert/alert-base.dto.d.ts +306 -0
  23. package/dist/package/entity/alert/alert.dto.d.ts +956 -0
  24. package/dist/package/entity/alert/alert.enum.d.ts +7 -0
  25. package/dist/package/entity/alert/categories/integration.d.ts +333 -0
  26. package/dist/package/entity/alert/categories/kiosk.d.ts +297 -0
  27. package/dist/package/entity/alert/categories/mini-program.d.ts +297 -0
  28. package/dist/package/entity/alert/investigation.dto.d.ts +86 -0
  29. package/dist/package/entity/alert/lark-callback.dto.d.ts +106 -0
  30. package/dist/package/entity/alert/suggestion.dto.d.ts +31 -0
  31. package/dist/package/entity/alert/suggestion.enum.d.ts +7 -0
  32. package/dist/package/entity/incoming-order/incoming-order-to-bill.dto.d.ts +80 -0
  33. package/dist/package/entity/incoming-order/incoming-order.dto.d.ts +575 -0
  34. package/dist/package/entity/index.d.ts +12 -0
  35. package/dist/package/entity/index.frontend.d.ts +1 -0
  36. package/dist/package/entity/member/member.dto.d.ts +2 -1
  37. package/dist/package/entity/order/order.do.d.ts +64 -7
  38. package/dist/package/entity/order/order.dto.d.ts +85 -67
  39. package/dist/package/entity/order/payment/payment.dto.d.ts +36 -28
  40. package/dist/package/entity/order-platform/external/auth/external-auth.dto.d.ts +9 -15
  41. package/dist/package/entity/order-platform/external/menu/external-master-menu.do.d.ts +33 -0
  42. package/dist/package/entity/order-platform/external/menu/external-menu.do.d.ts +1185 -23
  43. package/dist/package/entity/order-platform/external/order/external-order.do.d.ts +1104 -52
  44. package/dist/package/entity/order-platform/external/order/external-order.dto.d.ts +2914 -88
  45. package/dist/package/entity/order-platform/external/setting/external-setting.do.d.ts +38 -0
  46. package/dist/package/entity/order-platform/external/setting/external-setting.dto.d.ts +26 -0
  47. package/dist/package/entity/order-platform/foodpanda/foodpanda-availability.dto.d.ts +75 -0
  48. package/dist/package/entity/order-platform/foodpanda/foodpanda-order.do.d.ts +9 -0
  49. package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.do.d.ts +13 -0
  50. package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.dto.d.ts +3 -0
  51. package/dist/package/entity/order-platform/shopeefood/shopeefood.enum.d.ts +6 -0
  52. package/dist/package/entity/order-platform/tng/tng-order.do.d.ts +6 -6
  53. package/dist/package/entity/order-platform/tng/tng.dto.d.ts +2 -2
  54. package/dist/package/entity/order-setting/kiosk/kiosk.do.d.ts +3 -0
  55. package/dist/package/entity/order-setting/kiosk/kiosk.dto.d.ts +3 -0
  56. package/dist/package/entity/order-setting/kiosk/kiosk.enum.d.ts +12 -0
  57. package/dist/package/entity/order-setting/order-setting.do.d.ts +5 -0
  58. package/dist/package/entity/order-setting/order-setting.dto.d.ts +10 -0
  59. package/dist/package/entity/payment/payment.dto.d.ts +2 -2
  60. package/dist/package/entity/payment/transaction.d.ts +115 -30
  61. package/dist/package/entity/queue/queue.dto.d.ts +13 -11
  62. package/dist/package/entity/restaurant/restaurant.dto.d.ts +5 -0
  63. package/dist/package/entity/websocket/websocket.dto.d.ts +12 -0
  64. package/package.json +1 -1
  65. package/src/locales/en-US.json +5 -0
  66. package/src/locales/ja-JP.json +5 -0
  67. package/src/locales/th-TH.json +5 -0
  68. package/src/locales/zh-CN.json +5 -0
  69. package/src/modules/order-setting/kiosk/interface.ts +8 -0
  70. package/src/modules/order-setting/kiosk/languages.ts +29 -0
  71. package/src/stores/order-setting/mapper.spec.ts +44 -0
  72. package/src/stores/order-setting/mapper.ts +9 -2
  73. package/src/views/kiosk/settings/KioskSettingView.vue +63 -0
  74. package/src/views/order-settings/delivery/integrated-delivery/IntegratedDelivery.vue +6 -1
  75. package/src/views/order-settings/delivery/integrated-delivery/LinemanSetting.vue +2 -1
@@ -493,6 +493,7 @@ export declare const WebSocketEventDto: z.ZodDiscriminatedUnion<"type", z.Primit
493
493
  otaChannel: z.ZodNullable<z.ZodOptional<z.ZodString>>;
494
494
  autoCloseBill: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
495
495
  printReceiptAfterClose: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
496
+ languages: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
496
497
  }, "strip", z.ZodTypeAny, {
497
498
  autoCloseBill?: boolean | null | undefined;
498
499
  printReceiptAfterClose?: boolean | null | undefined;
@@ -554,6 +555,7 @@ export declare const WebSocketEventDto: z.ZodDiscriminatedUnion<"type", z.Primit
554
555
  coverImageLandscape?: string | null | undefined;
555
556
  coverImagePortrait?: string | null | undefined;
556
557
  otaChannel?: string | null | undefined;
558
+ languages?: string[] | null | undefined;
557
559
  }, {
558
560
  autoCloseBill?: boolean | null | undefined;
559
561
  printReceiptAfterClose?: boolean | null | undefined;
@@ -615,6 +617,7 @@ export declare const WebSocketEventDto: z.ZodDiscriminatedUnion<"type", z.Primit
615
617
  coverImageLandscape?: string | null | undefined;
616
618
  coverImagePortrait?: string | null | undefined;
617
619
  otaChannel?: string | null | undefined;
620
+ languages?: string[] | null | undefined;
618
621
  }>>;
619
622
  }>, "strip", z.ZodTypeAny, {
620
623
  data: {
@@ -678,6 +681,7 @@ export declare const WebSocketEventDto: z.ZodDiscriminatedUnion<"type", z.Primit
678
681
  coverImageLandscape?: string | null | undefined;
679
682
  coverImagePortrait?: string | null | undefined;
680
683
  otaChannel?: string | null | undefined;
684
+ languages?: string[] | null | undefined;
681
685
  };
682
686
  type: "KIOSK:UPDATE_SETTING";
683
687
  }, {
@@ -742,6 +746,7 @@ export declare const WebSocketEventDto: z.ZodDiscriminatedUnion<"type", z.Primit
742
746
  coverImageLandscape?: string | null | undefined;
743
747
  coverImagePortrait?: string | null | undefined;
744
748
  otaChannel?: string | null | undefined;
749
+ languages?: string[] | null | undefined;
745
750
  } | undefined;
746
751
  type: "KIOSK:UPDATE_SETTING";
747
752
  }>>;
@@ -1204,6 +1209,7 @@ export declare const WebSocketReqDto: z.ZodObject<{
1204
1209
  otaChannel: z.ZodNullable<z.ZodOptional<z.ZodString>>;
1205
1210
  autoCloseBill: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
1206
1211
  printReceiptAfterClose: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
1212
+ languages: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
1207
1213
  }, "strip", z.ZodTypeAny, {
1208
1214
  autoCloseBill?: boolean | null | undefined;
1209
1215
  printReceiptAfterClose?: boolean | null | undefined;
@@ -1265,6 +1271,7 @@ export declare const WebSocketReqDto: z.ZodObject<{
1265
1271
  coverImageLandscape?: string | null | undefined;
1266
1272
  coverImagePortrait?: string | null | undefined;
1267
1273
  otaChannel?: string | null | undefined;
1274
+ languages?: string[] | null | undefined;
1268
1275
  }, {
1269
1276
  autoCloseBill?: boolean | null | undefined;
1270
1277
  printReceiptAfterClose?: boolean | null | undefined;
@@ -1326,6 +1333,7 @@ export declare const WebSocketReqDto: z.ZodObject<{
1326
1333
  coverImageLandscape?: string | null | undefined;
1327
1334
  coverImagePortrait?: string | null | undefined;
1328
1335
  otaChannel?: string | null | undefined;
1336
+ languages?: string[] | null | undefined;
1329
1337
  }>>;
1330
1338
  }>, "strip", z.ZodTypeAny, {
1331
1339
  data: {
@@ -1389,6 +1397,7 @@ export declare const WebSocketReqDto: z.ZodObject<{
1389
1397
  coverImageLandscape?: string | null | undefined;
1390
1398
  coverImagePortrait?: string | null | undefined;
1391
1399
  otaChannel?: string | null | undefined;
1400
+ languages?: string[] | null | undefined;
1392
1401
  };
1393
1402
  type: "KIOSK:UPDATE_SETTING";
1394
1403
  }, {
@@ -1453,6 +1462,7 @@ export declare const WebSocketReqDto: z.ZodObject<{
1453
1462
  coverImageLandscape?: string | null | undefined;
1454
1463
  coverImagePortrait?: string | null | undefined;
1455
1464
  otaChannel?: string | null | undefined;
1465
+ languages?: string[] | null | undefined;
1456
1466
  } | undefined;
1457
1467
  type: "KIOSK:UPDATE_SETTING";
1458
1468
  }>>;
@@ -1565,6 +1575,7 @@ export declare const WebSocketReqDto: z.ZodObject<{
1565
1575
  coverImageLandscape?: string | null | undefined;
1566
1576
  coverImagePortrait?: string | null | undefined;
1567
1577
  otaChannel?: string | null | undefined;
1578
+ languages?: string[] | null | undefined;
1568
1579
  };
1569
1580
  type: "KIOSK:UPDATE_SETTING";
1570
1581
  };
@@ -1678,6 +1689,7 @@ export declare const WebSocketReqDto: z.ZodObject<{
1678
1689
  coverImageLandscape?: string | null | undefined;
1679
1690
  coverImagePortrait?: string | null | undefined;
1680
1691
  otaChannel?: string | null | undefined;
1692
+ languages?: string[] | null | undefined;
1681
1693
  } | undefined;
1682
1694
  type: "KIOSK:UPDATE_SETTING";
1683
1695
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feedmepos/mf-order-setting",
3
- "version": "0.0.83",
3
+ "version": "0.0.84",
4
4
  "type": "module",
5
5
  "module": "./dist/app.js",
6
6
  "license": "UNLICENSED",
@@ -313,6 +313,11 @@
313
313
  "kioskDevices": "Kiosk devices",
314
314
  "kioskDisplay": "Kiosk Display",
315
315
  "kioskDisplayDescription": "Click \"Bind device\" to receive a special code for device binding.",
316
+ "kioskLanguage": "Language",
317
+ "kioskLanguageAtLeastOne": "Select at least one language, or turn the limit off.",
318
+ "kioskLanguageHint": "Choose which languages customers can pick from on the kiosk. By default the kiosk offers every language it supports.",
319
+ "kioskLanguageRestrict": "Limit the languages shown",
320
+ "kioskLanguageRestrictSublabel": "Off: show every supported language. On: show only the ones ticked below.",
316
321
  "kioskOnly": "Kiosk Only",
317
322
  "kioskSettingFailed": "Failed to update kiosk order setting",
318
323
  "kioskSettingSuccess": "Kiosk order setting updated",
@@ -399,6 +399,11 @@
399
399
  "searchDevices": "スロット、マシンID、名前で検索...",
400
400
  "allTypes": "すべてのタイプ",
401
401
  "tabletOnly": "タブレットのみ",
402
+ "kioskLanguage": "言語",
403
+ "kioskLanguageAtLeastOne": "言語を1つ以上選択するか、制限をオフにしてください。",
404
+ "kioskLanguageHint": "キオスクでお客様が選べる言語を設定します。既定では対応するすべての言語を表示します。",
405
+ "kioskLanguageRestrict": "表示する言語を制限する",
406
+ "kioskLanguageRestrictSublabel": "オフ:対応するすべての言語を表示。オン:下でチェックした言語のみ表示。",
402
407
  "kioskOnly": "キオスクのみ",
403
408
  "showingDevices": "{count} / {total} 件表示",
404
409
  "noDevicesMatch": "検索条件に一致するデバイスはありません",
@@ -494,6 +494,11 @@
494
494
  "searchDevices": "ค้นหาตามสล็อต, ID เครื่อง หรือชื่อ...",
495
495
  "allTypes": "ทุกประเภท",
496
496
  "tabletOnly": "แท็บเล็ตเท่านั้น",
497
+ "kioskLanguage": "ภาษา",
498
+ "kioskLanguageAtLeastOne": "เลือกอย่างน้อยหนึ่งภาษา หรือปิดการจำกัด",
499
+ "kioskLanguageHint": "เลือกภาษาที่ลูกค้าเลือกใช้ได้บนตู้คีออส ค่าเริ่มต้นคือแสดงทุกภาษาที่รองรับ",
500
+ "kioskLanguageRestrict": "จำกัดภาษาที่แสดง",
501
+ "kioskLanguageRestrictSublabel": "ปิด: แสดงทุกภาษาที่รองรับ เปิด: แสดงเฉพาะภาษาที่เลือกด้านล่าง",
497
502
  "kioskOnly": "คีออสก์เท่านั้น",
498
503
  "showingDevices": "แสดง {count} จาก {total}",
499
504
  "noDevicesMatch": "ไม่มีอุปกรณ์ตรงกับเกณฑ์การค้นหาของคุณ",
@@ -499,6 +499,11 @@
499
499
  "searchDevices": "按槽位、设备ID或名称搜索...",
500
500
  "allTypes": "全部类型",
501
501
  "tabletOnly": "仅平板",
502
+ "kioskLanguage": "语言",
503
+ "kioskLanguageAtLeastOne": "请至少选择一种语言,或关闭限制。",
504
+ "kioskLanguageHint": "选择顾客可以在自助点餐机上使用的语言。默认显示所有支持的语言。",
505
+ "kioskLanguageRestrict": "限制显示的语言",
506
+ "kioskLanguageRestrictSublabel": "关闭:显示所有支持的语言。开启:只显示下面勾选的语言。",
502
507
  "kioskOnly": "仅自助终端",
503
508
  "showingDevices": "显示 {count} / {total}",
504
509
  "noDevicesMatch": "没有设备符合您的搜索条件",
@@ -88,6 +88,10 @@ export interface MfKioskOrderSetting {
88
88
  // kiosk field would persist `false` and silently kill inheritance.
89
89
  autoCloseBill: boolean | null,
90
90
  printReceiptAfterClose: boolean | null,
91
+ // Locales the kiosk may offer the customer. null or [] = no restriction,
92
+ // i.e. the kiosk shows every language it ships. Anything else is the exact
93
+ // allowed set.
94
+ languages: string[] | null,
91
95
  }
92
96
 
93
97
  export interface MfKioskOrderSettingMenuItemForm {
@@ -107,6 +111,10 @@ export interface MfKioskOrderSettingForm {
107
111
  // kiosk field would persist `false` and silently kill inheritance.
108
112
  autoCloseBill: boolean | null,
109
113
  printReceiptAfterClose: boolean | null,
114
+ // Locales the kiosk may offer the customer. null or [] = no restriction,
115
+ // i.e. the kiosk shows every language it ships. Anything else is the exact
116
+ // allowed set.
117
+ languages: string[] | null,
110
118
  }
111
119
 
112
120
  export type KioskPickAtCounterSettingForm = MfKioskPickAtCounterSetting;
@@ -0,0 +1,29 @@
1
+ import { KIOSK_LOCALES, type KioskLocale } from '@entity'
2
+
3
+ export interface KioskLanguageOption {
4
+ code: KioskLocale
5
+ /** Endonym — the language's own name, so a merchant recognises it on sight. */
6
+ label: string
7
+ }
8
+
9
+ /**
10
+ * Labels for the locales the kiosk app ships (`KIOSK_LOCALES` in the entity
11
+ * package). Written as endonyms and NOT translated: the merchant is choosing
12
+ * which languages their customers will see, so "中文" must read as "中文" even
13
+ * when the portal itself is in English.
14
+ */
15
+ const LABELS: Record<KioskLocale, string> = {
16
+ 'en-US': 'English',
17
+ 'zh-CN': '中文',
18
+ 'th-TH': 'ไทย',
19
+ 'vi-VN': 'Tiếng Việt',
20
+ 'id-ID': 'Bahasa Indonesia',
21
+ 'ko-KR': '한국어',
22
+ 'ja-JP': '日本語',
23
+ 'ar-SA': 'العربية'
24
+ }
25
+
26
+ export const KIOSK_LANGUAGE_OPTIONS: KioskLanguageOption[] = KIOSK_LOCALES.map((code) => ({
27
+ code,
28
+ label: LABELS[code]
29
+ }))
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { OrderSettingMapper, defaultKioskOrderSetting } from './mapper'
3
+ import type { MfKioskOrderSetting } from '@/modules/order-setting/kiosk/interface'
4
+ import type { OrderSettingsDto } from '@entity'
5
+
6
+ function toKioskForm(kioskSettings: unknown) {
7
+ return OrderSettingMapper.convertOrderSettingDto({
8
+ _id: 'r1',
9
+ kioskSettings
10
+ } as OrderSettingsDto).kiosk
11
+ }
12
+
13
+ function saved(kiosk: MfKioskOrderSetting) {
14
+ return OrderSettingMapper.toOrderKioskSettingsDto(kiosk).kioskSettings
15
+ }
16
+
17
+ describe('kiosk languages round-trip', () => {
18
+ it('reads an unset restriction as null', () => {
19
+ expect(toKioskForm({}).languages).toBeNull()
20
+ })
21
+
22
+ it('reads a stored empty list as null — [] already means "all languages"', () => {
23
+ expect(toKioskForm({ languages: [] }).languages).toBeNull()
24
+ })
25
+
26
+ it('reads a stored restriction as-is', () => {
27
+ expect(toKioskForm({ languages: ['en-US', 'zh-CN'] }).languages).toEqual(['en-US', 'zh-CN'])
28
+ })
29
+
30
+ it('omits the field entirely when the merchant restricts nothing', () => {
31
+ expect(saved({ ...defaultKioskOrderSetting, languages: null }).languages).toBeUndefined()
32
+ })
33
+
34
+ it('omits the field rather than saving an empty restriction', () => {
35
+ expect(saved({ ...defaultKioskOrderSetting, languages: [] }).languages).toBeUndefined()
36
+ })
37
+
38
+ it('saves the chosen restriction', () => {
39
+ expect(saved({ ...defaultKioskOrderSetting, languages: ['en-US', 'th-TH'] }).languages).toEqual([
40
+ 'en-US',
41
+ 'th-TH'
42
+ ])
43
+ })
44
+ })
@@ -82,7 +82,8 @@ export const defaultKioskOrderSetting: MfKioskOrderSettingForm = {
82
82
  coverImagePortrait: null,
83
83
  otaChannel: '',
84
84
  autoCloseBill: null,
85
- printReceiptAfterClose: null
85
+ printReceiptAfterClose: null,
86
+ languages: null
86
87
  }
87
88
 
88
89
  export const defaultSubmitOrderInstruction: MfKioskSubmitOrderInstruction = {
@@ -143,7 +144,10 @@ const toKiosk = (
143
144
  // Preserve "unset" as null — collapsing to a boolean here is what let an
144
145
  // unrelated save overwrite the inherited dineInSettings value.
145
146
  autoCloseBill: kioskOrderSettings?.autoCloseBill ?? null,
146
- printReceiptAfterClose: kioskOrderSettings?.printReceiptAfterClose ?? null
147
+ printReceiptAfterClose: kioskOrderSettings?.printReceiptAfterClose ?? null,
148
+ // Stored [] means the same thing as unset ("all languages"), so collapse it
149
+ // to null here — the form only ever has to reason about null vs a real set.
150
+ languages: kioskOrderSettings?.languages?.length ? [...kioskOrderSettings.languages] : null
147
151
  }
148
152
  }
149
153
 
@@ -239,6 +243,9 @@ const toOrderKioskSettingsDto = (
239
243
  // still applies — sending `false` here would pin it off for good.
240
244
  autoCloseBill: kioskSetting.autoCloseBill ?? undefined,
241
245
  printReceiptAfterClose: kioskSetting.printReceiptAfterClose ?? undefined,
246
+ // Omit rather than send [] when the merchant restricts nothing, so the
247
+ // saved document stays free of a field that means "all languages" anyway.
248
+ languages: kioskSetting.languages?.length ? kioskSetting.languages : undefined,
242
249
  paymentSetting: createPaymentSettings(kioskSetting.paymentSetting),
243
250
  takeaway: kioskSetting.takeaway.submitOrderInstruction
244
251
  ? {
@@ -155,6 +155,39 @@
155
155
  />
156
156
  </div>
157
157
 
158
+ <!-- Language Section -->
159
+ <div class="flex flex-col gap-2">
160
+ <span class="fm-typo-en-title-sm-600">{{ t('order.kioskLanguage') }}</span>
161
+ <span class="fm-typo-en-body-sm-400 text-fm-color-typo-secondary">
162
+ {{ t('order.kioskLanguageHint') }}
163
+ </span>
164
+ <div class="border p-[1.5rem] fm-corner-radius-lg flex flex-col gap-4 my-5 w-11/12">
165
+ <FmSwitch
166
+ :model-value="restrictLanguages"
167
+ :label="t('order.kioskLanguageRestrict')"
168
+ :sublabel="t('order.kioskLanguageRestrictSublabel')"
169
+ label-placement="right"
170
+ @update:model-value="onRestrictLanguagesChange"
171
+ />
172
+ <div v-show="restrictLanguages" class="grid grid-cols-1 md:grid-cols-2 gap-3">
173
+ <FmCheckbox
174
+ v-for="option in languageOptions"
175
+ :key="option.code"
176
+ :model-value="selectedLanguages.includes(option.code)"
177
+ :value="option.code"
178
+ :label="option.label"
179
+ @update:model-value="(v: boolean) => toggleLanguage(option.code, v)"
180
+ />
181
+ </div>
182
+ <span
183
+ v-if="restrictLanguages && selectedLanguages.length === 0"
184
+ class="fm-typo-en-body-sm-400 text-fm-color-system-error-300"
185
+ >
186
+ {{ t('order.kioskLanguageAtLeastOne') }}
187
+ </span>
188
+ </div>
189
+ </div>
190
+
158
191
  <!-- Cover Image Section -->
159
192
  <div class="flex flex-col gap-2">
160
193
  <span class="fm-typo-en-title-sm-600">{{ t('order.coverImage') }}</span>
@@ -299,6 +332,10 @@ import type {
299
332
  MfKioskOrderSettingForm,
300
333
  KioskPaymentTypesForm
301
334
  } from '@/modules/order-setting/kiosk/interface'
335
+ import {
336
+ KIOSK_LANGUAGE_OPTIONS,
337
+ type KioskLanguageOption
338
+ } from '@/modules/order-setting/kiosk/languages'
302
339
  import { useLoading } from '@/composables/loading'
303
340
  import { useSnackbar } from '@feedmepos/ui-library'
304
341
  import { useCoreStore, useI18n } from '@feedmepos/mf-common'
@@ -336,6 +373,28 @@ const onAutoCloseBillChange = (value: boolean) => {
336
373
  }
337
374
  }
338
375
 
376
+ const languageOptions: KioskLanguageOption[] = KIOSK_LANGUAGE_OPTIONS
377
+
378
+ // `languages: null` is "no restriction" — the kiosk shows everything it ships.
379
+ // The switch is therefore derived from the saved value, not a separate flag, so
380
+ // a merchant who turns the restriction off leaves nothing behind in the record.
381
+ const restrictLanguages = computed(() => kioskOrderSettingForm.value.languages != null)
382
+
383
+ const selectedLanguages = computed<string[]>(() => kioskOrderSettingForm.value.languages ?? [])
384
+
385
+ function onRestrictLanguagesChange(value: boolean) {
386
+ // Seed with English rather than an empty list: an empty list is indistinguishable
387
+ // from "no restriction" once saved, and the form would open on an error state.
388
+ kioskOrderSettingForm.value.languages = value ? ['en-US'] : null
389
+ }
390
+
391
+ function toggleLanguage(code: string, selected: boolean) {
392
+ const current = kioskOrderSettingForm.value.languages ?? []
393
+ kioskOrderSettingForm.value.languages = selected
394
+ ? languageOptions.filter((o) => o.code === code || current.includes(o.code)).map((o) => o.code)
395
+ : current.filter((c) => c !== code)
396
+ }
397
+
339
398
  // Validation rules
340
399
  const nonNegativeRule = (val: any) => {
341
400
  if (val < 0) return t('order.nonNegativeRule')
@@ -370,6 +429,10 @@ const validKioskOrderSetting = computed<MfKioskOrderSetting | null>(() => {
370
429
  if (!isValid) return null
371
430
  }
372
431
 
432
+ // A restriction that allows no language would leave the kiosk with nothing to
433
+ // render. Saving it would also be a no-op, since [] round-trips as null.
434
+ if (f.languages != null && f.languages.length === 0) return null
435
+
373
436
  // Validate payment settings
374
437
  const payment = f.paymentSetting
375
438
  if (payment?.paymentTypes?.includes(F_ORDER_PAYMENT_TYPE.enum.ePayment)) {
@@ -61,6 +61,10 @@ const props = defineProps({
61
61
  integrationIdReadOnly: {
62
62
  type: Boolean,
63
63
  default: false
64
+ },
65
+ syncMenuLoading: {
66
+ type: Boolean,
67
+ default: false
64
68
  }
65
69
  })
66
70
 
@@ -402,7 +406,8 @@ const formattedMenuSyncError = computed(() => {
402
406
  variant="primary"
403
407
  :label="t('order.syncMenu')"
404
408
  @click="emits('syncMenu')"
405
- :disabled="!canEdit"
409
+ :loading="syncMenuLoading"
410
+ :disabled="!canEdit || syncMenuLoading"
406
411
  />
407
412
 
408
413
  <div
@@ -85,7 +85,7 @@ function updateVatExclude(vatExclude: boolean) {
85
85
  const { t } = useI18n()
86
86
  const { currentRestaurant } = useCoreStore()
87
87
  const { showSuccess, showError } = useSnackbarFunctions()
88
- const { startAsyncCallWithErr } = useLoading()
88
+ const { isLoading, startAsyncCallWithErr } = useLoading()
89
89
 
90
90
  async function syncMenu() {
91
91
  await startAsyncCallWithErr(async () => {
@@ -111,6 +111,7 @@ async function syncMenu() {
111
111
  :company="company"
112
112
  :can-use-auto-delivery-integration="canUseAutoDeliveryIntegration"
113
113
  :show-test-order-actions="false"
114
+ :sync-menu-loading="isLoading"
114
115
  @update:model-value="updateDataPass"
115
116
  @sync-menu="syncMenu"
116
117
  >