@feedmepos/mf-order-setting 0.0.50 → 0.0.52-dev.0
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-u14hzPbE.js → KioskDevicesView-Vy9FLX1n.js} +1 -1
- package/dist/{KioskDevicesView.vue_vue_type_script_setup_true_lang-DBgRDIoS.js → KioskDevicesView.vue_vue_type_script_setup_true_lang-DhZPOEEQ.js} +2 -2
- package/dist/{KioskSettingView-DmvtZcV1.js → KioskSettingView-cE-JdCBB.js} +206 -208
- package/dist/{KioskView-M8V91gD5.js → KioskView-BYs5bem0.js} +4 -4
- package/dist/OrderSettingsView-C4aEpC1j.js +56063 -0
- package/dist/{app-CLewMjcd.js → app-CwYXsqxX.js} +184 -20
- package/dist/app.js +1 -1
- package/dist/{dayjs.min-DCTYRWyD.js → dayjs.min-JEYIJz2D.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/restaurant/index.d.ts +3 -3
- package/dist/frontend/mf-order/src/views/all-orders/ReflowOrder.vue.d.ts +2 -2
- package/dist/frontend/mf-order/src/views/order-settings/delivery/inhouse/InHouseDelivery.vue.d.ts +2 -2
- 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 +10 -0
- package/dist/frontend/mf-order/src/views/order-settings/reservation/ReservationSetting.vue.d.ts +2 -0
- package/dist/{index-B7LtJeBJ.js → index-DZCjODMx.js} +2 -2
- package/dist/{menu.dto-Co7iXHNr.js → menu.dto-D9CDVLiP.js} +22865 -20028
- package/dist/package/entity/food-court/order.do.d.ts +47 -2
- package/dist/package/entity/food-court/order.dto.d.ts +0 -3
- package/dist/package/entity/incoming-order/incoming-order-to-bill.dto.d.ts +12356 -1
- package/dist/package/entity/incoming-order/incoming-order.do.d.ts +3 -22266
- package/dist/package/entity/incoming-order/incoming-order.dto.d.ts +18 -0
- package/dist/package/entity/index.d.ts +5 -0
- package/dist/package/entity/kiosk/marketing/marketing.dto.d.ts +1 -19864
- package/dist/package/entity/order/order-item/order-item.dto.d.ts +24 -3714
- package/dist/package/entity/order/order.do.d.ts +8 -0
- package/dist/package/entity/order/order.dto.d.ts +118 -0
- package/dist/package/entity/order-platform/external/menu/external-master-menu.do.d.ts +20 -0
- package/dist/package/entity/order-platform/external/menu/external-menu.do.d.ts +23 -0
- package/dist/package/entity/order-platform/menu.dto.d.ts +34 -0
- package/dist/package/entity/order-setting/order-setting.do.d.ts +861 -0
- package/dist/package/entity/order-setting/reservationV2/reservation.do.d.ts +1269 -0
- package/dist/package/entity/queue/queue.do.d.ts +1 -11
- package/dist/package/entity/queue/queue.dto.d.ts +25 -0
- package/dist/package/entity/reservation/reservation.do.d.ts +101 -0
- package/dist/package/entity/reservation/reservation.dto.d.ts +325 -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 +3 -3
- 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/main.ts +7 -5
- package/src/stores/order-setting/mapper.ts +50 -50
- package/src/views/kiosk/settings/KioskPaymentTypeSection.vue +1 -19
- package/src/views/order-settings/OrderSettingsView.vue +7 -2
- package/src/views/order-settings/delivery/integrated-delivery/IntegratedDelivery.vue +3 -1
- package/src/views/order-settings/drive-thru/DriveThruSetting.vue +13 -28
- package/src/views/order-settings/reservation/CopySettingsSheet.vue +238 -0
- package/src/views/order-settings/reservation/CustomSelect.vue +99 -0
- package/src/views/order-settings/reservation/CustomTimePicker.vue +201 -0
- package/src/views/order-settings/reservation/ReservationSetting.vue +1246 -0
- package/src/views/order-settings/servicecharge/ServiceChargeRule.vue +5 -1
- package/tsconfig.app.json +8 -6
- package/dist/OrderSettingsView-Bl3LshG3.js +0 -51603
- package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +0 -1
|
@@ -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>
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
modelValue: string
|
|
6
|
+
minTime?: string // Optional minimum time (HH:MM format)
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
const emit = defineEmits<{
|
|
10
|
+
(e: 'update:modelValue', value: string): void
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const isOpen = ref(false)
|
|
14
|
+
const inputRef = ref<any>(null)
|
|
15
|
+
const dropdownRef = ref<HTMLDivElement | null>(null)
|
|
16
|
+
|
|
17
|
+
// Generate time options in 30-minute intervals
|
|
18
|
+
const timeOptions = computed(() => {
|
|
19
|
+
const options: string[] = []
|
|
20
|
+
for (let hour = 0; hour < 24; hour++) {
|
|
21
|
+
for (let minute = 0; minute < 60; minute += 30) {
|
|
22
|
+
const timeString = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`
|
|
23
|
+
options.push(timeString)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
options.push('23:59')
|
|
27
|
+
|
|
28
|
+
// Filter out times earlier than minTime if provided
|
|
29
|
+
if (props.minTime) {
|
|
30
|
+
return options.filter((time) => isTimeGreaterOrEqual(time, props.minTime!))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return options
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// Helper function to compare times in HH:MM format
|
|
37
|
+
function isTimeGreaterOrEqual(time1: string, time2: string): boolean {
|
|
38
|
+
const [hours1, minutes1] = time1.split(':').map(Number)
|
|
39
|
+
const [hours2, minutes2] = time2.split(':').map(Number)
|
|
40
|
+
|
|
41
|
+
const totalMinutes1 = hours1 * 60 + minutes1
|
|
42
|
+
const totalMinutes2 = hours2 * 60 + minutes2
|
|
43
|
+
|
|
44
|
+
return totalMinutes1 >= totalMinutes2
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const displayValue = computed({
|
|
48
|
+
get: () => props.modelValue || '',
|
|
49
|
+
set: (value: string) => {
|
|
50
|
+
emit('update:modelValue', value)
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
function selectTime(time: string) {
|
|
55
|
+
displayValue.value = time
|
|
56
|
+
isOpen.value = false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function handleClickOutside(event: MouseEvent) {
|
|
60
|
+
const target = event.target as Node
|
|
61
|
+
const wrapperElement = inputRef.value?.$el || inputRef.value
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
dropdownRef.value &&
|
|
65
|
+
wrapperElement &&
|
|
66
|
+
!dropdownRef.value.contains(target) &&
|
|
67
|
+
!wrapperElement.contains(target)
|
|
68
|
+
) {
|
|
69
|
+
isOpen.value = false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function handleFocus() {
|
|
74
|
+
isOpen.value = true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function toggleDropdown(event?: Event) {
|
|
78
|
+
event?.stopPropagation()
|
|
79
|
+
isOpen.value = !isOpen.value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function handleInputValue(value: string) {
|
|
83
|
+
let formattedValue = value.replace(/[^\d:]/g, '')
|
|
84
|
+
|
|
85
|
+
// Auto-format as user types
|
|
86
|
+
if (formattedValue.length === 2 && !formattedValue.includes(':')) {
|
|
87
|
+
formattedValue = formattedValue + ':'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Limit to HH:MM format
|
|
91
|
+
if (formattedValue.length > 5) {
|
|
92
|
+
formattedValue = formattedValue.slice(0, 5)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
displayValue.value = formattedValue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onMounted(() => {
|
|
99
|
+
document.addEventListener('click', handleClickOutside)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
onUnmounted(() => {
|
|
103
|
+
document.removeEventListener('click', handleClickOutside)
|
|
104
|
+
})
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<template>
|
|
108
|
+
<div class="relative w-full">
|
|
109
|
+
<div @click="toggleDropdown" class="cursor-pointer relative">
|
|
110
|
+
<FmTextField
|
|
111
|
+
ref="inputRef"
|
|
112
|
+
:model-value="displayValue"
|
|
113
|
+
@update:model-value="handleInputValue"
|
|
114
|
+
@focus="handleFocus"
|
|
115
|
+
placeholder="HH:MM"
|
|
116
|
+
class="time-picker-field"
|
|
117
|
+
:class="{
|
|
118
|
+
focused: isOpen
|
|
119
|
+
}"
|
|
120
|
+
maxlength="5"
|
|
121
|
+
>
|
|
122
|
+
</FmTextField>
|
|
123
|
+
<FmIcon
|
|
124
|
+
class="absolute right-1 transition-all duration-200"
|
|
125
|
+
name="chevron_right"
|
|
126
|
+
:class="{ 'rotate-[-90deg] top-[4px]': isOpen, 'rotate-[90deg] top-[5px]': !isOpen }"
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div
|
|
131
|
+
v-if="isOpen"
|
|
132
|
+
ref="dropdownRef"
|
|
133
|
+
class="absolute z-50 w-full mt-2 bg-white border border-gray-200 rounded-md shadow-lg max-h-[240px] overflow-hidden"
|
|
134
|
+
>
|
|
135
|
+
<div class="max-h-[240px] overflow-y-auto scrollbar-thin">
|
|
136
|
+
<div
|
|
137
|
+
v-for="time in timeOptions"
|
|
138
|
+
:key="time"
|
|
139
|
+
@click.stop="selectTime(time)"
|
|
140
|
+
class="px-3 py-2 text-sm cursor-pointer transition-colors hover:bg-gray-50"
|
|
141
|
+
:class="{
|
|
142
|
+
'bg-orange-50 text-orange-600 font-medium': time === displayValue
|
|
143
|
+
}"
|
|
144
|
+
>
|
|
145
|
+
{{ time }}
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</template>
|
|
151
|
+
|
|
152
|
+
<style scoped>
|
|
153
|
+
/* Custom scrollbar for dropdown - target the scrollable container */
|
|
154
|
+
.scrollbar-thin::-webkit-scrollbar {
|
|
155
|
+
width: 6px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.scrollbar-thin::-webkit-scrollbar-track {
|
|
159
|
+
background: transparent;
|
|
160
|
+
border-radius: 3px;
|
|
161
|
+
margin: 4px 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.scrollbar-thin::-webkit-scrollbar-thumb {
|
|
165
|
+
background: #d1d5db;
|
|
166
|
+
border-radius: 3px;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
|
170
|
+
background: #9ca3af;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Override FmTextField styling for orange theme */
|
|
174
|
+
.time-picker-field :deep(.fm-text-field-input) {
|
|
175
|
+
border-width: 2px;
|
|
176
|
+
border-radius: 0.75rem;
|
|
177
|
+
padding: 12px 16px;
|
|
178
|
+
font-size: 1.125rem;
|
|
179
|
+
transition: all 0.2s;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.time-picker-field :deep(.fm-text-field-input):not(:focus):not(:focus-within) {
|
|
183
|
+
border-color: rgb(209 213 219);
|
|
184
|
+
/* gray-300 */
|
|
185
|
+
background-color: white;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.time-picker-field :deep(.fm-text-field-input):not(:focus):not(:focus-within):hover {
|
|
189
|
+
border-color: rgb(156 163 175);
|
|
190
|
+
/* gray-400 */
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.time-picker-field.focused :deep(.fm-text-field-input),
|
|
194
|
+
.time-picker-field :deep(.fm-text-field-input):focus,
|
|
195
|
+
.time-picker-field :deep(.fm-text-field-input):focus-within {
|
|
196
|
+
border-color: rgb(249 115 22) !important;
|
|
197
|
+
/* orange-500 */
|
|
198
|
+
background-color: rgb(255 247 237) !important;
|
|
199
|
+
/* orange-50 */
|
|
200
|
+
}
|
|
201
|
+
</style>
|