@feedmepos/mf-order-setting 0.0.76 → 0.0.77
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-H7stMDFu.js → KioskDevicesView-Dgs5_UoK.js} +1 -1
- package/dist/{KioskDevicesView.vue_vue_type_script_setup_true_lang-DgpV4oKE.js → KioskDevicesView.vue_vue_type_script_setup_true_lang-B4DXPgAV.js} +3 -3
- package/dist/KioskSettingView-BUcPNVTc.js +681 -0
- package/dist/{KioskView-Cxcc1UL1.js → KioskView-DelVMMj4.js} +137 -127
- package/dist/{OrderSettingsView-Ds2L_rbu.js → OrderSettingsView-DE2U7vqW.js} +9232 -8262
- package/dist/{app-BDlPjU4y.js → app-BIvt4NBq.js} +1476 -1302
- package/dist/app.js +1 -1
- package/dist/{dayjs.min-OJf1trOq.js → dayjs.min-B58_jBg3.js} +1 -1
- package/dist/frontend/mf-order/src/api/index.d.ts +3 -1
- package/dist/frontend/mf-order/src/api/remoteOrder/index.d.ts +65 -2
- package/dist/frontend/mf-order/src/app.d.ts +174 -0
- package/dist/frontend/mf-order/src/composables/useInternalUser.d.ts +3 -0
- package/dist/frontend/mf-order/src/main.d.ts +174 -0
- package/dist/frontend/mf-order/src/modules/order-setting/kiosk/interface.d.ts +4 -0
- package/dist/frontend/mf-order/src/stores/order-setting/index.d.ts +15 -3
- package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/AdditionalStores.vue.d.ts +16 -0
- package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/ExternalSetting.vue.d.ts +2 -2
- package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/FoodpandaAdditionalStores.vue.d.ts +10 -0
- package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/IntegratedDelivery.vue.d.ts +15 -4
- package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/IntegrationIdentityDetails.vue.d.ts +10 -0
- package/dist/frontend/mf-order/src/views/order-settings/delivery/integrated-delivery/ShopeefoodAdditionalStores.vue.d.ts +10 -0
- package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +1 -1
- package/dist/index-CLS-FSH-.js +762 -0
- package/dist/{index-gGOpbnY3.js → index-CahsmFL1.js} +1 -1
- package/dist/{lineman-settings.dto-Cny_fF4n.js → lineman-settings.dto-DBDThIb0.js} +8999 -8874
- package/dist/package/entity/delivery/delivery.enum.d.ts +1 -1
- package/dist/package/entity/incoming-order/incoming-order.do.d.ts +4 -4
- package/dist/package/entity/kiosk/marketing/marketing.dto.d.ts +11 -0
- package/dist/package/entity/order-platform/base-integration.dto.d.ts +179 -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.dto.d.ts +2 -2
- package/dist/package/entity/order-platform/external/setting/external-setting.do.d.ts +4 -4
- package/dist/package/entity/order-platform/external/setting/external-setting.dto.d.ts +4 -4
- package/dist/package/entity/order-platform/foodpanda/foodpanda-menu.dto.d.ts +1 -1
- package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.do.d.ts +105 -4
- package/dist/package/entity/order-platform/foodpanda/foodpanda-settings.dto.d.ts +30 -11
- package/dist/package/entity/order-platform/gofood/gofood-settings.do.d.ts +2 -2
- package/dist/package/entity/order-platform/gofood/gofood-settings.dto.d.ts +2 -2
- package/dist/package/entity/order-platform/grabfood/grabfood-order.do.d.ts +10 -10
- package/dist/package/entity/order-platform/grabfood/grabfood-settings.do.d.ts +134 -6
- package/dist/package/entity/order-platform/grabfood/grabfood.dto.d.ts +42 -20
- package/dist/package/entity/order-platform/lineman/lineman-settings.do.d.ts +2 -2
- package/dist/package/entity/order-platform/lineman/lineman-settings.dto.d.ts +2 -2
- package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.do.d.ts +102 -4
- package/dist/package/entity/order-platform/shopeefood/shopeefood-settings.dto.d.ts +66 -5
- package/dist/package/entity/order-platform/tng/tng-settings.do.d.ts +2 -2
- package/dist/package/entity/order-platform/tng/tng-settings.dto.d.ts +4 -4
- package/dist/package/entity/order-setting/dine-in/dine-in.do.d.ts +2 -2
- package/dist/package/entity/order-setting/dine-in/dine-in.dto.d.ts +2 -2
- package/dist/package/entity/order-setting/kiosk/kiosk.do.d.ts +6 -0
- package/dist/package/entity/order-setting/kiosk/kiosk.dto.d.ts +6 -0
- package/dist/package/entity/order-setting/order-setting.do.d.ts +14 -4
- package/dist/package/entity/order-setting/order-setting.dto.d.ts +28 -8
- package/dist/package/entity/restaurant/restaurant.dto.d.ts +10 -0
- package/dist/package/entity/websocket/websocket.dto.d.ts +24 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/api/index.ts +8 -2
- package/src/api/remoteOrder/index.ts +200 -4
- package/src/composables/useInternalUser.ts +14 -0
- package/src/locales/en-US.json +43 -0
- package/src/locales/ja-JP.json +43 -0
- package/src/locales/th-TH.json +90 -45
- package/src/locales/zh-CN.json +43 -0
- package/src/modules/order-setting/kiosk/interface.ts +29 -19
- package/src/stores/order-setting/mapper.ts +65 -55
- package/src/views/kiosk/settings/KioskSettingView.vue +33 -0
- package/src/views/order-settings/delivery/DeliverySetting.vue +0 -1
- package/src/views/order-settings/delivery/delivery.data.ts +3 -0
- package/src/views/order-settings/delivery/integrated-delivery/AdditionalStores.vue +472 -0
- package/src/views/order-settings/delivery/integrated-delivery/FoodpandaAdditionalStores.vue +314 -0
- package/src/views/order-settings/delivery/integrated-delivery/FoodpandaSetting.vue +27 -8
- package/src/views/order-settings/delivery/integrated-delivery/GrabfoodSetting.vue +31 -12
- package/src/views/order-settings/delivery/integrated-delivery/IntegratedDelivery.vue +23 -11
- package/src/views/order-settings/delivery/integrated-delivery/IntegrationIdentityDetails.vue +144 -0
- package/src/views/order-settings/delivery/integrated-delivery/ShopeefoodAdditionalStores.vue +335 -0
- package/src/views/order-settings/delivery/integrated-delivery/ShopeefoodSetting.vue +29 -8
- package/src/views/order-settings/queue/QueueSetting.vue +1 -1
- package/dist/KioskSettingView-fF6k9s_q.js +0 -658
- package/dist/index-Bid9lO2x.js +0 -665
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
|
3
|
+
import Day from 'dayjs'
|
|
4
|
+
import { useI18n } from '@feedmepos/mf-common'
|
|
5
|
+
import { useDialog } from '@feedmepos/ui-library'
|
|
6
|
+
import { remoteOrderApi, type FoodpandaStoreView } from '@/api/remoteOrder'
|
|
7
|
+
import { useSnackbarFunctions } from '@/components/snackbar'
|
|
8
|
+
import { useLoading } from '@/composables/loading'
|
|
9
|
+
import { useMenuStore } from '@/stores/menu/menu'
|
|
10
|
+
import IntegrationIdentityDetails from './IntegrationIdentityDetails.vue'
|
|
11
|
+
import { useInternalUser } from '@/composables/useInternalUser'
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{ restaurantId: string; primaryEnabled: boolean }>()
|
|
14
|
+
const emits = defineEmits<{ (event: 'syncMenu', isPrimary: false): void }>()
|
|
15
|
+
const { t } = useI18n()
|
|
16
|
+
const Dialog = useDialog()
|
|
17
|
+
const menuStore = useMenuStore()
|
|
18
|
+
const { startAsyncCallWithErr } = useLoading()
|
|
19
|
+
const { showError, showSuccess } = useSnackbarFunctions()
|
|
20
|
+
const { isInternal } = useInternalUser()
|
|
21
|
+
|
|
22
|
+
const stores = ref<FoodpandaStoreView[]>([])
|
|
23
|
+
const editing = ref<null | {
|
|
24
|
+
mode: 'new' | 'edit'
|
|
25
|
+
integrationId: string | null
|
|
26
|
+
routeId: string
|
|
27
|
+
remoteId: string
|
|
28
|
+
}>(null)
|
|
29
|
+
const isSaving = ref(false)
|
|
30
|
+
const form = reactive<{ catalogId: string | null; enable: boolean }>({
|
|
31
|
+
catalogId: null,
|
|
32
|
+
enable: true
|
|
33
|
+
})
|
|
34
|
+
const originalForm = reactive<{ catalogId: string | null; enable: boolean }>({
|
|
35
|
+
catalogId: null,
|
|
36
|
+
enable: true
|
|
37
|
+
})
|
|
38
|
+
const catalogOptions = computed(() => menuStore.catalogOptions ?? [])
|
|
39
|
+
const additionalStores = computed(() => stores.value.filter((store) => !store.isPrimary))
|
|
40
|
+
const showSyncAction = computed(
|
|
41
|
+
() => !props.primaryEnabled && additionalStores.value.some((store) => store.enable === true)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
function catalogLabel(catalogId?: string | null) {
|
|
45
|
+
if (!catalogId) return '—'
|
|
46
|
+
return catalogOptions.value.find((catalog) => catalog.value === catalogId)?.label ?? catalogId
|
|
47
|
+
}
|
|
48
|
+
function formatDate(value?: string | null) {
|
|
49
|
+
return value ? Day(value).format('DD/MM/YYYY hh:mm a') : '—'
|
|
50
|
+
}
|
|
51
|
+
async function load() {
|
|
52
|
+
if (!props.restaurantId) return
|
|
53
|
+
await startAsyncCallWithErr(async () => {
|
|
54
|
+
stores.value = await remoteOrderApi.integratedDelivery.listFoodpandaStores(props.restaurantId)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
onMounted(load)
|
|
58
|
+
watch(() => props.restaurantId, load)
|
|
59
|
+
|
|
60
|
+
function openAdd() {
|
|
61
|
+
editing.value = { mode: 'new', integrationId: null, routeId: '', remoteId: '' }
|
|
62
|
+
form.catalogId = null
|
|
63
|
+
form.enable = true
|
|
64
|
+
}
|
|
65
|
+
function openEdit(store: FoodpandaStoreView) {
|
|
66
|
+
editing.value = {
|
|
67
|
+
mode: 'edit',
|
|
68
|
+
integrationId: store.integrationId,
|
|
69
|
+
routeId: store.routeId,
|
|
70
|
+
remoteId: store.remoteId
|
|
71
|
+
}
|
|
72
|
+
form.catalogId = store.catalogId ?? null
|
|
73
|
+
form.enable = store.enable ?? false
|
|
74
|
+
originalForm.catalogId = form.catalogId
|
|
75
|
+
originalForm.enable = form.enable
|
|
76
|
+
}
|
|
77
|
+
function cancelEdit() {
|
|
78
|
+
editing.value = null
|
|
79
|
+
}
|
|
80
|
+
function onSheetToggle(open: boolean) {
|
|
81
|
+
if (!open) cancelEdit()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function save() {
|
|
85
|
+
if (isSaving.value) return
|
|
86
|
+
|
|
87
|
+
isSaving.value = true
|
|
88
|
+
try {
|
|
89
|
+
await startAsyncCallWithErr(async () => {
|
|
90
|
+
const payload = { catalogId: form.catalogId, enable: form.enable }
|
|
91
|
+
if (editing.value?.mode === 'new') {
|
|
92
|
+
await remoteOrderApi.integratedDelivery.addFoodpandaStore(props.restaurantId, payload)
|
|
93
|
+
showSuccess(t('order.storeAdded'))
|
|
94
|
+
} else if (editing.value) {
|
|
95
|
+
const updatePayload =
|
|
96
|
+
form.enable === false && form.catalogId === originalForm.catalogId
|
|
97
|
+
? { enable: false }
|
|
98
|
+
: payload
|
|
99
|
+
await remoteOrderApi.integratedDelivery.updateFoodpandaStore(
|
|
100
|
+
props.restaurantId,
|
|
101
|
+
editing.value.routeId,
|
|
102
|
+
updatePayload
|
|
103
|
+
)
|
|
104
|
+
showSuccess(t('order.storeUpdated'))
|
|
105
|
+
}
|
|
106
|
+
editing.value = null
|
|
107
|
+
await load()
|
|
108
|
+
})
|
|
109
|
+
} finally {
|
|
110
|
+
isSaving.value = false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function promptRemove(store: FoodpandaStoreView) {
|
|
115
|
+
if (store.enable) {
|
|
116
|
+
showError(t('order.disableBeforeRemove'))
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
Dialog.open({
|
|
120
|
+
title: t('order.removeStoreConfirm', { merchantID: store.remoteId }),
|
|
121
|
+
primaryActions: { text: t('order.removeStore'), variant: 'destructive', close: true },
|
|
122
|
+
secondaryActions: { text: t('order.cancel'), variant: 'tertiary', close: true }
|
|
123
|
+
}).onPrimary(async () => {
|
|
124
|
+
await startAsyncCallWithErr(async () => {
|
|
125
|
+
await remoteOrderApi.integratedDelivery.removeFoodpandaStore(
|
|
126
|
+
props.restaurantId,
|
|
127
|
+
store.routeId
|
|
128
|
+
)
|
|
129
|
+
showSuccess(t('order.storeRemoved'))
|
|
130
|
+
await load()
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
</script>
|
|
135
|
+
|
|
136
|
+
<template>
|
|
137
|
+
<div class="mt-10 store-section">
|
|
138
|
+
<div class="flex flex-row items-center mb-1">
|
|
139
|
+
<div class="flex-grow fm-typo-en-title-sm-600">{{ t('order.additionalStores') }}</div>
|
|
140
|
+
<div class="store-card__actions">
|
|
141
|
+
<FmButton
|
|
142
|
+
v-if="showSyncAction"
|
|
143
|
+
variant="primary"
|
|
144
|
+
:label="t('order.syncMenu')"
|
|
145
|
+
@click="emits('syncMenu', false)"
|
|
146
|
+
/>
|
|
147
|
+
<FmButton variant="secondary" :label="t('order.addStore')" @click="openAdd" />
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="text-fm-color-neutral-gray-400 fm-typo-cn-body-md-400 mb-3">
|
|
151
|
+
{{ t('order.foodpandaStoresHint') }}
|
|
152
|
+
</div>
|
|
153
|
+
<div
|
|
154
|
+
v-if="additionalStores.length"
|
|
155
|
+
class="text-fm-color-neutral-gray-400 fm-typo-cn-body-md-400 mb-3"
|
|
156
|
+
>
|
|
157
|
+
{{ t('order.syncAllStoresHint') }}
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<FmCard
|
|
161
|
+
v-for="store in additionalStores"
|
|
162
|
+
:key="store.routeId"
|
|
163
|
+
variant="outlined"
|
|
164
|
+
class="store-card"
|
|
165
|
+
>
|
|
166
|
+
<div class="store-card__top">
|
|
167
|
+
<div class="store-card__title fm-typo-en-title-sm-600">{{ store.remoteId }}</div>
|
|
168
|
+
<div class="store-card__actions">
|
|
169
|
+
<FmButton variant="secondary" :label="t('order.edit')" @click="openEdit(store)" />
|
|
170
|
+
<FmButton
|
|
171
|
+
v-if="isInternal"
|
|
172
|
+
variant="secondary"
|
|
173
|
+
:label="t('order.removeStore')"
|
|
174
|
+
@click="promptRemove(store)"
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
<div
|
|
179
|
+
v-if="isInternal && store.enable"
|
|
180
|
+
class="text-fm-color-neutral-gray-400 fm-typo-cn-body-md-400 mb-3"
|
|
181
|
+
>
|
|
182
|
+
{{ t('order.disableBeforeRemove') }}
|
|
183
|
+
</div>
|
|
184
|
+
<div class="store-card__fields fm-typo-cn-body-md-400">
|
|
185
|
+
<div class="store-field">
|
|
186
|
+
<span>{{ t('order.enabled') }}</span
|
|
187
|
+
><span>{{ store.enable ? '✓' : '✕' }}</span>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="store-field">
|
|
190
|
+
<span>{{ t('order.catalog') }}</span
|
|
191
|
+
><span>{{ catalogLabel(store.catalogId) }}</span>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="store-field">
|
|
194
|
+
<span>{{ t('order.status') }}</span
|
|
195
|
+
><span>{{ store.status ?? '—' }}</span>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="store-field">
|
|
198
|
+
<span>{{ t('order.menuSyncError') }}</span
|
|
199
|
+
><span>{{ store.menuSyncError ?? '—' }}</span>
|
|
200
|
+
</div>
|
|
201
|
+
<div class="store-field">
|
|
202
|
+
<span>{{ t('order.lastSync') }}</span
|
|
203
|
+
><span>{{ formatDate(store.lastSynchronize) }}</span>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="store-field">
|
|
206
|
+
<span>{{ t('order.updatedAt') }}</span
|
|
207
|
+
><span>{{ formatDate(store.updatedAt) }}</span>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
<IntegrationIdentityDetails
|
|
211
|
+
:integration-id="store.integrationId"
|
|
212
|
+
:partner-id="store.partnerId ?? store.remoteId"
|
|
213
|
+
:partner-label="t('order.foodpandaStoreId')"
|
|
214
|
+
/>
|
|
215
|
+
</FmCard>
|
|
216
|
+
|
|
217
|
+
<FmSideSheet
|
|
218
|
+
:header="editing?.mode === 'new' ? t('order.addStore') : t('order.editStore')"
|
|
219
|
+
dismiss-away
|
|
220
|
+
class="w-full fm-typo-en-title-md-600"
|
|
221
|
+
:max-width="600"
|
|
222
|
+
:model-value="!!editing"
|
|
223
|
+
@update:model-value="onSheetToggle"
|
|
224
|
+
>
|
|
225
|
+
<div class="store-sheet-body">
|
|
226
|
+
<IntegrationIdentityDetails
|
|
227
|
+
v-if="editing?.mode === 'edit'"
|
|
228
|
+
:integration-id="editing.integrationId"
|
|
229
|
+
:partner-id="editing.remoteId"
|
|
230
|
+
:partner-label="t('order.foodpandaStoreId')"
|
|
231
|
+
/>
|
|
232
|
+
<div v-else class="text-fm-color-neutral-gray-400 fm-typo-cn-body-md-400">
|
|
233
|
+
{{ t('order.foodpandaStoreIdGeneratedHint') }}
|
|
234
|
+
</div>
|
|
235
|
+
<FmSelect
|
|
236
|
+
v-model="form.catalogId"
|
|
237
|
+
:label="t('order.catalog')"
|
|
238
|
+
:placeholder="t('order.selectCatalog')"
|
|
239
|
+
:items="catalogOptions"
|
|
240
|
+
/>
|
|
241
|
+
<FmSwitch v-model="form.enable" :label="t('order.enabled')" label-placement="right" />
|
|
242
|
+
</div>
|
|
243
|
+
<template #side-sheet-footer>
|
|
244
|
+
<div class="flex gap-8 items-center justify-start">
|
|
245
|
+
<FmButton
|
|
246
|
+
variant="primary"
|
|
247
|
+
size="lg"
|
|
248
|
+
:label="editing?.mode === 'new' ? t('order.addStore') : t('order.save')"
|
|
249
|
+
:loading="isSaving"
|
|
250
|
+
:disabled="isSaving"
|
|
251
|
+
@click="save"
|
|
252
|
+
/>
|
|
253
|
+
<FmButton variant="secondary" size="lg" :label="t('order.cancel')" @click="cancelEdit" />
|
|
254
|
+
</div>
|
|
255
|
+
</template>
|
|
256
|
+
</FmSideSheet>
|
|
257
|
+
</div>
|
|
258
|
+
</template>
|
|
259
|
+
|
|
260
|
+
<style scoped>
|
|
261
|
+
.store-section {
|
|
262
|
+
border-top: 1px solid #e5e7eb;
|
|
263
|
+
padding-top: 1.5rem;
|
|
264
|
+
}
|
|
265
|
+
.store-card {
|
|
266
|
+
padding: 1.25rem;
|
|
267
|
+
margin-bottom: 1rem;
|
|
268
|
+
}
|
|
269
|
+
.store-card__top {
|
|
270
|
+
display: flex;
|
|
271
|
+
align-items: flex-start;
|
|
272
|
+
justify-content: space-between;
|
|
273
|
+
gap: 1rem;
|
|
274
|
+
margin-bottom: 1rem;
|
|
275
|
+
}
|
|
276
|
+
.store-card__title {
|
|
277
|
+
min-width: 0;
|
|
278
|
+
word-break: break-all;
|
|
279
|
+
}
|
|
280
|
+
.store-card__actions {
|
|
281
|
+
display: flex;
|
|
282
|
+
flex-wrap: wrap;
|
|
283
|
+
justify-content: flex-end;
|
|
284
|
+
gap: 0.5rem;
|
|
285
|
+
flex-shrink: 0;
|
|
286
|
+
}
|
|
287
|
+
.store-card__fields {
|
|
288
|
+
display: flex;
|
|
289
|
+
flex-direction: column;
|
|
290
|
+
gap: 0.625rem;
|
|
291
|
+
}
|
|
292
|
+
.store-field {
|
|
293
|
+
display: grid;
|
|
294
|
+
grid-template-columns: 9rem minmax(0, 1fr);
|
|
295
|
+
gap: 1rem;
|
|
296
|
+
}
|
|
297
|
+
.store-field > span:first-child {
|
|
298
|
+
color: var(--fm-color-neutral-gray-400);
|
|
299
|
+
}
|
|
300
|
+
.store-field > span:last-child {
|
|
301
|
+
word-break: break-word;
|
|
302
|
+
}
|
|
303
|
+
.store-sheet-body {
|
|
304
|
+
display: flex;
|
|
305
|
+
flex-direction: column;
|
|
306
|
+
gap: 1rem;
|
|
307
|
+
padding-top: 0.5rem;
|
|
308
|
+
}
|
|
309
|
+
@media (max-width: 460px) {
|
|
310
|
+
.store-field {
|
|
311
|
+
grid-template-columns: 1fr;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
</style>
|
|
@@ -3,6 +3,8 @@ import { FdoLinkedDelivery } from '@feedmepos/core/entity'
|
|
|
3
3
|
import { FdoFoodpandaReportCalculationConfig, FdoFoodpandaSettings } from '@entity'
|
|
4
4
|
import { type PropType, reactive, computed } from 'vue'
|
|
5
5
|
import IntegratedDelivery from './IntegratedDelivery.vue'
|
|
6
|
+
import FoodpandaAdditionalStores from './FoodpandaAdditionalStores.vue'
|
|
7
|
+
import IntegrationIdentityDetails from './IntegrationIdentityDetails.vue'
|
|
6
8
|
import { useCoreStore } from '@feedmepos/mf-common'
|
|
7
9
|
import { clone } from '@/helpers/object'
|
|
8
10
|
import type { DeliveryCompany } from '../delivery'
|
|
@@ -68,10 +70,12 @@ function initReportConfig(): FdoFoodpandaReportCalculationConfig {
|
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
function updateData({ manual, auto }: IntegratedDeliverySettingData) {
|
|
73
|
+
const { integrationId: remoteId, ...autoSettings } = auto
|
|
71
74
|
data.manual = manual
|
|
72
75
|
data.integrated = {
|
|
73
|
-
...
|
|
74
|
-
|
|
76
|
+
...data.integrated,
|
|
77
|
+
...autoSettings,
|
|
78
|
+
remoteId,
|
|
75
79
|
reportConfig: {
|
|
76
80
|
...(data.integrated?.reportConfig || initReportConfig()),
|
|
77
81
|
commissionRate: auto.commissionRate
|
|
@@ -191,20 +195,20 @@ function formatCampaignDate(date: { start: string; end: string }) {
|
|
|
191
195
|
}
|
|
192
196
|
|
|
193
197
|
const { startAsyncCallWithErr } = useLoading()
|
|
194
|
-
async function syncMenu() {
|
|
198
|
+
async function syncMenu(isPrimary = true) {
|
|
195
199
|
const restaurantId = currentRestaurant.value?._id ?? ''
|
|
196
200
|
await startAsyncCallWithErr(async () => {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
lastSynchronize: res?.lastSynchronize || modelValue.value.auto.lastSynchronize
|
|
201
|
+
await remoteOrderApi.integratedDelivery.syncFoodpandaMenu(restaurantId)
|
|
202
|
+
if (isPrimary) {
|
|
203
|
+
data.integrated.lastSynchronize = new Date().toISOString()
|
|
201
204
|
}
|
|
202
|
-
updateData({ auto: temp, manual: data.manual })
|
|
203
205
|
showSuccess(t('order.syncMenuSuccess'))
|
|
204
206
|
})
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
const { currentRestaurant } = useCoreStore()
|
|
210
|
+
const restaurantId = computed(() => currentRestaurant.value?._id ?? '')
|
|
211
|
+
const primaryIntegrationId = computed(() => data.integrated.integrationId)
|
|
208
212
|
import { useSnackbarFunctions } from '@/components/snackbar'
|
|
209
213
|
const { showSuccess } = useSnackbarFunctions()
|
|
210
214
|
|
|
@@ -229,10 +233,25 @@ async function cancelTestOrder() {
|
|
|
229
233
|
:model-value="modelValue"
|
|
230
234
|
:company="company"
|
|
231
235
|
:can-use-auto-delivery-integration="canUseAutoDeliveryIntegration"
|
|
236
|
+
integration-id-read-only
|
|
232
237
|
@update:model-value="updateDataPass"
|
|
233
238
|
@sync-menu="syncMenu"
|
|
234
239
|
@flow-test-order="flowTestOrder"
|
|
235
240
|
@cancel-test-order="cancelTestOrder"
|
|
236
241
|
>
|
|
242
|
+
<template #integration-details>
|
|
243
|
+
<IntegrationIdentityDetails
|
|
244
|
+
:integration-id="primaryIntegrationId"
|
|
245
|
+
:partner-id="data.integrated.remoteId"
|
|
246
|
+
:partner-label="t('order.foodpandaStoreId')"
|
|
247
|
+
:store-label="t('order.primaryStore')"
|
|
248
|
+
/>
|
|
249
|
+
</template>
|
|
237
250
|
</IntegratedDelivery>
|
|
251
|
+
<FoodpandaAdditionalStores
|
|
252
|
+
v-if="canUseAutoDeliveryIntegration === true"
|
|
253
|
+
:restaurant-id="restaurantId"
|
|
254
|
+
:primary-enabled="modelValue.auto.enable"
|
|
255
|
+
@sync-menu="syncMenu"
|
|
256
|
+
/>
|
|
238
257
|
</template>
|
|
@@ -8,6 +8,8 @@ import type { IntegratedDeliverySettingData } from './IntegratedDelivery.vue'
|
|
|
8
8
|
import { useLoading } from '@/composables/loading'
|
|
9
9
|
import { remoteOrderApi } from '@/api/remoteOrder'
|
|
10
10
|
import IntegratedDelivery from './IntegratedDelivery.vue'
|
|
11
|
+
import AdditionalStores from './AdditionalStores.vue'
|
|
12
|
+
import IntegrationIdentityDetails from './IntegrationIdentityDetails.vue'
|
|
11
13
|
import { clone } from '@/helpers/object'
|
|
12
14
|
import { useI18n, useCoreStore } from '@feedmepos/mf-common'
|
|
13
15
|
|
|
@@ -61,16 +63,20 @@ const modelValue = computed<IntegratedDeliverySettingData>(() => {
|
|
|
61
63
|
})
|
|
62
64
|
|
|
63
65
|
const { currentRestaurant, currentCountry } = useCoreStore()
|
|
66
|
+
const restaurantId = computed(() => currentRestaurant.value?._id ?? '')
|
|
67
|
+
const primaryIntegrationId = computed(() => data.integrated.integrationId)
|
|
64
68
|
const learnMoreUrl = computed(() => {
|
|
65
69
|
const country = (currentCountry.value || F_COUNTRY.enum.MY) as F_COUNTRY
|
|
66
70
|
return GRABFOOD_LEARN_MORE_URL_BY_COUNTRY[country] || GRABFOOD_LEARN_MORE_URL_DEFAULT
|
|
67
71
|
})
|
|
68
72
|
|
|
69
73
|
function updateData({ manual, auto }: IntegratedDeliverySettingData) {
|
|
74
|
+
const { integrationId: merchantID, ...autoSettings } = auto
|
|
70
75
|
data.manual = manual
|
|
71
76
|
data.integrated = {
|
|
72
|
-
...
|
|
73
|
-
|
|
77
|
+
...data.integrated,
|
|
78
|
+
...autoSettings,
|
|
79
|
+
merchantID,
|
|
74
80
|
commissionRate: auto.commissionRate
|
|
75
81
|
}
|
|
76
82
|
return { manual: data.manual, integrated: data.integrated }
|
|
@@ -86,16 +92,14 @@ function updateDataPass({ manual, auto }: IntegratedDeliverySettingData) {
|
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
const { startAsyncCallWithErr } = useLoading()
|
|
89
|
-
async function syncMenu() {
|
|
95
|
+
async function syncMenu(merchantID?: string) {
|
|
90
96
|
await startAsyncCallWithErr(async () => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
lastSynchronize: res?.lastSynchronize || modelValue.value.auto.lastSynchronize
|
|
97
|
+
const isPrimary = merchantID === undefined
|
|
98
|
+
const targetMerchantID = merchantID ?? data.integrated.merchantID ?? ''
|
|
99
|
+
await remoteOrderApi.integratedDelivery.syncGrabfoodMenu(targetMerchantID)
|
|
100
|
+
if (isPrimary) {
|
|
101
|
+
data.integrated.lastSynchronize = new Date().toISOString()
|
|
97
102
|
}
|
|
98
|
-
updateData({ auto: temp, manual: data.manual })
|
|
99
103
|
showSuccess(t('order.syncMenuSuccess'))
|
|
100
104
|
})
|
|
101
105
|
}
|
|
@@ -147,7 +151,16 @@ async function startSelfServeFlow() {
|
|
|
147
151
|
@sync-menu="syncMenu"
|
|
148
152
|
@flow-test-order="flowTestOrder"
|
|
149
153
|
@cancel-test-order="cancelTestOrder"
|
|
150
|
-
|
|
154
|
+
>
|
|
155
|
+
<template #integration-details>
|
|
156
|
+
<IntegrationIdentityDetails
|
|
157
|
+
:integration-id="primaryIntegrationId"
|
|
158
|
+
:provider-id="data.integrated.merchantID"
|
|
159
|
+
:provider-label="t('order.merchantId')"
|
|
160
|
+
:store-label="t('order.primaryStore')"
|
|
161
|
+
/>
|
|
162
|
+
</template>
|
|
163
|
+
</IntegratedDelivery>
|
|
151
164
|
<div v-if="canUseAutoDeliveryIntegration === true">
|
|
152
165
|
<div class="flex flex-row mt-5 items-center">
|
|
153
166
|
<div class="flex-grow fm-typo-en-title-sm-600 mb-3 mt-3">
|
|
@@ -155,7 +168,7 @@ async function startSelfServeFlow() {
|
|
|
155
168
|
</div>
|
|
156
169
|
<FmButton
|
|
157
170
|
variant="primary"
|
|
158
|
-
:label="t('order.
|
|
171
|
+
:label="t('order.activate')"
|
|
159
172
|
@click="startSelfServeFlow"
|
|
160
173
|
class="m-1"
|
|
161
174
|
/>
|
|
@@ -167,4 +180,10 @@ async function startSelfServeFlow() {
|
|
|
167
180
|
>.
|
|
168
181
|
</div>
|
|
169
182
|
</div>
|
|
183
|
+
<AdditionalStores
|
|
184
|
+
v-if="canUseAutoDeliveryIntegration === true"
|
|
185
|
+
:restaurant-id="restaurantId"
|
|
186
|
+
:primary-enabled="modelValue.auto.enable"
|
|
187
|
+
@sync-menu="syncMenu"
|
|
188
|
+
/>
|
|
170
189
|
</template>
|
|
@@ -6,8 +6,9 @@ import type { DeliveryCompany } from '../delivery'
|
|
|
6
6
|
import { useMenuStore } from '@/stores/menu/menu'
|
|
7
7
|
import ManualIntegratedDeliverySetting from '../components/ManualIntegratedDeliverySetting.vue'
|
|
8
8
|
import { ItemTax } from '@/helpers/menu'
|
|
9
|
-
import { useCoreStore } from '@feedmepos/mf-common'
|
|
10
9
|
import { useI18n } from '@feedmepos/mf-common'
|
|
10
|
+
import { overrideMenu } from '@feedmepos/menu'
|
|
11
|
+
import { useInternalUser } from '@/composables/useInternalUser'
|
|
11
12
|
|
|
12
13
|
const { t } = useI18n()
|
|
13
14
|
|
|
@@ -54,6 +55,10 @@ const props = defineProps({
|
|
|
54
55
|
showTestOrderActions: {
|
|
55
56
|
type: Boolean,
|
|
56
57
|
default: true
|
|
58
|
+
},
|
|
59
|
+
integrationIdReadOnly: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
default: false
|
|
57
62
|
}
|
|
58
63
|
})
|
|
59
64
|
|
|
@@ -65,18 +70,12 @@ const emits = defineEmits<{
|
|
|
65
70
|
}>()
|
|
66
71
|
|
|
67
72
|
const menuStore = useMenuStore()
|
|
68
|
-
const CoreStore = useCoreStore()
|
|
69
73
|
const taxSystemCode = computed(() => ItemTax.taxSetting().systemCode)
|
|
74
|
+
const { isInternal } = useInternalUser()
|
|
70
75
|
|
|
71
76
|
// For now, allow all users to edit, but added detail logging in backend
|
|
72
77
|
const canEdit = computed(() => true)
|
|
73
78
|
|
|
74
|
-
// FeedMe-internal (staff/admin) only. Merchants/business owners must not edit
|
|
75
|
-
// the platform integration id or place test orders; both gated on this below.
|
|
76
|
-
const isInternal = computed(
|
|
77
|
-
() => (CoreStore.sessionUser.value?.role?.isStaff || CoreStore.sessionUser.value?.role?.isAdmin) ?? false
|
|
78
|
-
)
|
|
79
|
-
|
|
80
79
|
function updateManual(manual: FdoLinkedDelivery | null) {
|
|
81
80
|
emits('update:model-value', {
|
|
82
81
|
...props.modelValue,
|
|
@@ -249,8 +248,17 @@ const formattedMenuSyncError = computed(() => {
|
|
|
249
248
|
const matches = error.match(itemIdPattern)
|
|
250
249
|
|
|
251
250
|
if (matches) {
|
|
251
|
+
let items = menuStore.menu.modules.item
|
|
252
|
+
try {
|
|
253
|
+
items = overrideMenu({
|
|
254
|
+
menu: menuStore.menu,
|
|
255
|
+
catalogId: props.modelValue.auto.catalogId
|
|
256
|
+
}).modules.item
|
|
257
|
+
} catch {
|
|
258
|
+
// A malformed override must not prevent the integration setting from rendering.
|
|
259
|
+
}
|
|
252
260
|
for (const itemId of matches) {
|
|
253
|
-
const item =
|
|
261
|
+
const item = items.find((i) => i._id === itemId)
|
|
254
262
|
if (item) {
|
|
255
263
|
const itemName = item.code ? `${item.code} - ${item.name}` : item.name
|
|
256
264
|
formatted = formatted.replace(itemId, `"${itemName}" (${itemId})`)
|
|
@@ -281,7 +289,7 @@ const formattedMenuSyncError = computed(() => {
|
|
|
281
289
|
<div v-if="modelValue.auto.enable">
|
|
282
290
|
<FmCard variant="outlined" class="mt-10 p-5">
|
|
283
291
|
<FmTextField
|
|
284
|
-
:disabled="!isInternal"
|
|
292
|
+
:disabled="!isInternal || integrationIdReadOnly"
|
|
285
293
|
:model-value="modelValue.auto.integrationId"
|
|
286
294
|
:label="t('order.integrationId')"
|
|
287
295
|
@update:model-value="updateIntegrateId"
|
|
@@ -304,6 +312,8 @@ const formattedMenuSyncError = computed(() => {
|
|
|
304
312
|
<slot name="order-setting" :editable="!canEdit" />
|
|
305
313
|
</div>
|
|
306
314
|
|
|
315
|
+
<slot name="integration-details" />
|
|
316
|
+
|
|
307
317
|
<!-- Commision rate not used yet -->
|
|
308
318
|
<!-- <div class="mb-5">
|
|
309
319
|
<FmTextField
|
|
@@ -380,7 +390,9 @@ const formattedMenuSyncError = computed(() => {
|
|
|
380
390
|
<div v-else class="mt-10">
|
|
381
391
|
<FmSnackbar
|
|
382
392
|
:title="`${t('order.autoIntegration')}`"
|
|
383
|
-
:description="`${t('order.autoIntegrationWarning', {
|
|
393
|
+
:description="`${t('order.autoIntegrationWarning', {
|
|
394
|
+
reason: canUseAutoDeliveryIntegration
|
|
395
|
+
})}`"
|
|
384
396
|
variant="warning"
|
|
385
397
|
/>
|
|
386
398
|
</div>
|