@feedmepos/mf-order-setting 0.0.72-test.1 → 0.0.74-dev

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 (186) hide show
  1. package/.env +1 -1
  2. package/README.md +7 -23
  3. package/dist/{KioskDevicesView-CP_cGMXE.js → KioskDevicesView-CW7nDXi2.js} +1 -1
  4. package/dist/KioskDevicesView.vue_vue_type_script_setup_true_lang-CPDkls2i.js +762 -0
  5. package/dist/{KioskSettingView-BiKYEjfe.js → KioskSettingView-DErXeFIt.js} +183 -202
  6. package/dist/KioskView-d1YjttbD.js +362 -0
  7. package/dist/OrderSettingsView-asfw4cYT.js +60012 -0
  8. package/dist/app-DrWO2wkv.js +20750 -0
  9. package/dist/app.js +3 -3
  10. package/dist/{dayjs.min-Bml-FvS7.js → dayjs.min-Fu6ejRAr.js} +1 -1
  11. package/dist/frontend/mf-order/src/api/index.d.ts +3 -1
  12. package/dist/frontend/mf-order/src/api/menu/index.d.ts +1 -1
  13. package/dist/frontend/mf-order/src/api/remoteOrder/index.d.ts +10 -3
  14. package/dist/frontend/mf-order/src/app.d.ts +194 -150
  15. package/dist/frontend/mf-order/src/components/GoogleMap.vue.d.ts +1 -1
  16. package/dist/frontend/mf-order/src/composables/useKioskPermission.d.ts +11 -0
  17. package/dist/frontend/mf-order/src/composables/useOrderSettingPermission.d.ts +13 -0
  18. package/dist/frontend/mf-order/src/composables/useQrOrderingEnabled.d.ts +2 -0
  19. package/dist/frontend/mf-order/src/helpers/menu.d.ts +1 -1
  20. package/dist/frontend/mf-order/src/main.d.ts +194 -150
  21. package/dist/frontend/mf-order/src/stores/kiosk/index.d.ts +21 -21
  22. package/dist/frontend/mf-order/src/stores/menu/menu.d.ts +906 -1044
  23. package/dist/frontend/mf-order/src/stores/order-setting/index.d.ts +39 -6
  24. package/dist/frontend/mf-order/src/stores/restaurant/index.d.ts +5 -2
  25. package/dist/frontend/mf-order/src/views/kiosk/KioskStatePanel.vue.d.ts +19 -0
  26. package/dist/frontend/mf-order/src/views/kiosk/devices/KioskDevicesView.vue.d.ts +7 -1
  27. package/dist/frontend/mf-order/src/views/order-settings/delivery/DeliverySetting.vue.d.ts +2 -2
  28. package/dist/frontend/mf-order/src/views/order-settings/delivery/components/TaxInput.vue.d.ts +3 -8
  29. package/dist/frontend/mf-order/src/views/order-settings/delivery/delivery.d.ts +3 -1
  30. package/dist/frontend/mf-order/src/views/order-settings/delivery/delivery.data.d.ts +5 -1
  31. package/dist/frontend/mf-order/src/views/order-settings/delivery/inhouse/InHouseDelivery.vue.d.ts +21 -0
  32. package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/ExternalSetting.vue.d.ts +2 -2
  33. package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/GrabfoodSetting.vue.d.ts +1 -1
  34. package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/IntegratedDelivery.vue.d.ts +28 -2
  35. package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/LinemanSetting.vue.d.ts +46 -0
  36. package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/TngSetting.vue.d.ts +57 -0
  37. package/dist/frontend/mf-order/src/views/order-settings/pickup/PickUpSettingDialogContent.vue.d.ts +23 -1
  38. package/dist/frontend/mf-order/src/views/order-settings/pickup/Preorder.vue.d.ts +27 -0
  39. package/dist/frontend/mf-order/src/views/order-settings/servicecharge/RateInput.vue.d.ts +8 -0
  40. package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +1 -1
  41. package/dist/{index-BlKeAWPk.js → index-DEKeP24S.js} +7 -7
  42. package/dist/index-DJLCZHNZ.js +665 -0
  43. package/dist/lineman-settings.dto-DS4tgxBJ.js +61259 -0
  44. package/dist/package/entity/_type-assert.d.ts +7 -0
  45. package/dist/package/entity/booking/booking.dto.d.ts +24 -24
  46. package/dist/package/entity/delivery/delivery.dto.d.ts +8 -8
  47. package/dist/package/entity/delivery/delivery.enum.d.ts +12 -0
  48. package/dist/package/entity/delivery/gateway/grab.dto.d.ts +2 -2
  49. package/dist/package/entity/delivery/gateway/kosmo.dto.d.ts +16 -16
  50. package/dist/package/entity/delivery/gateway/pandago.dto.d.ts +10 -10
  51. package/dist/package/entity/incoming-order/incoming-order-to-bill.dto.d.ts +282 -4591
  52. package/dist/package/entity/incoming-order/incoming-order.do.d.ts +32 -24
  53. package/dist/package/entity/incoming-order/incoming-order.dto.d.ts +23 -11719
  54. package/dist/package/entity/incoming-order/incoming-order.enum.d.ts +2 -2
  55. package/dist/package/entity/index.d.ts +12 -0
  56. package/dist/package/entity/kiosk/kiosk.do.d.ts +140 -140
  57. package/dist/package/entity/kiosk/kiosk.dto.d.ts +168 -168
  58. package/dist/package/entity/kiosk/marketing/marketing.dto.d.ts +63 -7
  59. package/dist/package/entity/marketing/marketing.dto.d.ts +4 -4
  60. package/dist/package/entity/member/member.dto.d.ts +6 -0
  61. package/dist/package/entity/order/dine-in/dine-in.do.d.ts +8 -8
  62. package/dist/package/entity/order/effects/effect.dto.d.ts +68 -16
  63. package/dist/package/entity/order/menu/menu.dto.d.ts +45 -8
  64. package/dist/package/entity/order/order-item/order-item.dto.d.ts +358 -3853
  65. package/dist/package/entity/order/order.do.d.ts +239 -42
  66. package/dist/package/entity/order/order.dto.d.ts +6497 -2236
  67. package/dist/package/entity/order/payment/payment.dto.d.ts +52 -12
  68. package/dist/package/entity/order-platform/base-integration.do.d.ts +8 -0
  69. package/dist/package/entity/order-platform/deliveroo/deliveroo-dto.d.ts +2 -2
  70. package/dist/package/entity/order-platform/deliveroo/deliveroo-order.do.d.ts +524 -524
  71. package/dist/package/entity/order-platform/deliveroo/deliveroo-setting.do.d.ts +2 -2
  72. package/dist/package/entity/order-platform/deliveroo/deliveroo.menu.dto.d.ts +58 -58
  73. package/dist/package/entity/order-platform/external/menu/external-master-menu.do.d.ts +212 -284
  74. package/dist/package/entity/order-platform/external/menu/external-menu.do.d.ts +331 -589
  75. package/dist/package/entity/order-platform/external/menu/external-menu.dto.d.ts +1 -1
  76. package/dist/package/entity/order-platform/external/order/external-order.do.d.ts +12 -12
  77. package/dist/package/entity/order-platform/external/order/external-order.dto.d.ts +275 -275
  78. package/dist/package/entity/order-platform/external/setting/external-setting.do.d.ts +3 -3
  79. package/dist/package/entity/order-platform/external/setting/external-setting.dto.d.ts +3 -3
  80. package/dist/package/entity/order-platform/foodpanda/foodpanda-menu.dto.d.ts +284 -284
  81. package/dist/package/entity/order-platform/foodpanda/foodpanda-order.do.d.ts +40 -40
  82. package/dist/package/entity/order-platform/foodpanda/foodpanda-order.dto.d.ts +16 -16
  83. package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.do.d.ts +2 -2
  84. package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.dto.d.ts +2 -2
  85. package/dist/package/entity/order-platform/gofood/gofood-order.do.d.ts +18 -18
  86. package/dist/package/entity/order-platform/gofood/gofood-settings.do.d.ts +2 -2
  87. package/dist/package/entity/order-platform/gofood/gofood-settings.dto.d.ts +2 -2
  88. package/dist/package/entity/order-platform/gofood/gofood.dto.d.ts +32 -32
  89. package/dist/package/entity/order-platform/grabfood/grabfood-edit-order.do.d.ts +12 -12
  90. package/dist/package/entity/order-platform/grabfood/grabfood-omni.do.d.ts +134 -115
  91. package/dist/package/entity/order-platform/grabfood/grabfood-order.do.d.ts +187 -165
  92. package/dist/package/entity/order-platform/grabfood/grabfood-settings.do.d.ts +8 -2
  93. package/dist/package/entity/order-platform/grabfood/grabfood-webhook.dto.d.ts +49 -42
  94. package/dist/package/entity/order-platform/grabfood/grabfood.dto.d.ts +67 -54
  95. package/dist/package/entity/order-platform/grabfood/grabfood.enum.d.ts +1 -1
  96. package/dist/package/entity/order-platform/lineman/lineman-settings.do.d.ts +37 -0
  97. package/dist/package/entity/order-platform/lineman/lineman-settings.dto.d.ts +40 -0
  98. package/dist/package/entity/order-platform/menu.dto.d.ts +716 -13887
  99. package/dist/package/entity/order-platform/order-platform.dto.d.ts +136 -2
  100. package/dist/package/entity/order-platform/order-platform.enum.d.ts +1 -1
  101. package/dist/package/entity/order-platform/shopeefood/shopeefood-order.do.d.ts +176 -176
  102. package/dist/package/entity/order-platform/shopeefood/shopeefood-order.dto.d.ts +62 -62
  103. package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.do.d.ts +8 -2
  104. package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.dto.d.ts +8 -2
  105. package/dist/package/entity/order-platform/tng/tng-menu.do.d.ts +1061 -0
  106. package/dist/package/entity/order-platform/tng/tng-order.do.d.ts +1915 -0
  107. package/dist/package/entity/order-platform/tng/tng-settings.do.d.ts +379 -0
  108. package/dist/package/entity/order-platform/tng/tng-settings.dto.d.ts +301 -0
  109. package/dist/package/entity/order-platform/tng/tng.dto.d.ts +1162 -0
  110. package/dist/package/entity/order-platform/tng/tng.enum.d.ts +29 -0
  111. package/dist/package/entity/order-setting/delivery/inhouse-delivery.do.d.ts +12 -0
  112. package/dist/package/entity/order-setting/delivery/inhouse-delivery.dto.d.ts +12 -0
  113. package/dist/package/entity/order-setting/dine-in/dine-in.do.d.ts +11 -0
  114. package/dist/package/entity/order-setting/dine-in/dine-in.dto.d.ts +9 -0
  115. package/dist/package/entity/order-setting/kiosk/kiosk.do.d.ts +2 -2
  116. package/dist/package/entity/order-setting/kiosk/kiosk.dto.d.ts +2 -2
  117. package/dist/package/entity/order-setting/order-setting.do.d.ts +59 -8
  118. package/dist/package/entity/order-setting/order-setting.dto.d.ts +118 -16
  119. package/dist/package/entity/order-setting/pickup/pickup.do.d.ts +12 -0
  120. package/dist/package/entity/order-setting/pickup/pickup.dto.d.ts +12 -0
  121. package/dist/package/entity/order-setting/sms/sms.do.d.ts +2 -2
  122. package/dist/package/entity/order-setting/sms/sms.dto.d.ts +2 -2
  123. package/dist/package/entity/payment/transaction.d.ts +80 -8
  124. package/dist/package/entity/printer/printer.do.d.ts +14 -14
  125. package/dist/package/entity/pubsub/ably/ably.enum.d.ts +1 -1
  126. package/dist/package/entity/queue/queue.do.d.ts +236 -1901
  127. package/dist/package/entity/queue/queue.dto.d.ts +743 -445
  128. package/dist/package/entity/restaurant/restaurant.dto.d.ts +27 -4
  129. package/dist/package/entity/websocket/websocket.dto.d.ts +10 -10
  130. package/dist/style.css +1 -0
  131. package/golar.config.ts +20 -0
  132. package/package.json +11 -4
  133. package/src/Entry.vue +2 -10
  134. package/src/api/index.ts +5 -2
  135. package/src/api/menu/index.ts +1 -1
  136. package/src/api/remoteOrder/index.ts +176 -110
  137. package/src/composables/useKioskPermission.ts +41 -0
  138. package/src/composables/useOrderSettingPermission.ts +34 -0
  139. package/src/composables/useQrOrderingEnabled.ts +66 -0
  140. package/src/helpers/menu.ts +1 -1
  141. package/src/locales/en-US.json +164 -147
  142. package/src/locales/ja-JP.json +1769 -1760
  143. package/src/locales/th-TH.json +409 -400
  144. package/src/locales/zh-CN.json +417 -408
  145. package/src/main.ts +12 -49
  146. package/src/router/index.ts +2 -2
  147. package/src/router/routes.ts +34 -5
  148. package/src/stores/menu/menu.ts +48 -27
  149. package/src/stores/restaurant/index.ts +24 -9
  150. package/src/utils/firebase-storage.ts +3 -13
  151. package/src/views/kiosk/KioskStatePanel.vue +31 -0
  152. package/src/views/kiosk/KioskView.vue +100 -62
  153. package/src/views/kiosk/devices/KioskDeviceCard.vue +4 -0
  154. package/src/views/kiosk/devices/KioskDevicesView.vue +42 -9
  155. package/src/views/kiosk/settings/KioskSettingView.vue +3 -2
  156. package/src/views/order-settings/OrderSettingsView.vue +39 -16
  157. package/src/views/order-settings/delivery/DeliverySetting.vue +223 -11
  158. package/src/views/order-settings/delivery/components/TaxInput.vue +1 -1
  159. package/src/views/order-settings/delivery/delivery.data.ts +31 -10
  160. package/src/views/order-settings/delivery/delivery.ts +60 -28
  161. package/src/views/order-settings/delivery/inhouse/InHouseDelivery.vue +40 -1
  162. package/src/views/order-settings/delivery/integrated-delivery/DeliverooSetting.vue +1 -1
  163. package/src/views/order-settings/delivery/integrated-delivery/GrabfoodSetting.vue +15 -7
  164. package/src/views/order-settings/delivery/integrated-delivery/IntegratedDelivery.vue +83 -27
  165. package/src/views/order-settings/delivery/integrated-delivery/LinemanSetting.vue +79 -0
  166. package/src/views/order-settings/delivery/integrated-delivery/ShopeefoodSetting.vue +2 -0
  167. package/src/views/order-settings/delivery/integrated-delivery/TngSetting.vue +749 -0
  168. package/src/views/order-settings/dine-in/DineInSetting.vue +54 -23
  169. package/src/views/order-settings/dine-in/PaymentType.vue +9 -12
  170. package/src/views/order-settings/general/GeneralSetting.vue +15 -6
  171. package/src/views/order-settings/pickup/PickUpSettingDialog.vue +31 -3
  172. package/src/views/order-settings/pickup/PickUpSettingDialogContent.vue +34 -0
  173. package/src/views/order-settings/pickup/Preorder.vue +13 -12
  174. package/src/views/order-settings/queue/QueueSetting.vue +30 -1
  175. package/src/views/order-settings/servicecharge/RateInput.vue +9 -7
  176. package/vite.config.ts +21 -18
  177. package/dist/KioskDevicesView.vue_vue_type_script_setup_true_lang-CiNLqFw6.js +0 -707
  178. package/dist/KioskView-BYxPrI7N.js +0 -343
  179. package/dist/OrderSettingsView-C7Y-_y5U.js +0 -32222
  180. package/dist/app-BN02cB5o.js +0 -18668
  181. package/dist/frontend/mf-order/src/api/auth/index.d.ts +0 -7
  182. package/dist/frontend/mf-order/src/stores/iframe/index.d.ts +0 -14
  183. package/dist/index-DZA8A0m4.js +0 -630
  184. package/dist/queue-transfer.dto-C7nRZOZH.js +0 -45618
  185. package/src/api/auth/index.ts +0 -20
  186. package/src/stores/iframe/index.ts +0 -23
@@ -1,58 +1,63 @@
1
- <template>
2
- <div class="py-[1.5rem]">
3
- <FmPageHead :title="t('order.kiosk')" />
4
- <div class="px-[1.5rem]">
5
- <FmTabs v-model:model-value="selectedMenuItem" :items="menuItems"></FmTabs>
6
- <div class="pt-[1.5rem]">
7
- <div class="flex flex-col gap-10">
8
- <div class="flex flex-col gap-2">
9
- <span class="fm-typo-en-title-sm-600"> {{ t('order.activateLocation') }} </span>
10
- <span class="fm-typo-en-title-sm-400"> {{ t('order.selectRestaurant') }}: </span>
11
- <FmSelect
12
- class="w-1/2"
13
- v-model="currentSelect"
14
- :items="restaurantItems"
15
- :placeholder="t('order.selectItem')"
16
- @update:model-value="selectRestaurant"
17
- />
18
- </div>
19
- <KioskDevicesView
20
- v-if="selectedMenuItem == 'device'"
21
- :devices="devices"
22
- @request-otp="requestOtp"
23
- />
24
- <KioskSettingView
25
- :key="`KioskSettingView-${currentRestaurantId}`"
26
- v-if="selectedMenuItem == 'setting'"
27
- :restaurant-id="currentRestaurantId"
28
- @update-kiosk-order-setting="updateKioskOrderSetting"
29
- :form="OrderSettingStore.state.kiosk ?? defaultKioskOrderSetting"
30
- />
31
- </div>
32
- </div>
33
- </div>
34
- </div>
35
- </template>
36
-
37
- <script setup lang="ts">
38
- import KioskDevicesView from '@/views/kiosk/devices/KioskDevicesView.vue'
39
- import {
40
- useDialog,
41
- useSnackbar,
42
- type FmTabProps,
43
- type ISelectItem
44
- } from '@feedmepos/ui-library'
45
- import { ref, computed, defineAsyncComponent, watch, onMounted } from 'vue'
1
+ <template>
2
+ <div class="py-[1.5rem]">
3
+ <FmPageHead :title="t('order.kiosk')" />
4
+ <div class="px-[1.5rem]">
5
+ <FmTabs v-model:model-value="selectedMenuItem" :items="menuItems"></FmTabs>
6
+ <div class="pt-[1.5rem]">
7
+ <div class="flex flex-col gap-10">
8
+ <div class="flex flex-col gap-2">
9
+ <span class="fm-typo-en-title-sm-600"> {{ t('order.activateLocation') }} </span>
10
+ <span class="fm-typo-en-title-sm-400"> {{ t('order.selectRestaurant') }}: </span>
11
+ <FmSelect
12
+ class="w-1/2"
13
+ v-model="currentSelect"
14
+ :items="restaurantItems"
15
+ :placeholder="t('order.selectItem')"
16
+ @update:model-value="selectRestaurant"
17
+ />
18
+ </div>
19
+ <KioskDevicesView
20
+ v-if="selectedMenuItem == 'device'"
21
+ :devices="devices"
22
+ :load-error="devicesLoadError"
23
+ @request-otp="requestOtp"
24
+ @retry="loadDevices"
25
+ />
26
+ <KioskSettingView
27
+ :key="`KioskSettingView-${currentRestaurantId}`"
28
+ v-if="selectedMenuItem == 'setting' && canManageOrderSetting"
29
+ :restaurant-id="currentRestaurantId"
30
+ @update-kiosk-order-setting="updateKioskOrderSetting"
31
+ :form="OrderSettingStore.state.kiosk ?? defaultKioskOrderSetting"
32
+ />
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import KioskDevicesView from '@/views/kiosk/devices/KioskDevicesView.vue'
41
+ import {
42
+ useDialog,
43
+ useSnackbar,
44
+ type FmTabProps,
45
+ type ISelectItem
46
+ } from '@feedmepos/ui-library'
47
+ import { ref, computed, defineAsyncComponent, watch, onMounted } from 'vue'
46
48
  import { useKioskStore } from '@/stores/kiosk'
47
49
  import type { MfKioskDevice } from '@/modules/kiosk/interface'
48
50
  import KioskOtpDialog from '@/views/kiosk/devices/KioskOtpDialog.vue'
51
+ import { SvcCompanyApiError } from '@/api'
49
52
  import { useCoreStore, useI18n } from '@feedmepos/mf-common'
50
53
  import { useOrderSettings } from '@/stores/order-setting'
51
54
  import type { MfKioskOrderSetting } from '@/modules/order-setting/kiosk/interface'
52
55
  import { defaultKioskOrderSetting } from '@/stores/order-setting/mapper'
56
+ import { useOrderSettingPermission } from '@/composables/useOrderSettingPermission'
53
57
  import { capitalCase } from 'change-case'
54
58
 
55
59
  const { t } = useI18n()
60
+ const { canManageOrderSetting } = useOrderSettingPermission()
56
61
 
57
62
  type KioskMenuItemValue = 'device' | 'setting'
58
63
 
@@ -63,12 +68,15 @@ const KioskSettingView = defineAsyncComponent(
63
68
  const selectedMenuItem = ref<KioskMenuItemValue>('device')
64
69
  const KioskStore = useKioskStore()
65
70
  const OrderSettingStore = useOrderSettings()
66
- const Dialog = useDialog()
67
- const { currentRestaurant, restaurants, changeRestaurant } = useCoreStore()
68
- const SnackBar = useSnackbar()
71
+ const Dialog = useDialog()
72
+ const { currentRestaurant, restaurants, changeRestaurant } = useCoreStore()
73
+ const SnackBar = useSnackbar()
69
74
 
70
75
  const currentSelect = ref<string | undefined>(currentRestaurant.value?._id ?? undefined)
71
76
 
77
+ /** Drives the denied/error panel inside the device tab. 'forbidden' === HTTP 403. */
78
+ const devicesLoadError = ref<'forbidden' | 'error' | null>(null)
79
+
72
80
  const restaurantItems = computed<ISelectItem[]>(() => {
73
81
  return restaurants.value.map((r) => ({
74
82
  label: r.profile.name,
@@ -78,7 +86,7 @@ const restaurantItems = computed<ISelectItem[]>(() => {
78
86
 
79
87
  const currentRestaurantId = ref<string>(currentRestaurant.value?._id ?? '')
80
88
 
81
- async function selectRestaurant(v: string) {
89
+ async function selectRestaurant(v: string) {
82
90
  const restaurant = restaurants.value.find((r) => r._id == v)
83
91
  await changeRestaurant(restaurant)
84
92
  await loadRestaurantData()
@@ -87,27 +95,57 @@ async function selectRestaurant(v: string) {
87
95
  }
88
96
 
89
97
  async function loadRestaurantData() {
90
- await OrderSettingStore.getSetting()
91
- await KioskStore.getDevices()
98
+ // Order settings are only needed for the (permission-gated) Settings tab. Skip the
99
+ // fetch when the user can't manage them, and never let its failure block the Device
100
+ // tab — the order-settings endpoint is now HRM-guarded and 403s without the subject.
101
+ if (canManageOrderSetting.value) {
102
+ try {
103
+ await OrderSettingStore.getSetting()
104
+ } catch {
105
+ // Settings tab surfaces its own state; don't block device loading.
106
+ }
107
+ }
108
+ await loadDevices()
92
109
  }
93
110
 
94
- const menuItems = computed<FmTabProps[]>(() => [
95
- {
96
- label: t('order.deviceBinding'),
97
- value: 'device'
98
- },
99
- {
100
- label: t('order.settings'),
101
- value: 'setting'
111
+ // Loads the device list, mapping a 403 to the in-tab "access denied" panel and any
112
+ // other failure to a retryable error panel (instead of silently showing "no devices").
113
+ async function loadDevices() {
114
+ devicesLoadError.value = null
115
+ try {
116
+ await KioskStore.getDevices()
117
+ } catch (e) {
118
+ devicesLoadError.value =
119
+ e instanceof SvcCompanyApiError && e.status === 403 ? 'forbidden' : 'error'
102
120
  }
103
- ])
121
+ }
122
+
123
+ const menuItems = computed<FmTabProps[]>(() => {
124
+ const items: FmTabProps[] = [{ label: t('order.deviceBinding'), value: 'device' }]
125
+ // The Settings tab is order settings — only show it to users who can manage them.
126
+ if (canManageOrderSetting.value) {
127
+ items.push({ label: t('order.settings'), value: 'setting' })
128
+ }
129
+ return items
130
+ })
104
131
 
105
132
  const devices = computed<MfKioskDevice[]>(() => {
106
133
  return KioskStore.state.devices
107
134
  })
108
135
 
109
136
  async function requestOtp() {
110
- await KioskStore.requestOtp()
137
+ try {
138
+ await KioskStore.requestOtp()
139
+ } catch (e) {
140
+ SnackBar.open({
141
+ title:
142
+ e instanceof SvcCompanyApiError && e.status === 403
143
+ ? t('order.bindDevicePermissionDenied')
144
+ : t('order.bindDeviceFailed'),
145
+ type: 'error'
146
+ })
147
+ return
148
+ }
111
149
  Dialog.open({
112
150
  title: capitalCase(t('order.bindDevice')),
113
151
  contentComponent: KioskOtpDialog,
@@ -116,7 +154,7 @@ async function requestOtp() {
116
154
  }
117
155
  }).onClose(async () => {
118
156
  // Refresh devices after dialog closes (in case a device was bound)
119
- await KioskStore.getDevices()
157
+ await loadDevices()
120
158
  })
121
159
  }
122
160
 
@@ -80,6 +80,7 @@
80
80
  </div>
81
81
 
82
82
  <FmButton
83
+ v-if="canManageDevices"
83
84
  class="w-full"
84
85
  variant="primary"
85
86
  :label="device?.pinInfo ? t('order.resetDevicePin') : t('order.setDevicePin')"
@@ -139,6 +140,7 @@
139
140
  </div>
140
141
  </FmSideSheet>
141
142
  <FmButton
143
+ v-if="canBindDevice"
142
144
  variant="plain"
143
145
  icon="link_off"
144
146
  class="text-fm-color-system-error-300"
@@ -213,9 +215,11 @@ import dayjs from 'dayjs'
213
215
  import relativeTime from 'dayjs/plugin/relativeTime'
214
216
  import { computed, ref, watch } from 'vue'
215
217
  import KioskUnbindConfirm from './KioskUnbindConfirm.vue'
218
+ import { useKioskPermission } from '@/composables/useKioskPermission'
216
219
  dayjs.extend(relativeTime)
217
220
 
218
221
  const { t } = useI18n()
222
+ const { canManageDevices, canBindDevice } = useKioskPermission()
219
223
 
220
224
  const Dialog = useDialog()
221
225
  const Snackbar = useSnackbar()
@@ -7,6 +7,7 @@
7
7
  <span class="fm-typo-en-body-md-400 block"> {{ t('order.kioskDisplayDescription') }} </span>
8
8
  </div>
9
9
  <FmButton
10
+ v-if="canBindDevice"
10
11
  variant="primary"
11
12
  :label="t('order.bindDevice')"
12
13
  size="md"
@@ -15,6 +16,31 @@
15
16
  />
16
17
  </div>
17
18
 
19
+ <!-- Permission denied (e.g. the user reached this tab via the bind-only -->
20
+ <!-- coverSubject, but GET /devices requires the device-management subject). -->
21
+ <KioskStatePanel
22
+ v-if="loadError === 'forbidden'"
23
+ icon="lock"
24
+ :title="t('order.deviceAccessDeniedTitle')"
25
+ :message="t('order.deviceAccessDeniedMessage')"
26
+ />
27
+
28
+ <!-- Any other load failure -->
29
+ <KioskStatePanel
30
+ v-else-if="loadError === 'error'"
31
+ icon="error"
32
+ :title="t('order.deviceLoadErrorTitle')"
33
+ :message="t('order.deviceLoadErrorMessage')"
34
+ >
35
+ <FmButton
36
+ variant="secondary"
37
+ :label="t('order.retry')"
38
+ prepend-icon="refresh"
39
+ @click="$emit('retry')"
40
+ />
41
+ </KioskStatePanel>
42
+
43
+ <template v-else>
18
44
  <!-- Search and Filter Bar -->
19
45
  <div class="flex items-center gap-3 flex-wrap">
20
46
  <FmSearch
@@ -72,6 +98,7 @@
72
98
  {{ t('order.noDevicesBound') }}
73
99
  </div>
74
100
  </div>
101
+ </template>
75
102
  </div>
76
103
  </div>
77
104
  </template>
@@ -80,16 +107,22 @@ import type { MfKioskDevice } from '@/modules/kiosk/interface'
80
107
  import { useI18n } from '@feedmepos/mf-common'
81
108
  import { computed, ref } from 'vue'
82
109
  import KioskDeviceCard from './KioskDeviceCard.vue'
110
+ import KioskStatePanel from '@/views/kiosk/KioskStatePanel.vue'
111
+ import { useKioskPermission } from '@/composables/useKioskPermission'
83
112
 
84
113
  const { t } = useI18n()
114
+ const { canBindDevice } = useKioskPermission()
85
115
 
86
116
  interface Props {
87
117
  devices: MfKioskDevice[]
118
+ /** Why the device list failed to load, if at all. 'forbidden' === HTTP 403. */
119
+ loadError?: 'forbidden' | 'error' | null
88
120
  }
89
121
 
90
- const props = defineProps<Props>()
122
+ const props = withDefaults(defineProps<Props>(), { loadError: null })
91
123
  defineEmits<{
92
124
  (e: 'requestOtp'): void
125
+ (e: 'retry'): void
93
126
  }>()
94
127
 
95
128
  const searchQuery = ref('')
@@ -119,14 +152,14 @@ const filteredDevices = computed(() => {
119
152
  // Apply search filter
120
153
  const query = searchQuery.value.toLowerCase().trim()
121
154
  if (query) {
122
- result = result.filter((d) => {
123
- const name = d.name?.toLowerCase() || ''
124
- const machineId = d.machineId?.toLowerCase() || ''
125
- const slot = d.slotInfo?.toLowerCase() || ''
126
- const remark = d.remarkInfo?.toLowerCase() || ''
127
- return name.includes(query) || machineId.includes(query) || slot.includes(query) || remark.includes(query)
128
- })
129
- }
155
+ result = result.filter((d) => {
156
+ const name = d.name?.toLowerCase() || ''
157
+ const machineId = d.machineId?.toLowerCase() || ''
158
+ const slot = d.slotInfo?.toLowerCase() || ''
159
+ const remark = d.remarkInfo?.toLowerCase() || ''
160
+ return name.includes(query) || machineId.includes(query) || slot.includes(query) || remark.includes(query)
161
+ })
162
+ }
130
163
 
131
164
  // Sort tablets by slot, kiosks by name
132
165
  return result.sort((a, b) => {
@@ -337,13 +337,14 @@ const validKioskOrderSetting = computed<MfKioskOrderSetting | null>(() => {
337
337
  if (!ePayment) return null
338
338
 
339
339
  const hasCard = ePayment.card?.terminal || ePayment.card?.nfc
340
- const hasWallet =
340
+ const hasWallet =
341
341
  ePayment.eWallet?.qrPay ||
342
342
  ePayment.eWallet?.scanPay ||
343
343
  ePayment.eWallet?.terminalScanPay ||
344
344
  ePayment.eWallet?.terminalQrPay
345
+ const hasTerminal = ePayment.terminal
345
346
 
346
- if (!hasCard && !hasWallet) return null
347
+ if (!hasCard && !hasWallet && !hasTerminal) return null
347
348
  }
348
349
 
349
350
  // Return the form as-is if valid, with display stand disabled when dineIn is disabled
@@ -30,11 +30,13 @@ import QueueSetting from './queue/QueueSetting.vue'
30
30
  import { useI18n, useCoreStore } from '@feedmepos/mf-common'
31
31
  import { useMenuStore } from '@/stores/menu/menu'
32
32
  import { useSnackbarFunctions } from '@/components/snackbar'
33
+ import { useQrOrderingEnabled } from '@/composables/useQrOrderingEnabled'
33
34
 
34
35
  const { t } = useI18n()
35
36
  const { currentRestaurant } = useCoreStore()
36
37
  const menuStore = useMenuStore()
37
38
  const { showError } = useSnackbarFunctions()
39
+ const qrEnabled = useQrOrderingEnabled()
38
40
  type OrderSettingMenuItemValue =
39
41
  | 'delivery'
40
42
  | 'pickup'
@@ -47,17 +49,33 @@ type OrderSettingMenuItemValue =
47
49
  | 'queue'
48
50
  const selectedOrderSetting = ref<OrderSettingMenuItemValue>('delivery')
49
51
 
50
- const settingItem = computed<FmTabProps[]>(() => [
51
- { label: t('order.delivery'), value: 'delivery' },
52
- { label: t('order.pickup'), value: 'pickup' },
53
- { label: t('order.dineIn'), value: 'dinein' },
54
- { label: t('order.driveThru'), value: 'driveThru' },
55
- // { label: t('order.serviceCharge'), value: 'servicecharge' },
56
- // { label: t('order.discountRule.title'), value: 'discountRule' },
57
- { label: t('order.sms'), value: 'sms' },
58
- { label: t('order.general'), value: 'general' },
59
- { label: t('order.queue'), value: 'queue' }
60
- ])
52
+ const settingItem = computed<FmTabProps[]>(() => {
53
+ // QR ordering off -> expose only the Delivery tab (Local delivery company).
54
+ if (!qrEnabled.value) {
55
+ return [{ label: t('order.delivery'), value: 'delivery' }]
56
+ }
57
+ return [
58
+ { label: t('order.delivery'), value: 'delivery' },
59
+ { label: t('order.pickup'), value: 'pickup' },
60
+ { label: t('order.dineIn'), value: 'dinein' },
61
+ { label: t('order.driveThru'), value: 'driveThru' },
62
+ // { label: t('order.serviceCharge'), value: 'servicecharge' },
63
+ // { label: t('order.discountRule.title'), value: 'discountRule' },
64
+ { label: t('order.sms'), value: 'sms' },
65
+ { label: t('order.general'), value: 'general' },
66
+ { label: t('order.queue'), value: 'queue' }
67
+ ]
68
+ })
69
+
70
+ // When QR ordering is off, keep selection on the only visible tab so a
71
+ // previously-selected (now hidden) tab can't stay active.
72
+ watch(
73
+ qrEnabled,
74
+ (enabled) => {
75
+ if (!enabled) selectedOrderSetting.value = 'delivery'
76
+ },
77
+ { immediate: true }
78
+ )
61
79
 
62
80
  const currentComponent = computed(() => {
63
81
  switch (selectedOrderSetting.value) {
@@ -79,6 +97,8 @@ const currentComponent = computed(() => {
79
97
  return GeneralSetting
80
98
  case 'queue':
81
99
  return QueueSetting
100
+ default:
101
+ return DeliverySetting
82
102
  }
83
103
  })
84
104
 
@@ -88,11 +108,14 @@ onMounted(async () => {
88
108
  }
89
109
  })
90
110
 
91
- watch(currentRestaurant, async (newRestaurant: any) => {
92
- if (newRestaurant?._id) {
93
- await menuStore.loadCatalogOptions(newRestaurant._id)
94
- }
95
- })
111
+ watch(
112
+ () => currentRestaurant.value,
113
+ async (newRestaurant) => {
114
+ if (newRestaurant?._id) {
115
+ await menuStore.loadCatalogOptions(newRestaurant._id)
116
+ }
117
+ },
118
+ )
96
119
 
97
120
  watch(
98
121
  () => menuStore.catalogOptionsError,