@feedmepos/mf-order-setting 0.0.48 → 0.0.50
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.
- package/dist/{KioskDevicesView-iZTQ--DO.js → KioskDevicesView-u14hzPbE.js} +1 -1
- package/dist/KioskDevicesView.vue_vue_type_script_setup_true_lang-DBgRDIoS.js +501 -0
- package/dist/{KioskSettingView-mN7UPNYu.js → KioskSettingView-DmvtZcV1.js} +34 -33
- package/dist/KioskView-M8V91gD5.js +474 -0
- package/dist/{OrderSettingsView-B5Nq_u9Z.js → OrderSettingsView-Bl3LshG3.js} +15016 -14688
- package/dist/{app-B3KtJhiw.js → app-CLewMjcd.js} +89 -25
- package/dist/app.js +1 -1
- package/dist/{dayjs.min-GjkM9dM2.js → dayjs.min-DCTYRWyD.js} +1 -1
- package/dist/frontend/mf-order/src/api/restaurant-setting/index.d.ts +2 -1
- package/dist/frontend/mf-order/src/app.d.ts +64 -0
- package/dist/frontend/mf-order/src/main.d.ts +64 -0
- package/dist/frontend/mf-order/src/modules/kiosk/interface.d.ts +1 -0
- package/dist/frontend/mf-order/src/stores/kiosk/index.d.ts +3 -0
- package/dist/frontend/mf-order/src/stores/menu/menu.d.ts +1 -0
- package/dist/frontend/mf-order/src/stores/restaurant/index.d.ts +5 -2
- package/dist/frontend/mf-order/src/views/kiosk/devices/KioskDevicesView.vue.d.ts +6 -3
- package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/ExternalSetting.vue.d.ts +8 -0
- package/dist/frontend/mf-order/src/views/order-settings/drive-thru/DriveThruSetting.vue.d.ts +2 -0
- package/dist/frontend/mf-order/src/views/order-settings/servicecharge/ServiceChargeRule.vue.d.ts +2 -2
- package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +1 -1
- package/dist/{index-Dwzta1U-.js → index-B7LtJeBJ.js} +2 -2
- package/dist/{menu.dto-DgPhiIVk.js → menu.dto-Co7iXHNr.js} +25558 -25019
- package/dist/package/entity/incoming-order/incoming-order-to-bill.dto.d.ts +108 -16
- package/dist/package/entity/incoming-order/incoming-order.do.d.ts +63 -18
- package/dist/package/entity/incoming-order/incoming-order.dto.d.ts +118 -18
- package/dist/package/entity/kiosk/kiosk.do.d.ts +32 -0
- package/dist/package/entity/kiosk/kiosk.dto.d.ts +44 -0
- package/dist/package/entity/kiosk/marketing/marketing.dto.d.ts +1071 -0
- package/dist/package/entity/order/order-item/order-item.dto.d.ts +8 -0
- package/dist/package/entity/order/order.dto.d.ts +16 -8
- package/dist/package/entity/order/order.enum.d.ts +2 -0
- package/dist/package/entity/order-platform/deliveroo/deliveroo-dto.d.ts +2 -2
- package/dist/package/entity/order-platform/deliveroo/deliveroo-setting.do.d.ts +2 -2
- package/dist/package/entity/order-platform/external/order/external-order.do.d.ts +62 -0
- package/dist/package/entity/order-platform/external/order/external-order.dto.d.ts +178 -0
- package/dist/package/entity/order-platform/external/setting/external-setting.do.d.ts +6 -0
- package/dist/package/entity/order-platform/external/setting/external-setting.dto.d.ts +6 -0
- package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.do.d.ts +4 -4
- package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.dto.d.ts +5 -5
- package/dist/package/entity/order-platform/grabfood/grabfood-menu.do.d.ts +15 -0
- package/dist/package/entity/order-platform/grabfood/grabfood-omni.do.d.ts +1834 -15
- package/dist/package/entity/order-platform/grabfood/grabfood-order.do.d.ts +13 -13
- package/dist/package/entity/order-platform/grabfood/grabfood-settings.do.d.ts +5 -2
- package/dist/package/entity/order-platform/grabfood/grabfood-webhook.dto.d.ts +528 -805
- package/dist/package/entity/order-platform/grabfood/grabfood.dto.d.ts +14 -11
- package/dist/package/entity/order-platform/grabfood/grabfood.enum.d.ts +1 -1
- package/dist/package/entity/order-platform/order-platform.dto.d.ts +0 -20
- package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.do.d.ts +2 -2
- package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.dto.d.ts +2 -2
- package/dist/package/entity/queue/queue.do.d.ts +12 -0
- package/dist/package/entity/queue/queue.dto.d.ts +26 -6
- package/package.json +4 -4
- package/src/api/restaurant-setting/index.ts +6 -0
- package/src/locales/en-US.json +16 -0
- package/src/locales/ja-JP.json +16 -0
- package/src/locales/th-TH.json +16 -0
- package/src/locales/zh-CN.json +16 -0
- package/src/main.ts +2 -2
- package/src/modules/kiosk/interface.ts +1 -0
- package/src/stores/kiosk/mapper.ts +1 -0
- package/src/stores/restaurant/index.ts +6 -0
- package/src/views/kiosk/KioskSummary.vue +37 -31
- package/src/views/kiosk/KioskView.vue +4 -1
- package/src/views/kiosk/devices/KioskDeviceCard.vue +205 -89
- package/src/views/kiosk/devices/KioskDevicesView.vue +133 -17
- package/src/views/kiosk/settings/KioskPaymentTypeSection.vue +8 -3
- package/src/views/order-settings/OrderSettingsView.vue +6 -1
- package/src/views/order-settings/delivery/integrated-delivery/ExternalSetting.vue +22 -1
- package/src/views/order-settings/drive-thru/DriveThruSetting.vue +101 -0
- package/dist/KioskDevicesView.vue_vue_type_script_setup_true_lang-CPDemTBj.js +0 -306
- package/dist/KioskView-NEpc7Ivb.js +0 -452
|
@@ -2,44 +2,50 @@
|
|
|
2
2
|
<div :class="dialogType ? '' : ['p-[1.5rem] border fm-corner-radius-lg flex flex-col gap-5 fixed ml-40 w-[320px]']">
|
|
3
3
|
<span v-if="!dialogType" class="fm-typo-en-title-sm-600">{{ t('order.kioskSummary') }}</span>
|
|
4
4
|
<hr v-if="!dialogType" class="my-[0.25rem]" />
|
|
5
|
-
<div class="flex flex-col gap-
|
|
6
|
-
|
|
5
|
+
<div class="flex flex-col gap-4">
|
|
6
|
+
<!-- Device Summary -->
|
|
7
|
+
<div class="flex flex-col gap-2">
|
|
7
8
|
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">{{ t('order.kioskDisplay') }}</span>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
<div v-if="devices.length > 0" class="flex flex-col gap-1">
|
|
10
|
+
<div
|
|
11
|
+
v-for="device in devices"
|
|
12
|
+
:key="device.machineId"
|
|
13
|
+
class="fm-typo-en-body-sm-400 flex items-center gap-2"
|
|
14
|
+
>
|
|
15
|
+
<span class="px-1.5 py-0.5 fm-corner-radius-sm bg-fm-color-neutral-gray-100 text-fm-color-typo-secondary fm-typo-en-body-xs-600 uppercase">
|
|
16
|
+
{{ device.deviceAppType === 'TABLET_APP' ? 'T' : 'K' }}
|
|
17
|
+
</span>
|
|
18
|
+
<span v-if="device.slotInfo" class="fm-typo-en-body-sm-600">{{ device.slotInfo }}</span>
|
|
19
|
+
<span class="text-fm-color-typo-secondary truncate flex-1" :title="device.name">{{ device.name }}</span>
|
|
12
20
|
</div>
|
|
13
|
-
<div v-else>-</div>
|
|
14
21
|
</div>
|
|
22
|
+
<div v-else class="fm-typo-en-body-lg-600">-</div>
|
|
23
|
+
</div>
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
</div>
|
|
25
|
+
<!-- Dine In Status -->
|
|
26
|
+
<div v-if="dineInStatus === t('order.activated')" class="flex flex-col gap-1">
|
|
27
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">{{ t('order.dineIn') }}</span>
|
|
28
|
+
<div class="fm-typo-en-body-lg-600">{{ dineInStatus }}</div>
|
|
29
|
+
</div>
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</div>
|
|
31
|
+
<!-- Dine In Type -->
|
|
32
|
+
<div v-if="dineInStatus === t('order.activated') && dineInType !== t('order.unknown')" class="flex flex-col gap-1">
|
|
33
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">{{ t('order.dineInType') }}</span>
|
|
34
|
+
<div class="fm-typo-en-body-lg-600">{{ dineInType }}</div>
|
|
35
|
+
</div>
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
</div>
|
|
37
|
+
<!-- Takeaway Status -->
|
|
38
|
+
<div v-if="takeAwayStatus === t('order.activated')" class="flex flex-col gap-1">
|
|
39
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">{{ t('order.takeaway') }}</span>
|
|
40
|
+
<div class="fm-typo-en-body-lg-600">{{ takeAwayStatus }}</div>
|
|
41
|
+
</div>
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
</div>
|
|
43
|
+
<!-- Payment Status -->
|
|
44
|
+
<div v-if="acceptedPaymentStatus || offlinePaymentStatus" class="flex flex-col gap-1">
|
|
45
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">{{ t('order.acceptedPayment') }}</span>
|
|
46
|
+
<div class="fm-typo-en-body-lg-600">
|
|
47
|
+
<div v-if="offlinePaymentStatus">{{ t('order.offlinePayment') }}</div>
|
|
48
|
+
<div>{{ acceptedPaymentStatus }}</div>
|
|
43
49
|
</div>
|
|
44
50
|
</div>
|
|
45
51
|
</div>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<KioskDevicesView
|
|
30
30
|
v-if="selectedMenuItem == 'device'"
|
|
31
31
|
:devices="devices"
|
|
32
|
-
|
|
32
|
+
@request-otp="requestOtp"
|
|
33
33
|
/>
|
|
34
34
|
<KioskSettingView
|
|
35
35
|
:key="`KioskSettingView-${currentRestaurantId}`"
|
|
@@ -151,6 +151,9 @@ async function requestOtp() {
|
|
|
151
151
|
contentComponentProps: {
|
|
152
152
|
otp: KioskStore.state.otp
|
|
153
153
|
}
|
|
154
|
+
}).onClose(async () => {
|
|
155
|
+
// Refresh devices after dialog closes (in case a device was bound)
|
|
156
|
+
await KioskStore.getDevices()
|
|
154
157
|
})
|
|
155
158
|
}
|
|
156
159
|
|
|
@@ -1,104 +1,185 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="border fm-corner-radius-lg
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@on:clickedAway="closeUpdateDevicePin"
|
|
9
|
-
>
|
|
10
|
-
<template #side-sheet-button>
|
|
11
|
-
<FmListItem
|
|
12
|
-
class="flex-1"
|
|
13
|
-
:label="device.name"
|
|
14
|
-
:sublabel="`${t('order.activatedDate')}: ${dayjs(device.activatedAt).format('DD MMM YYYY HH:mm')}`"
|
|
2
|
+
<div class="border fm-corner-radius-lg p-12 hover:bg-fm-color-neutral-gray-50 transition-colors">
|
|
3
|
+
<!-- Header Row: Type Badge + Slot + Actions -->
|
|
4
|
+
<div class="flex items-center justify-between mb-2">
|
|
5
|
+
<div class="flex items-center gap-2">
|
|
6
|
+
<span
|
|
7
|
+
class="px-2 py-1 fm-corner-radius-md fm-typo-en-body-sm-600 uppercase bg-fm-color-neutral-gray-200 text-fm-color-typo-secondary"
|
|
15
8
|
>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<span class="fm-typo-en-title-sm-600 mb-3">App Info</span>
|
|
22
|
-
<div class="flex flex-col gap-1 mb-2">
|
|
23
|
-
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">Type</span>
|
|
24
|
-
<span class="fm-typo-en-body-lg-600 block">{{ appType }}</span>
|
|
25
|
-
</div>
|
|
26
|
-
<div class="flex flex-col gap-1 mb-2">
|
|
27
|
-
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">Version</span>
|
|
28
|
-
<span class="fm-typo-en-body-lg-600 block">{{
|
|
29
|
-
device.deviceAppVersion ?? 'Below 0.0.42'
|
|
30
|
-
}}</span>
|
|
31
|
-
</div>
|
|
32
|
-
<div class="flex flex-col gap-1 mb-2">
|
|
33
|
-
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">OTA Version</span>
|
|
34
|
-
<span class="fm-typo-en-body-lg-600 block">{{
|
|
35
|
-
device.deviceAppOTAVersion ?? 'Not available'
|
|
36
|
-
}}</span>
|
|
37
|
-
</div>
|
|
38
|
-
<div class="flex flex-col gap-1 mb-2">
|
|
39
|
-
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">PIN Code</span>
|
|
40
|
-
<span class="fm-typo-en-body-lg-600 block">{{ isPinUpdate ? 'Yes' : 'No' }}</span>
|
|
41
|
-
</div>
|
|
42
|
-
<FmButton
|
|
43
|
-
class="w-auto"
|
|
44
|
-
variant="primary"
|
|
45
|
-
:label="device?.pinInfo ? 'Reset Device PIN' : 'Set Device PIN'"
|
|
46
|
-
@click="showUpdateDevicePin"
|
|
47
|
-
/>
|
|
48
|
-
</div>
|
|
49
|
-
<div
|
|
50
|
-
v-for="config in configs.filter((c) => c[1])"
|
|
51
|
-
class="w-full border p-[1.5rem] fm-corner-radius-lg flex flex-col gap-2"
|
|
9
|
+
{{ isTabletApp ? 'Tablet' : 'Kiosk' }}
|
|
10
|
+
</span>
|
|
11
|
+
<span
|
|
12
|
+
v-if="isTabletApp && device.slotInfo"
|
|
13
|
+
class="fm-typo-en-body-md-600 text-fm-color-typo-primary"
|
|
52
14
|
>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class="flex flex-col gap-1"
|
|
56
|
-
v-if="config[1]"
|
|
57
|
-
v-for="configEntry in Object.entries(config[1])"
|
|
58
|
-
>
|
|
59
|
-
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">{{
|
|
60
|
-
configEntry[0]
|
|
61
|
-
}}</span>
|
|
62
|
-
<span class="fm-typo-en-body-lg-600 block">{{
|
|
63
|
-
configEntry[1] == '' ? '-' : configEntry[1]
|
|
64
|
-
}}</span>
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
15
|
+
{{ device.slotInfo }}
|
|
16
|
+
</span>
|
|
67
17
|
</div>
|
|
68
|
-
<div
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
18
|
+
<div class="flex items-center gap-1">
|
|
19
|
+
<FmSideSheet
|
|
20
|
+
:header="t('order.deviceDetails')"
|
|
21
|
+
dismiss-away
|
|
22
|
+
:maxWidth="700"
|
|
23
|
+
@on:clickedAway="closeUpdateDevicePin"
|
|
24
|
+
>
|
|
25
|
+
<template #side-sheet-button>
|
|
26
|
+
<FmButton variant="tertiary" icon="more_vert" />
|
|
27
|
+
</template>
|
|
28
|
+
<div v-if="!showPinSetting" class="py-[1rem] flex flex-col gap-6">
|
|
29
|
+
<span class="fm-typo-en-title-sm-600">{{ device.name }}</span>
|
|
30
|
+
<div class="w-full border p-[1.5rem] fm-corner-radius-lg flex flex-col gap-2">
|
|
31
|
+
<span class="fm-typo-en-title-sm-600 mb-3">App Info</span>
|
|
32
|
+
<div class="flex flex-col gap-1 mb-2">
|
|
33
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">Type</span>
|
|
34
|
+
<span class="fm-typo-en-body-lg-600 block">{{ appType }}</span>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="flex flex-col gap-1 mb-2">
|
|
37
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">Machine ID</span>
|
|
38
|
+
<span class="fm-typo-en-body-lg-600 block font-mono text-sm break-all">{{
|
|
39
|
+
device.machineId
|
|
40
|
+
}}</span>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="flex flex-col gap-1 mb-2">
|
|
43
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">Version</span>
|
|
44
|
+
<span class="fm-typo-en-body-lg-600 block">{{
|
|
45
|
+
device.deviceAppVersion ?? 'Below 0.0.42'
|
|
46
|
+
}}</span>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="flex flex-col gap-1 mb-2">
|
|
49
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">OTA Version</span>
|
|
50
|
+
<span class="fm-typo-en-body-lg-600 block">{{
|
|
51
|
+
device.deviceAppOTAVersion ?? 'Not available'
|
|
52
|
+
}}</span>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="flex flex-col gap-1 mb-2">
|
|
55
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">PIN Code</span>
|
|
56
|
+
<span class="fm-typo-en-body-lg-600 block">{{ isPinUpdate ? 'Yes' : 'No' }}</span>
|
|
57
|
+
</div>
|
|
58
|
+
<FmButton
|
|
59
|
+
class="w-auto"
|
|
60
|
+
variant="primary"
|
|
61
|
+
:label="device?.pinInfo ? 'Reset Device PIN' : 'Set Device PIN'"
|
|
62
|
+
@click="showUpdateDevicePin"
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
<div
|
|
66
|
+
v-for="config in configs.filter((c) => c[1])"
|
|
67
|
+
:key="config[0]"
|
|
68
|
+
class="w-full border p-[1.5rem] fm-corner-radius-lg flex flex-col gap-2"
|
|
69
|
+
>
|
|
70
|
+
<span class="fm-typo-en-title-sm-600" v-if="config[1]">{{ config[0] }}</span>
|
|
71
|
+
<div
|
|
72
|
+
class="flex flex-col gap-1"
|
|
73
|
+
v-if="config[1]"
|
|
74
|
+
v-for="configEntry in Object.entries(config[1])"
|
|
75
|
+
:key="configEntry[0]"
|
|
76
|
+
>
|
|
77
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">{{
|
|
78
|
+
configEntry[0]
|
|
79
|
+
}}</span>
|
|
80
|
+
<span class="fm-typo-en-body-lg-600 block">{{
|
|
81
|
+
configEntry[1] == '' ? '-' : configEntry[1]
|
|
82
|
+
}}</span>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
76
85
|
</div>
|
|
77
|
-
<
|
|
78
|
-
class="
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
<div v-else>
|
|
87
|
+
<div class="py-[1rem] flex flex-col gap-6 text-center items-center">
|
|
88
|
+
<div class="flex flex-col gap-1 mb-2">
|
|
89
|
+
<span class="fm-typo-en-body-lg-400 text-fm-color-typo-secondary">{{
|
|
90
|
+
pinText
|
|
91
|
+
}}</span>
|
|
92
|
+
<span class="mt-5 block">
|
|
93
|
+
<FmPinField
|
|
94
|
+
v-if="!isPinEntered"
|
|
95
|
+
:length="4"
|
|
96
|
+
@complete="(val) => pinEntered(val)"
|
|
97
|
+
/>
|
|
98
|
+
<FmPinField
|
|
99
|
+
v-if="isPinEntered"
|
|
100
|
+
:length="4"
|
|
101
|
+
@complete="(val) => pinEntered(val)"
|
|
102
|
+
/>
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
<FmButton
|
|
106
|
+
class="w-1/3"
|
|
107
|
+
variant="secondary"
|
|
108
|
+
label="Cancel reset PIN"
|
|
109
|
+
@click="closeUpdateDevicePin"
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</FmSideSheet>
|
|
114
|
+
<FmButton
|
|
115
|
+
variant="plain"
|
|
116
|
+
icon="link_off"
|
|
117
|
+
class="text-fm-color-system-error-300"
|
|
118
|
+
@click="openUnbindDialog"
|
|
119
|
+
/>
|
|
84
120
|
</div>
|
|
85
|
-
</
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class="text-fm-color-
|
|
90
|
-
|
|
91
|
-
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<!-- Device Name (truncated for long names) -->
|
|
124
|
+
<div
|
|
125
|
+
class="fm-typo-en-body-md-600 text-fm-color-typo-primary mb-1 overflow-hidden text-ellipsis whitespace-nowrap"
|
|
126
|
+
:title="device.name"
|
|
127
|
+
>
|
|
128
|
+
{{ device.name }}
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<!-- Machine ID Row (clickable to copy) -->
|
|
132
|
+
<div
|
|
133
|
+
class="flex items-center gap-1 mb-2 cursor-pointer group"
|
|
134
|
+
@click="copyMachineId"
|
|
135
|
+
:title="t('order.clickToCopy')"
|
|
136
|
+
>
|
|
137
|
+
<span class="fm-typo-en-body-sm-400 text-fm-color-typo-tertiary font-mono">
|
|
138
|
+
{{ truncatedMachineId }}
|
|
139
|
+
</span>
|
|
140
|
+
<FmIcon
|
|
141
|
+
name="content_copy"
|
|
142
|
+
class="text-fm-color-typo-tertiary opacity-0 group-hover:opacity-100 transition-opacity"
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<!-- Info Row: Version, OTA, PIN, Last Startup -->
|
|
147
|
+
<div
|
|
148
|
+
class="flex items-center gap-3 flex-wrap fm-typo-en-body-sm-400 text-fm-color-typo-secondary"
|
|
149
|
+
>
|
|
150
|
+
<span class="flex items-center gap-1">
|
|
151
|
+
<FmIcon name="smartphone" />
|
|
152
|
+
v{{ device.deviceAppVersion ?? '?' }}
|
|
153
|
+
</span>
|
|
154
|
+
<span v-if="device.deviceAppOTAVersion" class="flex items-center gap-1">
|
|
155
|
+
<FmIcon name="system_update" />
|
|
156
|
+
{{ device.deviceAppOTAVersion }}
|
|
157
|
+
</span>
|
|
158
|
+
<span class="flex items-center gap-1">
|
|
159
|
+
<FmIcon name="lock" />
|
|
160
|
+
{{ isPinUpdate ? t('order.pinSet') : t('order.noPin') }}
|
|
161
|
+
</span>
|
|
162
|
+
<span
|
|
163
|
+
v-if="device.lastStartup"
|
|
164
|
+
class="flex items-center gap-1"
|
|
165
|
+
:title="formatFullDate(device.lastStartup)"
|
|
166
|
+
>
|
|
167
|
+
<FmIcon name="schedule" />
|
|
168
|
+
{{ formatRelativeTime(device.lastStartup) }}
|
|
169
|
+
</span>
|
|
170
|
+
</div>
|
|
92
171
|
</div>
|
|
93
172
|
</template>
|
|
94
173
|
<script setup lang="ts">
|
|
95
174
|
import type { MfKioskDevice } from '@/modules/kiosk/interface'
|
|
96
|
-
import { type SnackbarPosition, useDialog, useSnackbar } from '@feedmepos/ui-library'
|
|
97
|
-
import KioskUnbindConfirm from './KioskUnbindConfirm.vue'
|
|
98
|
-
import { computed, onUnmounted, ref } from 'vue'
|
|
99
175
|
import { useKioskStore } from '@/stores/kiosk'
|
|
100
|
-
import dayjs from 'dayjs'
|
|
101
176
|
import { useI18n } from '@feedmepos/mf-common'
|
|
177
|
+
import { useDialog, useSnackbar, type SnackbarPosition } from '@feedmepos/ui-library'
|
|
178
|
+
import dayjs from 'dayjs'
|
|
179
|
+
import relativeTime from 'dayjs/plugin/relativeTime'
|
|
180
|
+
import { computed, ref } from 'vue'
|
|
181
|
+
import KioskUnbindConfirm from './KioskUnbindConfirm.vue'
|
|
182
|
+
dayjs.extend(relativeTime)
|
|
102
183
|
|
|
103
184
|
const { t } = useI18n()
|
|
104
185
|
|
|
@@ -121,6 +202,41 @@ const appType = computed(() => {
|
|
|
121
202
|
return props.device?.deviceAppType === 'TABLET_APP' ? 'Tablet App' : 'Kiosk App'
|
|
122
203
|
})
|
|
123
204
|
|
|
205
|
+
const isTabletApp = computed(() => {
|
|
206
|
+
return props.device?.deviceAppType === 'TABLET_APP'
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
const truncatedMachineId = computed(() => {
|
|
210
|
+
const id = props.device.machineId
|
|
211
|
+
if (!id || id.length <= 16) return id
|
|
212
|
+
return `${id.slice(0, 8)}...${id.slice(-4)}`
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
async function copyMachineId() {
|
|
216
|
+
try {
|
|
217
|
+
await navigator.clipboard.writeText(props.device.machineId)
|
|
218
|
+
Snackbar.open({
|
|
219
|
+
type: 'success',
|
|
220
|
+
message: t('order.machineIdCopied'),
|
|
221
|
+
position: 'bottom' as SnackbarPosition
|
|
222
|
+
})
|
|
223
|
+
} catch {
|
|
224
|
+
Snackbar.open({
|
|
225
|
+
type: 'error',
|
|
226
|
+
message: t('order.copyFailed'),
|
|
227
|
+
position: 'bottom' as SnackbarPosition
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function formatRelativeTime(date: Date): string {
|
|
233
|
+
return dayjs(date).fromNow()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function formatFullDate(date: Date): string {
|
|
237
|
+
return dayjs(date).format('DD MMM YYYY HH:mm:ss')
|
|
238
|
+
}
|
|
239
|
+
|
|
124
240
|
// function showDeviceDetails() {
|
|
125
241
|
// Dialog.open({
|
|
126
242
|
// title: props.device.name,
|
|
@@ -1,29 +1,145 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<div class="flex flex-col gap-4">
|
|
4
|
+
<div class="flex items-center justify-between">
|
|
3
5
|
<div class="flex flex-col gap-2">
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<span class="fm-typo-en-body-md-400 block"> {{ t('order.kioskDisplayDescription') }} </span>
|
|
7
|
-
</div>
|
|
8
|
-
<div class="flex flex-col gap-6">
|
|
9
|
-
<KioskDeviceCard v-for="device in devices" :key="device.machineId" :device="device"></KioskDeviceCard>
|
|
10
|
-
<FmButton variant="plain" class="w-[10rem] border border-fm-color-primary fm-corner-radius-lg"
|
|
11
|
-
:label="t('order.bindDevice')" size="md" prepend-icon="link" @click="requestOtp" />
|
|
12
|
-
</div>
|
|
6
|
+
<span class="fm-typo-en-title-sm-600 block"> {{ t('order.kioskDisplay') }} </span>
|
|
7
|
+
<span class="fm-typo-en-body-md-400 block"> {{ t('order.kioskDisplayDescription') }} </span>
|
|
13
8
|
</div>
|
|
9
|
+
<FmButton
|
|
10
|
+
variant="primary"
|
|
11
|
+
:label="t('order.bindDevice')"
|
|
12
|
+
size="md"
|
|
13
|
+
prepend-icon="link"
|
|
14
|
+
@click="$emit('requestOtp')"
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Search and Filter Bar -->
|
|
19
|
+
<div class="flex items-center gap-3 flex-wrap">
|
|
20
|
+
<FmSearch
|
|
21
|
+
v-model:model-value="searchQuery"
|
|
22
|
+
class="flex-1 min-w-[200px] max-w-[400px]"
|
|
23
|
+
:placeholder="t('order.searchDevices')"
|
|
24
|
+
/>
|
|
25
|
+
<FmMenu>
|
|
26
|
+
<template #menu-button>
|
|
27
|
+
<FmButton
|
|
28
|
+
variant="tertiary"
|
|
29
|
+
prepend-icon="filter_list"
|
|
30
|
+
:label="activeFilterLabel"
|
|
31
|
+
class="fm-typo-en-body-md-600"
|
|
32
|
+
/>
|
|
33
|
+
</template>
|
|
34
|
+
<FmMenuItem
|
|
35
|
+
:model-value="filterType === 'all'"
|
|
36
|
+
:label="t('order.allTypes')"
|
|
37
|
+
@click="filterType = 'all'"
|
|
38
|
+
/>
|
|
39
|
+
<FmMenuItem
|
|
40
|
+
:model-value="filterType === 'tablet'"
|
|
41
|
+
:label="t('order.tabletOnly')"
|
|
42
|
+
@click="filterType = 'tablet'"
|
|
43
|
+
/>
|
|
44
|
+
<FmMenuItem
|
|
45
|
+
:model-value="filterType === 'kiosk'"
|
|
46
|
+
:label="t('order.kioskOnly')"
|
|
47
|
+
@click="filterType = 'kiosk'"
|
|
48
|
+
/>
|
|
49
|
+
</FmMenu>
|
|
50
|
+
<span class="fm-typo-en-body-sm-400 text-fm-color-typo-tertiary">
|
|
51
|
+
{{ t('order.showingDevices', { count: filteredDevices.length, total: devices.length }) }}
|
|
52
|
+
</span>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<!-- Device Cards -->
|
|
56
|
+
<div class="grid grid-cols-2 gap-2 py-8">
|
|
57
|
+
<KioskDeviceCard
|
|
58
|
+
v-for="device in filteredDevices"
|
|
59
|
+
:key="device.machineId"
|
|
60
|
+
:device="device"
|
|
61
|
+
/>
|
|
62
|
+
<div
|
|
63
|
+
v-if="filteredDevices.length === 0 && devices.length > 0"
|
|
64
|
+
class="text-center py-8 text-fm-color-typo-tertiary fm-typo-en-body-md-400 col-span-2"
|
|
65
|
+
>
|
|
66
|
+
{{ t('order.noDevicesMatch') }}
|
|
67
|
+
</div>
|
|
68
|
+
<div
|
|
69
|
+
v-if="devices.length === 0"
|
|
70
|
+
class="text-center py-8 text-fm-color-typo-tertiary fm-typo-en-body-md-400 col-span-2"
|
|
71
|
+
>
|
|
72
|
+
{{ t('order.noDevicesBound') }}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
14
75
|
</div>
|
|
76
|
+
</div>
|
|
15
77
|
</template>
|
|
16
78
|
<script setup lang="ts">
|
|
17
|
-
import
|
|
18
|
-
import type { MfKioskDevice } from "@/modules/kiosk/interface";
|
|
79
|
+
import type { MfKioskDevice } from '@/modules/kiosk/interface'
|
|
19
80
|
import { useI18n } from '@feedmepos/mf-common'
|
|
81
|
+
import { computed, ref } from 'vue'
|
|
82
|
+
import KioskDeviceCard from './KioskDeviceCard.vue'
|
|
20
83
|
|
|
21
|
-
const { t } = useI18n()
|
|
84
|
+
const { t } = useI18n()
|
|
22
85
|
|
|
23
86
|
interface Props {
|
|
24
|
-
|
|
25
|
-
requestOtp: () => Promise<void>;
|
|
87
|
+
devices: MfKioskDevice[]
|
|
26
88
|
}
|
|
27
89
|
|
|
28
|
-
const props = defineProps<Props>()
|
|
29
|
-
|
|
90
|
+
const props = defineProps<Props>()
|
|
91
|
+
defineEmits<{
|
|
92
|
+
(e: 'requestOtp'): void
|
|
93
|
+
}>()
|
|
94
|
+
|
|
95
|
+
const searchQuery = ref('')
|
|
96
|
+
const filterType = ref<'all' | 'tablet' | 'kiosk'>('all')
|
|
97
|
+
|
|
98
|
+
const activeFilterLabel = computed(() => {
|
|
99
|
+
switch (filterType.value) {
|
|
100
|
+
case 'tablet':
|
|
101
|
+
return t('order.tabletOnly')
|
|
102
|
+
case 'kiosk':
|
|
103
|
+
return t('order.kioskOnly')
|
|
104
|
+
default:
|
|
105
|
+
return t('order.allTypes')
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const filteredDevices = computed(() => {
|
|
110
|
+
let result = props.devices
|
|
111
|
+
|
|
112
|
+
// Apply type filter
|
|
113
|
+
if (filterType.value === 'tablet') {
|
|
114
|
+
result = result.filter((d) => d.deviceAppType === 'TABLET_APP')
|
|
115
|
+
} else if (filterType.value === 'kiosk') {
|
|
116
|
+
result = result.filter((d) => d.deviceAppType !== 'TABLET_APP')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Apply search filter
|
|
120
|
+
const query = searchQuery.value.toLowerCase().trim()
|
|
121
|
+
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
|
+
return name.includes(query) || machineId.includes(query) || slot.includes(query)
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Sort tablets by slot, kiosks by name
|
|
131
|
+
return result.sort((a, b) => {
|
|
132
|
+
// Tablets first
|
|
133
|
+
if (a.deviceAppType === 'TABLET_APP' && b.deviceAppType !== 'TABLET_APP') return -1
|
|
134
|
+
if (a.deviceAppType !== 'TABLET_APP' && b.deviceAppType === 'TABLET_APP') return 1
|
|
135
|
+
|
|
136
|
+
// For tablets, sort by slot
|
|
137
|
+
if (a.deviceAppType === 'TABLET_APP' && b.deviceAppType === 'TABLET_APP') {
|
|
138
|
+
return (a.slotInfo || '').localeCompare(b.slotInfo || '')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// For kiosks, sort by name
|
|
142
|
+
return (a.name || '').localeCompare(b.name || '')
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
</script>
|
|
@@ -72,9 +72,9 @@ const initializeCheckboxes = () => {
|
|
|
72
72
|
parentCheckbox.value = initialValues.length === checkboxChildren.length
|
|
73
73
|
if (!(Object.keys(props.ePaymentTypes).length === 0)) {
|
|
74
74
|
if (props.ePaymentTypes.terminal) {
|
|
75
|
-
useTerminal.value = true
|
|
75
|
+
useTerminal.value = true
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
if (props.ePaymentTypes.card.terminal) {
|
|
79
79
|
cardPaymentRadioVal.value = F_ORDER_E_PAYMENT_TYPE.enum.TERMINAL
|
|
80
80
|
useCard.value = true
|
|
@@ -157,6 +157,7 @@ const handleChildUpdate = (ev: string[]) => {
|
|
|
157
157
|
|
|
158
158
|
const handleEPaymentCardUpdate = (ev: string) => {
|
|
159
159
|
const ePaymentObj = {
|
|
160
|
+
terminal: false,
|
|
160
161
|
card: {
|
|
161
162
|
terminal: false,
|
|
162
163
|
nfc: false
|
|
@@ -195,6 +196,10 @@ const handleEPaymentCardUpdate = (ev: string) => {
|
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
|
|
199
|
+
if (useTerminal.value) {
|
|
200
|
+
ePaymentObj.terminal = true
|
|
201
|
+
}
|
|
202
|
+
|
|
198
203
|
emits('update:ePaymentTypes', ePaymentObj)
|
|
199
204
|
}
|
|
200
205
|
|
|
@@ -209,7 +214,7 @@ const handleEPaymentEWalletUpdate = () => {
|
|
|
209
214
|
qrPay: false,
|
|
210
215
|
scanPay: false,
|
|
211
216
|
terminalScanPay: false,
|
|
212
|
-
terminalQrPay: false
|
|
217
|
+
terminalQrPay: false
|
|
213
218
|
}
|
|
214
219
|
} as FdoEPaymentMethod
|
|
215
220
|
|
|
@@ -19,6 +19,7 @@ import { type FmTabProps } from '@feedmepos/ui-library'
|
|
|
19
19
|
import { ref, computed, onMounted, watch } from 'vue'
|
|
20
20
|
import DeliverySetting from './delivery/DeliverySetting.vue'
|
|
21
21
|
import DineInSetting from './dine-in/DineInSetting.vue'
|
|
22
|
+
import DriveThruSetting from './drive-thru/DriveThruSetting.vue'
|
|
22
23
|
import PickUpSetting from './pickup/PickUpSetting.vue'
|
|
23
24
|
import SmsSetting from './sms/SmsSetting.vue'
|
|
24
25
|
// import ServiceChargeSetting from './servicecharge/ServiceChargeSetting.vue'
|
|
@@ -38,6 +39,7 @@ type OrderSettingMenuItemValue =
|
|
|
38
39
|
| 'delivery'
|
|
39
40
|
| 'pickup'
|
|
40
41
|
| 'dinein'
|
|
42
|
+
| 'driveThru'
|
|
41
43
|
// | 'servicecharge'
|
|
42
44
|
| 'sms'
|
|
43
45
|
// | 'discountRule'
|
|
@@ -49,6 +51,7 @@ const settingItem = computed<FmTabProps[]>(() => [
|
|
|
49
51
|
{ label: t('order.delivery'), value: 'delivery' },
|
|
50
52
|
{ label: t('order.pickup'), value: 'pickup' },
|
|
51
53
|
{ label: t('order.dineIn'), value: 'dinein' },
|
|
54
|
+
{ label: t('order.driveThru'), value: 'driveThru' },
|
|
52
55
|
// { label: t('order.serviceCharge'), value: 'servicecharge' },
|
|
53
56
|
// { label: t('order.discountRule.title'), value: 'discountRule' },
|
|
54
57
|
{ label: t('order.sms'), value: 'sms' },
|
|
@@ -62,6 +65,8 @@ const currentComponent = computed(() => {
|
|
|
62
65
|
return DeliverySetting
|
|
63
66
|
case 'dinein':
|
|
64
67
|
return DineInSetting
|
|
68
|
+
case 'driveThru':
|
|
69
|
+
return DriveThruSetting
|
|
65
70
|
case 'pickup':
|
|
66
71
|
return PickUpSetting
|
|
67
72
|
case 'sms':
|
|
@@ -83,7 +88,7 @@ onMounted(async () => {
|
|
|
83
88
|
}
|
|
84
89
|
})
|
|
85
90
|
|
|
86
|
-
watch(currentRestaurant, async (newRestaurant) => {
|
|
91
|
+
watch(currentRestaurant, async (newRestaurant: any) => {
|
|
87
92
|
if (newRestaurant?._id) {
|
|
88
93
|
await menuStore.loadCatalogOptions(newRestaurant._id)
|
|
89
94
|
}
|