@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.
Files changed (82) hide show
  1. package/.tsbuildinfo +1 -0
  2. package/dist/{KioskDevicesView-CMKNjgWx.js → KioskDevicesView-Qv-xd_kZ.js} +1 -1
  3. package/dist/{KioskDevicesView.vue_vue_type_script_setup_true_lang-B1sNvlUC.js → KioskDevicesView.vue_vue_type_script_setup_true_lang-CCF1mKni.js} +2 -2
  4. package/dist/KioskSettingView-CvvrK6Bv.js +643 -0
  5. package/dist/{KioskView-U-Wg8oMC.js → KioskView-CppTVBv-.js} +117 -117
  6. package/dist/OrderSettingsView-C38N61dM.js +36564 -0
  7. package/dist/{app-CFfgPAd8.js → app-Bss1GkKY.js} +392 -228
  8. package/dist/app.js +1 -1
  9. package/dist/{dayjs.min-CuRr-wlf.js → dayjs.min-DZfxGUk4.js} +1 -1
  10. package/dist/frontend/mf-order/src/api/reservation/index.d.ts +8 -0
  11. package/dist/frontend/mf-order/src/app.d.ts +164 -0
  12. package/dist/frontend/mf-order/src/main.d.ts +164 -0
  13. package/dist/frontend/mf-order/src/stores/order-setting/index.d.ts +3 -0
  14. package/dist/frontend/mf-order/src/stores/restaurant/index.d.ts +1 -1
  15. package/dist/frontend/mf-order/src/views/kiosk/settings/KioskPaymentTypeSection.vue.d.ts +13 -3
  16. package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/ExternalSetting.vue.d.ts +12 -4
  17. package/dist/frontend/mf-order/src/views/order-settings/dine-in/OfflinePaymentTypeDialog.vue.d.ts +4 -4
  18. package/dist/frontend/mf-order/src/views/order-settings/dine-in/PaymentType.vue.d.ts +38 -4
  19. package/dist/frontend/mf-order/src/views/order-settings/pickup/PaymentSidesheet.vue.d.ts +1 -0
  20. package/dist/frontend/mf-order/src/views/order-settings/reservation/CopySettingsSheet.vue.d.ts +186 -0
  21. package/dist/frontend/mf-order/src/views/order-settings/reservation/CustomSelect.vue.d.ts +15 -0
  22. package/dist/frontend/mf-order/src/views/order-settings/reservation/CustomTimePicker.vue.d.ts +11 -0
  23. package/dist/frontend/mf-order/src/views/order-settings/reservation/ReservationSetting.vue.d.ts +2 -0
  24. package/dist/{index-Bj0bCGTm.js → index-B6AGCsrw.js} +3 -3
  25. package/dist/index-BpKR-Cxd.js +19757 -0
  26. package/dist/{menu.dto-DAh1J2ES.js → menu.dto-C_B3M2fs.js} +7390 -7134
  27. package/dist/package/entity/incoming-order/incoming-order.do.d.ts +22443 -3
  28. package/dist/package/entity/incoming-order/incoming-order.dto.d.ts +3 -3
  29. package/dist/package/entity/incoming-order/incoming-order.enum.d.ts +1 -1
  30. package/dist/package/entity/index.d.ts +6 -0
  31. package/dist/package/entity/marketing/marketing.dto.d.ts +1 -1
  32. package/dist/package/entity/order/dine-in/qr.dto.d.ts +38 -0
  33. package/dist/package/entity/order/order.do.d.ts +6358 -2
  34. package/dist/package/entity/order/order.dto.d.ts +22 -0
  35. package/dist/package/entity/order-platform/deliveroo/deliveroo-dto.d.ts +3 -0
  36. package/dist/package/entity/order-platform/deliveroo/deliveroo-setting.do.d.ts +3 -0
  37. package/dist/package/entity/order-platform/external/order/external-order.do.d.ts +12 -12
  38. package/dist/package/entity/order-platform/external/order/external-order.dto.d.ts +32 -32
  39. package/dist/package/entity/order-platform/external/setting/external-setting.do.d.ts +21 -3
  40. package/dist/package/entity/order-platform/external/setting/external-setting.dto.d.ts +12 -2
  41. package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.do.d.ts +3 -0
  42. package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.dto.d.ts +3 -0
  43. package/dist/package/entity/order-platform/grabfood/grabfood-edit-order.do.d.ts +9 -1
  44. package/dist/package/entity/order-platform/grabfood/grabfood-settings.do.d.ts +2 -2
  45. package/dist/package/entity/order-platform/grabfood/grabfood.dto.d.ts +3 -3
  46. package/dist/package/entity/order-platform/order-platform.dto.d.ts +2 -2
  47. package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.do.d.ts +3 -0
  48. package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.dto.d.ts +3 -0
  49. package/dist/package/entity/order-setting/order-setting.do.d.ts +864 -0
  50. package/dist/package/entity/order-setting/order-setting.dto.d.ts +6 -0
  51. package/dist/package/entity/order-setting/reservationV2/reservation.do.d.ts +1269 -0
  52. package/dist/package/entity/queue/queue.do.d.ts +3 -8
  53. package/dist/package/entity/queue/queue.dto.d.ts +10 -0
  54. package/dist/package/entity/reservation/reservation.do.d.ts +105 -0
  55. package/dist/package/entity/reservation/reservation.dto.d.ts +335 -0
  56. package/dist/package/entity/reservation/reservation.enum.d.ts +3 -0
  57. package/dist/package/entity/reservation/reservation.utils.d.ts +152 -0
  58. package/dist/style.css +1 -0
  59. package/package.json +1 -1
  60. package/src/api/reservation/index.ts +28 -0
  61. package/src/assets/images/not-found.png +0 -0
  62. package/src/locales/en-US.json +56 -0
  63. package/src/locales/th-TH.json +54 -0
  64. package/src/locales/zh-CN.json +54 -0
  65. package/src/views/kiosk/KioskSummary.vue +3 -0
  66. package/src/views/kiosk/settings/KioskPaymentTypeSection.vue +99 -211
  67. package/src/views/kiosk/settings/KioskSettingView.vue +43 -25
  68. package/src/views/order-settings/OrderSettingsView.vue +6 -1
  69. package/src/views/order-settings/dine-in/DineInSetting.vue +1 -0
  70. package/src/views/order-settings/dine-in/OfflinePaymentTypeDialog.vue +2 -3
  71. package/src/views/order-settings/dine-in/PaymentType.vue +151 -43
  72. package/src/views/order-settings/pickup/PaymentSidesheet.vue +33 -172
  73. package/src/views/order-settings/pickup/PickUpSettingDialogContent.vue +1 -0
  74. package/src/views/order-settings/reservation/CopySettingsSheet.vue +256 -0
  75. package/src/views/order-settings/reservation/CustomSelect.vue +99 -0
  76. package/src/views/order-settings/reservation/CustomTimePicker.vue +311 -0
  77. package/src/views/order-settings/reservation/ReservationSetting.vue +1555 -0
  78. package/tsconfig.app.json +8 -6
  79. package/dist/KioskSettingView-BE_pMA-i.js +0 -720
  80. package/dist/OrderSettingsView-BWzaITT6.js +0 -51916
  81. package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +0 -1
  82. 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, onMounted, ref } from 'vue'
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 parent = computed<boolean>(() => {
95
- return (
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
- return paymentTypes.includes(F_ORDER_PAYMENT_TYPE.enum.cash) || offlinePaymentTypes.length > 0
139
- })
140
-
141
- onMounted(() => {
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 (allowEPayment.value) {
151
- initialValues.push(F_ORDER_PAYMENT_TYPE.enum.ePayment)
37
+ if (!allowCreditBool.value) {
38
+ suffixes['credit'] = typeof props.allowCredit === 'string' ? props.allowCredit : t('order.notAllowed')
152
39
  }
153
- nestedVal.value = initialValues
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 updateOfflinePaymentTypes(v: FdoRestaurantPickup['offlinePaymentTypes']) {
190
- localOfflinePaymentTypes.value = v || []
43
+ function updatePayments(
44
+ paymentTypes: F_ORDER_PAYMENT_TYPE[],
45
+ offlinePaymentTypes: FdoOfflinePaymentMethod[] | null | undefined
46
+ ) {
191
47
  emits('update:payments', {
192
- paymentTypes: props.paymentTypes??[],
193
- offlinePaymentTypes: localOfflinePaymentTypes.value
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>
@@ -16,6 +16,7 @@
16
16
  <PaymentSidesheet
17
17
  :offline-payment-types="pickupSetting.offlinePaymentTypes"
18
18
  :allow-e-payment="restaurantStore.getRestaurantEPayment()"
19
+ :allow-credit="true"
19
20
  :payment-types="pickupSetting.paymentTypes"
20
21
  @update:payments="updatePayments"
21
22
  />
@@ -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>