@feedmepos/mf-order-setting 0.0.54 → 0.0.56-dev.2
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/.tsbuildinfo +1 -0
- package/dist/{KioskDevicesView-CMKNjgWx.js → KioskDevicesView-Qv-xd_kZ.js} +1 -1
- package/dist/{KioskDevicesView.vue_vue_type_script_setup_true_lang-B1sNvlUC.js → KioskDevicesView.vue_vue_type_script_setup_true_lang-CCF1mKni.js} +2 -2
- package/dist/KioskSettingView-CvvrK6Bv.js +643 -0
- package/dist/{KioskView-U-Wg8oMC.js → KioskView-CppTVBv-.js} +117 -117
- package/dist/OrderSettingsView-C38N61dM.js +36564 -0
- package/dist/{app-CFfgPAd8.js → app-Bss1GkKY.js} +392 -228
- package/dist/app.js +1 -1
- package/dist/{dayjs.min-CuRr-wlf.js → dayjs.min-DZfxGUk4.js} +1 -1
- package/dist/frontend/mf-order/src/api/reservation/index.d.ts +8 -0
- package/dist/frontend/mf-order/src/app.d.ts +164 -0
- package/dist/frontend/mf-order/src/main.d.ts +164 -0
- package/dist/frontend/mf-order/src/stores/order-setting/index.d.ts +3 -0
- package/dist/frontend/mf-order/src/stores/restaurant/index.d.ts +1 -1
- package/dist/frontend/mf-order/src/views/kiosk/settings/KioskPaymentTypeSection.vue.d.ts +13 -3
- package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/ExternalSetting.vue.d.ts +12 -4
- package/dist/frontend/mf-order/src/views/order-settings/dine-in/OfflinePaymentTypeDialog.vue.d.ts +4 -4
- package/dist/frontend/mf-order/src/views/order-settings/dine-in/PaymentType.vue.d.ts +38 -4
- package/dist/frontend/mf-order/src/views/order-settings/pickup/PaymentSidesheet.vue.d.ts +1 -0
- package/dist/frontend/mf-order/src/views/order-settings/reservation/CopySettingsSheet.vue.d.ts +186 -0
- package/dist/frontend/mf-order/src/views/order-settings/reservation/CustomSelect.vue.d.ts +15 -0
- package/dist/frontend/mf-order/src/views/order-settings/reservation/CustomTimePicker.vue.d.ts +11 -0
- package/dist/frontend/mf-order/src/views/order-settings/reservation/ReservationSetting.vue.d.ts +2 -0
- package/dist/{index-Bj0bCGTm.js → index-B6AGCsrw.js} +3 -3
- package/dist/index-BpKR-Cxd.js +19757 -0
- package/dist/{menu.dto-DAh1J2ES.js → menu.dto-C_B3M2fs.js} +7390 -7134
- package/dist/package/entity/incoming-order/incoming-order.do.d.ts +22443 -3
- package/dist/package/entity/incoming-order/incoming-order.dto.d.ts +3 -3
- package/dist/package/entity/incoming-order/incoming-order.enum.d.ts +1 -1
- package/dist/package/entity/index.d.ts +6 -0
- package/dist/package/entity/marketing/marketing.dto.d.ts +1 -1
- package/dist/package/entity/order/dine-in/qr.dto.d.ts +38 -0
- package/dist/package/entity/order/order.do.d.ts +6358 -2
- package/dist/package/entity/order/order.dto.d.ts +22 -0
- package/dist/package/entity/order-platform/deliveroo/deliveroo-dto.d.ts +3 -0
- package/dist/package/entity/order-platform/deliveroo/deliveroo-setting.do.d.ts +3 -0
- package/dist/package/entity/order-platform/external/order/external-order.do.d.ts +12 -12
- package/dist/package/entity/order-platform/external/order/external-order.dto.d.ts +32 -32
- package/dist/package/entity/order-platform/external/setting/external-setting.do.d.ts +21 -3
- package/dist/package/entity/order-platform/external/setting/external-setting.dto.d.ts +12 -2
- package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.do.d.ts +3 -0
- package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.dto.d.ts +3 -0
- package/dist/package/entity/order-platform/grabfood/grabfood-edit-order.do.d.ts +9 -1
- package/dist/package/entity/order-platform/grabfood/grabfood-settings.do.d.ts +2 -2
- package/dist/package/entity/order-platform/grabfood/grabfood.dto.d.ts +3 -3
- package/dist/package/entity/order-platform/order-platform.dto.d.ts +2 -2
- package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.do.d.ts +3 -0
- package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.dto.d.ts +3 -0
- package/dist/package/entity/order-setting/order-setting.do.d.ts +864 -0
- package/dist/package/entity/order-setting/order-setting.dto.d.ts +6 -0
- package/dist/package/entity/order-setting/reservationV2/reservation.do.d.ts +1269 -0
- package/dist/package/entity/queue/queue.do.d.ts +3 -8
- package/dist/package/entity/queue/queue.dto.d.ts +10 -0
- package/dist/package/entity/reservation/reservation.do.d.ts +105 -0
- package/dist/package/entity/reservation/reservation.dto.d.ts +335 -0
- package/dist/package/entity/reservation/reservation.enum.d.ts +3 -0
- package/dist/package/entity/reservation/reservation.utils.d.ts +152 -0
- package/dist/style.css +1 -0
- package/package.json +1 -1
- package/src/api/reservation/index.ts +28 -0
- package/src/assets/images/not-found.png +0 -0
- package/src/locales/en-US.json +56 -0
- package/src/locales/th-TH.json +54 -0
- package/src/locales/zh-CN.json +54 -0
- package/src/views/kiosk/KioskSummary.vue +3 -0
- package/src/views/kiosk/settings/KioskPaymentTypeSection.vue +99 -211
- package/src/views/kiosk/settings/KioskSettingView.vue +43 -25
- package/src/views/order-settings/OrderSettingsView.vue +6 -1
- package/src/views/order-settings/dine-in/DineInSetting.vue +1 -0
- package/src/views/order-settings/dine-in/OfflinePaymentTypeDialog.vue +2 -3
- package/src/views/order-settings/dine-in/PaymentType.vue +151 -43
- package/src/views/order-settings/pickup/PaymentSidesheet.vue +33 -172
- package/src/views/order-settings/pickup/PickUpSettingDialogContent.vue +1 -0
- package/src/views/order-settings/reservation/CopySettingsSheet.vue +256 -0
- package/src/views/order-settings/reservation/CustomSelect.vue +99 -0
- package/src/views/order-settings/reservation/CustomTimePicker.vue +311 -0
- package/src/views/order-settings/reservation/ReservationSetting.vue +1555 -0
- package/tsconfig.app.json +8 -6
- package/dist/KioskSettingView-BE_pMA-i.js +0 -720
- package/dist/OrderSettingsView-BWzaITT6.js +0 -51916
- package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +0 -1
- package/dist/index-BXsnV_eO.js +0 -150
|
@@ -1,78 +1,12 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<div class="flex-grow fm-typo-en-title-sm-600 mb-3">{{ t('order.acceptedPayment') }}</div>
|
|
4
|
-
<FmCheckbox
|
|
5
|
-
:model-value="parent"
|
|
6
|
-
value=""
|
|
7
|
-
:label="t('order.all')"
|
|
8
|
-
@update:model-value="handleUpdate"
|
|
9
|
-
:indeterminate="nestedVal.length > 0 && nestedVal.length < checkboxChildren.length"
|
|
10
|
-
/>
|
|
11
|
-
<div class="flex flex-row items-center">
|
|
12
|
-
<FmCheckbox
|
|
13
|
-
v-model="nestedVal"
|
|
14
|
-
:label="checkboxChildren[0].label"
|
|
15
|
-
:value="checkboxChildren[0].value"
|
|
16
|
-
@update:model-value="handleUpdate"
|
|
17
|
-
/>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="mt-3 mb-3">
|
|
20
|
-
<CustomPayment
|
|
21
|
-
:offlinePaymentTypes="localOfflinePaymentTypeCopy ?? []"
|
|
22
|
-
@update:offlinePaymentTypes="updateOfflinePaymentTypes"
|
|
23
|
-
/>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<div v-if="nestedVal.includes(F_ORDER_PAYMENT_TYPE.enum.cash)">
|
|
27
|
-
<div v-if="localOfflinePaymentTypes?.length === 0">
|
|
28
|
-
<FmCard variant="outlined">
|
|
29
|
-
<FmCardSection>{{ t('order.cash') }}</FmCardSection>
|
|
30
|
-
</FmCard>
|
|
31
|
-
</div>
|
|
32
|
-
<p v-for="(type, i) in localOfflinePaymentTypes || []" :key="i">
|
|
33
|
-
<FmCard variant="outlined" class="mb-3">
|
|
34
|
-
<FmCardSection class="flex items-center justify-between">
|
|
35
|
-
<div>
|
|
36
|
-
<span class="font-bold">{{ type.name }}</span>
|
|
37
|
-
<br />
|
|
38
|
-
<span>{{ type.instruction }}</span>
|
|
39
|
-
</div>
|
|
40
|
-
<div>
|
|
41
|
-
<FmButton append-icon="delete" variant="plain" @click="deleteOfflinePayment(i)" />
|
|
42
|
-
</div>
|
|
43
|
-
</FmCardSection>
|
|
44
|
-
</FmCard>
|
|
45
|
-
</p>
|
|
46
|
-
</div>
|
|
47
|
-
|
|
48
|
-
<div class="m-5"></div>
|
|
49
|
-
|
|
50
|
-
<FmCheckbox
|
|
51
|
-
v-model="nestedVal"
|
|
52
|
-
:label="checkboxChildren[1].label"
|
|
53
|
-
:value="checkboxChildren[1].value"
|
|
54
|
-
@update:model-value="handleUpdate"
|
|
55
|
-
:disabled="checkboxChildren[1].disable"
|
|
56
|
-
>
|
|
57
|
-
</FmCheckbox>
|
|
58
|
-
|
|
59
|
-
<div class="text-fm-color-secondary-salmon fm-typo-en-body-sm-600 ml-7">
|
|
60
|
-
{{ checkboxChildren?.[1]?.suffix?.text }}
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</template>
|
|
64
|
-
|
|
65
1
|
<script setup lang="ts">
|
|
66
2
|
import {
|
|
67
3
|
F_ORDER_PAYMENT_TYPE,
|
|
68
|
-
FdoRestaurantPickup,
|
|
69
4
|
FdoOfflinePaymentMethod
|
|
70
5
|
} from '@feedmepos/core/entity'
|
|
71
|
-
import CustomPayment from './CustomPayment.vue'
|
|
72
6
|
import { useI18n } from '@feedmepos/mf-common'
|
|
7
|
+
import PaymentType from '../dine-in/PaymentType.vue'
|
|
73
8
|
|
|
74
|
-
import { computed
|
|
75
|
-
import type { CheckboxOption } from '../../../components/type'
|
|
9
|
+
import { computed } from 'vue'
|
|
76
10
|
|
|
77
11
|
const { t } = useI18n()
|
|
78
12
|
const emits = defineEmits<{
|
|
@@ -89,118 +23,45 @@ const props = defineProps<{
|
|
|
89
23
|
paymentTypes: F_ORDER_PAYMENT_TYPE[] | null | undefined
|
|
90
24
|
offlinePaymentTypes: any
|
|
91
25
|
allowEPayment: Boolean | String
|
|
26
|
+
allowCredit?: Boolean | String
|
|
92
27
|
}>()
|
|
93
28
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
nestedVal.value.length ===
|
|
97
|
-
(props.allowEPayment === true
|
|
98
|
-
? checkboxChildren.value.length
|
|
99
|
-
: checkboxChildren.value.length - 1)
|
|
100
|
-
)
|
|
101
|
-
})
|
|
102
|
-
const nestedVal = ref<string[]>([])
|
|
103
|
-
|
|
104
|
-
const checkboxChildren = computed(() => {
|
|
105
|
-
const allowedEPayment = props.allowEPayment === true
|
|
106
|
-
const options: CheckboxOption[] = [
|
|
107
|
-
{
|
|
108
|
-
label: `${t('order.offlinePayment')}`,
|
|
109
|
-
value: F_ORDER_PAYMENT_TYPE.enum.cash
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
label: `${t('order.ePayment')}`,
|
|
113
|
-
disable: !allowedEPayment,
|
|
114
|
-
suffix: !allowedEPayment
|
|
115
|
-
? {
|
|
116
|
-
text: `(${props.allowEPayment || t('order.notAllowed')})`,
|
|
117
|
-
class: 'text-negative t-system'
|
|
118
|
-
}
|
|
119
|
-
: {
|
|
120
|
-
text: '',
|
|
121
|
-
class: ''
|
|
122
|
-
},
|
|
123
|
-
value: F_ORDER_PAYMENT_TYPE.enum.ePayment
|
|
124
|
-
}
|
|
125
|
-
]
|
|
126
|
-
return options
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
const allowEPayment = computed(() => {
|
|
130
|
-
const paymentTypes = props.paymentTypes || []
|
|
131
|
-
return paymentTypes.includes(F_ORDER_PAYMENT_TYPE.enum.ePayment)
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
const allowOfflinePayment = computed(() => {
|
|
135
|
-
const paymentTypes = props.paymentTypes || []
|
|
136
|
-
const offlinePaymentTypes = props.offlinePaymentTypes || []
|
|
29
|
+
const allowEPaymentBool = computed(() => props.allowEPayment === true)
|
|
30
|
+
const allowCreditBool = computed(() => props.allowCredit === true)
|
|
137
31
|
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
initializeCheckboxes()
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
const initializeCheckboxes = () => {
|
|
146
|
-
const initialValues: string[] = []
|
|
147
|
-
if (allowOfflinePayment.value) {
|
|
148
|
-
initialValues.push(F_ORDER_PAYMENT_TYPE.enum.cash)
|
|
32
|
+
const disabledSuffixes = computed(() => {
|
|
33
|
+
const suffixes: Record<string, string> = {}
|
|
34
|
+
if (!allowEPaymentBool.value) {
|
|
35
|
+
suffixes['e-payment'] = typeof props.allowEPayment === 'string' ? props.allowEPayment : t('order.notAllowed')
|
|
149
36
|
}
|
|
150
|
-
if (
|
|
151
|
-
|
|
37
|
+
if (!allowCreditBool.value) {
|
|
38
|
+
suffixes['credit'] = typeof props.allowCredit === 'string' ? props.allowCredit : t('order.notAllowed')
|
|
152
39
|
}
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const handleUpdate = (ev: boolean | string[]) => {
|
|
157
|
-
if (typeof ev === 'boolean') {
|
|
158
|
-
const filteredCheckboxChildren = checkboxChildren.value.filter(
|
|
159
|
-
(item) =>
|
|
160
|
-
(item.value === F_ORDER_PAYMENT_TYPE.enum.ePayment && props.allowEPayment === true) ||
|
|
161
|
-
item.value !== F_ORDER_PAYMENT_TYPE.enum.ePayment
|
|
162
|
-
)
|
|
163
|
-
nestedVal.value = ev ? filteredCheckboxChildren.map((item) => item.value) : []
|
|
164
|
-
} else {
|
|
165
|
-
nestedVal.value = ev
|
|
166
|
-
}
|
|
167
|
-
const updatedPaymentTypes: F_ORDER_PAYMENT_TYPE[] = nestedVal.value.map((v) =>
|
|
168
|
-
F_ORDER_PAYMENT_TYPE.parse(v)
|
|
169
|
-
)
|
|
170
|
-
const updatedOfflinePaymentTypes = nestedVal.value.includes(F_ORDER_PAYMENT_TYPE.enum.cash)
|
|
171
|
-
? props.offlinePaymentTypes || []
|
|
172
|
-
: []
|
|
173
|
-
|
|
174
|
-
emits('update:payments', {
|
|
175
|
-
paymentTypes: updatedPaymentTypes,
|
|
176
|
-
offlinePaymentTypes: updatedOfflinePaymentTypes
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const localOfflinePaymentTypes = ref<FdoRestaurantPickup['offlinePaymentTypes']>(
|
|
181
|
-
props.offlinePaymentTypes ?? []
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
// Create a computed copy of `localOfflinePaymentTypes.value`
|
|
185
|
-
const localOfflinePaymentTypeCopy = computed(() => {
|
|
186
|
-
return localOfflinePaymentTypes.value ? [...localOfflinePaymentTypes.value] : []
|
|
40
|
+
return suffixes
|
|
187
41
|
})
|
|
188
42
|
|
|
189
|
-
function
|
|
190
|
-
|
|
43
|
+
function updatePayments(
|
|
44
|
+
paymentTypes: F_ORDER_PAYMENT_TYPE[],
|
|
45
|
+
offlinePaymentTypes: FdoOfflinePaymentMethod[] | null | undefined
|
|
46
|
+
) {
|
|
191
47
|
emits('update:payments', {
|
|
192
|
-
paymentTypes
|
|
193
|
-
offlinePaymentTypes
|
|
48
|
+
paymentTypes,
|
|
49
|
+
offlinePaymentTypes
|
|
194
50
|
})
|
|
195
51
|
}
|
|
196
|
-
|
|
197
|
-
function deleteOfflinePayment(index: number) {
|
|
198
|
-
if (localOfflinePaymentTypes.value) {
|
|
199
|
-
localOfflinePaymentTypes.value.splice(index, 1)
|
|
200
|
-
emits('update:payments', {
|
|
201
|
-
paymentTypes: props.paymentTypes?? [],
|
|
202
|
-
offlinePaymentTypes: localOfflinePaymentTypes.value
|
|
203
|
-
})
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
52
|
</script>
|
|
53
|
+
|
|
54
|
+
<template>
|
|
55
|
+
<div>
|
|
56
|
+
<PaymentType
|
|
57
|
+
:payment-types="paymentTypes || []"
|
|
58
|
+
:offline-payment-types="offlinePaymentTypes || []"
|
|
59
|
+
:allow-e-payment="allowEPaymentBool"
|
|
60
|
+
:allow-credit="allowCreditBool"
|
|
61
|
+
:show-disabled-options="true"
|
|
62
|
+
:disabled-suffixes="disabledSuffixes"
|
|
63
|
+
@update:payment-types="(v) => updatePayments(v, offlinePaymentTypes)"
|
|
64
|
+
@update:offline-payment-types="(v) => updatePayments(paymentTypes || [], v)"
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
</template>
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FmSideSheet :max-width="560" v-model="showSheet">
|
|
3
|
+
<template #side-sheet-button>
|
|
4
|
+
<FmButton label="Copy settings from another restaurant" icon="edit" variant="plain"
|
|
5
|
+
class="border-1 rounded-lg border-fm-color-primary" />
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<template #side-sheet-header> Copy settings from </template>
|
|
9
|
+
|
|
10
|
+
<template #default>
|
|
11
|
+
<div class="flex flex-col gap-24">
|
|
12
|
+
<!-- Restaurant Selector -->
|
|
13
|
+
<div>
|
|
14
|
+
<FmSelect :items="availableRestaurants" :model-value="selectedRestaurantId"
|
|
15
|
+
@update:model-value="(v: string) => (selectedRestaurantId = v)" placeholder="Select" :width="512" />
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Configuration Section -->
|
|
19
|
+
<div>
|
|
20
|
+
<div class="fm-typo-en-title-sm-600 mb-16">Configuration</div>
|
|
21
|
+
|
|
22
|
+
<div class="flex flex-col gap-12 pl-8">
|
|
23
|
+
<!-- All Checkbox -->
|
|
24
|
+
<FmCheckbox label="All" :model-value="isAllSelected" value="all" @update:model-value="toggleAll" />
|
|
25
|
+
|
|
26
|
+
<!-- Individual Configuration Checkboxes -->
|
|
27
|
+
<FmCheckbox label="Reservation Availability" :model-value="configOptions.reservationAvailability"
|
|
28
|
+
value="reservationAvailability"
|
|
29
|
+
@update:model-value="(v: boolean) => updateConfigOption('reservationAvailability', v)" />
|
|
30
|
+
|
|
31
|
+
<FmCheckbox label="Time Settings" :model-value="configOptions.timeSettings" value="timeSettings"
|
|
32
|
+
@update:model-value="(v: boolean) => updateConfigOption('timeSettings', v)" />
|
|
33
|
+
|
|
34
|
+
<FmCheckbox label="Table Capacity Settings" :model-value="configOptions.tableCapacity" value="tableCapacity"
|
|
35
|
+
@update:model-value="(v: boolean) => updateConfigOption('tableCapacity', v)" />
|
|
36
|
+
|
|
37
|
+
<FmCheckbox label="Pre-order" :model-value="configOptions.preorder" value="preorder"
|
|
38
|
+
@update:model-value="(v: boolean) => updateConfigOption('preorder', v)" />
|
|
39
|
+
|
|
40
|
+
<FmCheckbox label="Guest Preferences" :model-value="configOptions.guestPreferences" value="guestPreferences"
|
|
41
|
+
@update:model-value="(v: boolean) => updateConfigOption('guestPreferences', v)" />
|
|
42
|
+
|
|
43
|
+
<FmCheckbox label="Guest Message" :model-value="configOptions.guestMessage" value="guestMessage"
|
|
44
|
+
@update:model-value="(v: boolean) => updateConfigOption('guestMessage', v)" />
|
|
45
|
+
|
|
46
|
+
<FmCheckbox label="Cancellation Policy" :model-value="configOptions.cancellationPolicy"
|
|
47
|
+
value="cancellationPolicy"
|
|
48
|
+
@update:model-value="(v: boolean) => updateConfigOption('cancellationPolicy', v)" />
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<template #side-sheet-footer>
|
|
55
|
+
<div class="flex flex-col gap-16 w-full">
|
|
56
|
+
<FmSnackbar variant="warning" class="!shadow-none"
|
|
57
|
+
:description="`This will overwrite all reservation settings for ${targetRestaurantName}`" />
|
|
58
|
+
<div class="flex items-center justify-start gap-8">
|
|
59
|
+
<FmButton label="Apply" @click="handleApply" :disabled="!canApply" :loading="isLoading" />
|
|
60
|
+
<FmButton label="Cancel" variant="tertiary" @click="handleCancel" />
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
</FmSideSheet>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<script setup lang="ts">
|
|
68
|
+
import { useCoreStore, useI18n } from '@feedmepos/mf-common'
|
|
69
|
+
import { computed, ref, watch } from 'vue'
|
|
70
|
+
import { ReservationApi } from '@/api/reservation'
|
|
71
|
+
import { useSnackbarFunctions } from '@/components/snackbar'
|
|
72
|
+
import type { FdoOrderReservationSettingsV2 } from '@entity'
|
|
73
|
+
|
|
74
|
+
const { t } = useI18n()
|
|
75
|
+
const { showSuccess, showError } = useSnackbarFunctions()
|
|
76
|
+
const Core = useCoreStore()
|
|
77
|
+
|
|
78
|
+
// Props
|
|
79
|
+
const props = defineProps<{
|
|
80
|
+
currentSettings: FdoOrderReservationSettingsV2
|
|
81
|
+
}>()
|
|
82
|
+
|
|
83
|
+
// Emits
|
|
84
|
+
const emit = defineEmits<{
|
|
85
|
+
apply: [settings: Partial<FdoOrderReservationSettingsV2>]
|
|
86
|
+
}>()
|
|
87
|
+
|
|
88
|
+
// State
|
|
89
|
+
const showSheet = ref(false)
|
|
90
|
+
const isLoading = ref(false)
|
|
91
|
+
|
|
92
|
+
const configOptions = ref({
|
|
93
|
+
reservationAvailability: false,
|
|
94
|
+
timeSettings: false,
|
|
95
|
+
tableCapacity: false,
|
|
96
|
+
preorder: false,
|
|
97
|
+
guestPreferences: false,
|
|
98
|
+
guestMessage: false,
|
|
99
|
+
cancellationPolicy: false
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// Computed
|
|
103
|
+
const { currentRestaurant } = Core
|
|
104
|
+
|
|
105
|
+
const availableRestaurants = computed(() => {
|
|
106
|
+
// Filter out current restaurant from the list
|
|
107
|
+
return (Core.restaurants.value ?? [])
|
|
108
|
+
.filter((r) => r._id !== currentRestaurant.value?._id)
|
|
109
|
+
.map((r) => ({ label: r.profile.name, value: r._id }))
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const selectedRestaurantId = ref<string>('')
|
|
113
|
+
|
|
114
|
+
// Watch to set initial value when availableRestaurants loads
|
|
115
|
+
watch(availableRestaurants, (newRestaurants) => {
|
|
116
|
+
if (newRestaurants.length > 0 && !selectedRestaurantId.value) {
|
|
117
|
+
selectedRestaurantId.value = newRestaurants[0].value
|
|
118
|
+
}
|
|
119
|
+
}, { immediate: true })
|
|
120
|
+
|
|
121
|
+
const targetRestaurantName = computed(() => {
|
|
122
|
+
return currentRestaurant.value?.profile.name ?? ''
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const isAllSelected = computed(() => {
|
|
126
|
+
return Object.values(configOptions.value).every((v) => v === true)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const canApply = computed(() => {
|
|
130
|
+
return (
|
|
131
|
+
selectedRestaurantId.value !== '' && Object.values(configOptions.value).some((v) => v === true)
|
|
132
|
+
)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// Methods
|
|
136
|
+
function toggleAll(value: boolean) {
|
|
137
|
+
Object.keys(configOptions.value).forEach((key) => {
|
|
138
|
+
configOptions.value[key as keyof typeof configOptions.value] = value
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function updateConfigOption(key: keyof typeof configOptions.value, value: boolean) {
|
|
143
|
+
configOptions.value[key] = value
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function handleApply() {
|
|
147
|
+
if (!selectedRestaurantId.value) {
|
|
148
|
+
showError('Please select a restaurant')
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!Object.values(configOptions.value).some((v) => v)) {
|
|
153
|
+
showError('Please select at least one configuration option')
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
isLoading.value = true
|
|
159
|
+
|
|
160
|
+
// Fetch settings from selected restaurant
|
|
161
|
+
const sourceSettings = await ReservationApi.getReservationSetting(selectedRestaurantId.value)
|
|
162
|
+
|
|
163
|
+
if (!sourceSettings || !sourceSettings.ranges || sourceSettings.ranges.length === 0) {
|
|
164
|
+
showError('No settings found for the selected restaurant')
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Build partial settings object based on selected options
|
|
169
|
+
const updatedSettings: Partial<FdoOrderReservationSettingsV2> = {}
|
|
170
|
+
const sourceRange = sourceSettings.ranges[0]
|
|
171
|
+
const currentRange = props.currentSettings.ranges[0]
|
|
172
|
+
|
|
173
|
+
// Create a copy of the current range to modify
|
|
174
|
+
const updatedRange = { ...currentRange }
|
|
175
|
+
|
|
176
|
+
// Copy selected configuration sections
|
|
177
|
+
if (configOptions.value.reservationAvailability) {
|
|
178
|
+
// Copy enable state, min/max lead duration
|
|
179
|
+
updatedRange.enable = sourceRange.enable
|
|
180
|
+
updatedRange.minLeadDuration = { ...sourceRange.minLeadDuration }
|
|
181
|
+
updatedRange.maxLeadDuration = { ...sourceRange.maxLeadDuration }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (configOptions.value.timeSettings) {
|
|
185
|
+
// Copy operating hours, slot interval, booking duration
|
|
186
|
+
updatedRange.operatingHours = JSON.parse(JSON.stringify(sourceRange.operatingHours))
|
|
187
|
+
updatedRange.slotInterval = sourceRange.slotInterval
|
|
188
|
+
updatedRange.bookingDuration = sourceRange.bookingDuration
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (configOptions.value.tableCapacity) {
|
|
192
|
+
// Copy capacity tiers
|
|
193
|
+
updatedRange.capacityTiers = JSON.parse(JSON.stringify(sourceRange.capacityTiers))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (configOptions.value.preorder) {
|
|
197
|
+
// Copy preorder setting
|
|
198
|
+
updatedRange.enablePreorder = sourceRange.enablePreorder
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (configOptions.value.guestPreferences) {
|
|
202
|
+
// Copy preferences
|
|
203
|
+
updatedRange.preferences = JSON.parse(JSON.stringify(sourceRange.preferences))
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (configOptions.value.guestMessage) {
|
|
207
|
+
// Copy guest message
|
|
208
|
+
updatedRange.guestMessage = sourceRange.guestMessage
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (configOptions.value.cancellationPolicy) {
|
|
212
|
+
// Copy cancellation policy
|
|
213
|
+
updatedRange.cancellationPolicy = sourceRange.cancellationPolicy
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Update the settings with the modified range
|
|
217
|
+
updatedSettings.ranges = [updatedRange]
|
|
218
|
+
|
|
219
|
+
// Emit the updated settings to parent
|
|
220
|
+
emit('apply', updatedSettings)
|
|
221
|
+
|
|
222
|
+
// Show success with source outlet name
|
|
223
|
+
const sourceName = availableRestaurants.value.find(r => r.value === selectedRestaurantId.value)?.label ?? 'selected outlet'
|
|
224
|
+
showSuccess(`Settings from "${sourceName}" have been applied. Please review and save changes.`)
|
|
225
|
+
|
|
226
|
+
// Close the side sheet
|
|
227
|
+
showSheet.value = false
|
|
228
|
+
|
|
229
|
+
// Reset form
|
|
230
|
+
resetForm()
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error('Error copying settings:', error)
|
|
233
|
+
showError('Failed to copy settings. Please try again.')
|
|
234
|
+
} finally {
|
|
235
|
+
isLoading.value = false
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function handleCancel() {
|
|
240
|
+
showSheet.value = false
|
|
241
|
+
resetForm()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function resetForm() {
|
|
245
|
+
selectedRestaurantId.value = ''
|
|
246
|
+
configOptions.value = {
|
|
247
|
+
reservationAvailability: false,
|
|
248
|
+
timeSettings: false,
|
|
249
|
+
tableCapacity: false,
|
|
250
|
+
preorder: false,
|
|
251
|
+
guestPreferences: false,
|
|
252
|
+
guestMessage: false,
|
|
253
|
+
cancellationPolicy: false
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
</script>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
import { components } from '@feedmepos/ui-library';
|
|
4
|
+
|
|
5
|
+
interface SelectItem {
|
|
6
|
+
label: string
|
|
7
|
+
value: string
|
|
8
|
+
icon?: 'radio' | 'checkbox'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
modelValue: string
|
|
13
|
+
items: SelectItem[]
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
const emit = defineEmits<{
|
|
17
|
+
(e: 'update:modelValue', value: string): void
|
|
18
|
+
}>()
|
|
19
|
+
|
|
20
|
+
const isOpen = ref(false)
|
|
21
|
+
const selectRef = ref<HTMLDivElement | null>(null)
|
|
22
|
+
const menuRef = ref<typeof components['FmMenu'] | null>(null);
|
|
23
|
+
|
|
24
|
+
const selectedItem = computed(() => {
|
|
25
|
+
const found = props.items.find((item) => item.value === props.modelValue)
|
|
26
|
+
return found || props.items[0] || { label: 'Select...', value: '', icon: undefined }
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
function selectItem(item: SelectItem) {
|
|
30
|
+
emit('update:modelValue', item.value)
|
|
31
|
+
menuRef.value?.toggleMenu();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Close dropdown when clicking outside
|
|
35
|
+
function handleClickOutside(event: MouseEvent) {
|
|
36
|
+
if (selectRef.value && !selectRef.value.contains(event.target as Node)) {
|
|
37
|
+
menuRef.value?.toggleMenu();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Setup click outside listener when component mounts/unmounts
|
|
42
|
+
import { onMounted, onUnmounted } from 'vue'
|
|
43
|
+
|
|
44
|
+
onMounted(() => {
|
|
45
|
+
document.addEventListener('click', handleClickOutside)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
onUnmounted(() => {
|
|
49
|
+
document.removeEventListener('click', handleClickOutside)
|
|
50
|
+
})
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<div class="relative w-full" ref="selectRef">
|
|
55
|
+
<!-- Use FmMenu for dropdown behavior -->
|
|
56
|
+
<FmMenu ref="menuRef" @menu-changed="(v) => isOpen = v">
|
|
57
|
+
<template #menu-button>
|
|
58
|
+
<FmField>
|
|
59
|
+
<div class="flex items-center gap-4 w-full">
|
|
60
|
+
<FmCheckbox v-if="selectedItem.icon == 'checkbox'" :value="true" :model-value="true"
|
|
61
|
+
@update:model-value="(v) => v" />
|
|
62
|
+
<div v-if="selectedItem.icon == 'radio'" class="flex items-center">
|
|
63
|
+
<FmRadio :value="true" :model-value="true" @update:model-value="(v) => v" />
|
|
64
|
+
</div>
|
|
65
|
+
<!-- Label -->
|
|
66
|
+
<span class="text-gray-900 flex-1">
|
|
67
|
+
{{ selectedItem.label }}
|
|
68
|
+
</span>
|
|
69
|
+
<FmIcon name="keyboard_arrow_down" class="text-gray-500 transition-transform duration-200 ml-2"
|
|
70
|
+
:class="{ 'rotate-180': isOpen }" />
|
|
71
|
+
</div>
|
|
72
|
+
</FmField>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<!-- Dropdown Menu Items -->
|
|
76
|
+
<template #default>
|
|
77
|
+
<div :style="{ width: `${selectRef?.clientWidth}px` }" class="rounded-md flex flex-col gap-4">
|
|
78
|
+
<div v-for="item in items" :key="item.value" @click="selectItem(item)"
|
|
79
|
+
class="cursor-pointer transition-colors duration-150 flex items-center p-8 rounded-sm hover:bg-orange-100">
|
|
80
|
+
<FmCheckbox v-if="item.icon == 'checkbox'" :value="true" :model-value="true" @update:model-value="(v) => v"
|
|
81
|
+
class="mr-8" />
|
|
82
|
+
<div v-if="item.icon == 'radio'" class="flex items-center">
|
|
83
|
+
<FmRadio :value="true" :model-value="true" @update:model-value="(v) => v" />
|
|
84
|
+
</div>
|
|
85
|
+
<span class="text-gray-900 flex-1"> {{ item.label }} </span>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</template>
|
|
89
|
+
</FmMenu>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|
|
92
|
+
|
|
93
|
+
<style scoped>
|
|
94
|
+
/* Match FmTextField styling */
|
|
95
|
+
.fm-text-field-input {
|
|
96
|
+
font-size: 1.125rem;
|
|
97
|
+
line-height: 1.75rem;
|
|
98
|
+
}
|
|
99
|
+
</style>
|