@feedmepos/mf-order-setting 0.0.52 → 0.0.53

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 (74) hide show
  1. package/.env.dev +1 -1
  2. package/.env.prod +1 -1
  3. package/dist/{KioskDevicesView-D_YT-CL0.js → KioskDevicesView-CMKNjgWx.js} +1 -1
  4. package/dist/{KioskDevicesView.vue_vue_type_script_setup_true_lang-Bxv6oiFw.js → KioskDevicesView.vue_vue_type_script_setup_true_lang-B1sNvlUC.js} +2 -2
  5. package/dist/KioskSettingView-BE_pMA-i.js +720 -0
  6. package/dist/KioskView-U-Wg8oMC.js +480 -0
  7. package/dist/OrderSettingsView-BWzaITT6.js +51916 -0
  8. package/dist/app-CFfgPAd8.js +10445 -0
  9. package/dist/app.js +3 -3
  10. package/dist/dayjs.min-CuRr-wlf.js +283 -0
  11. package/dist/frontend/mf-order/src/app.d.ts +60 -8
  12. package/dist/frontend/mf-order/src/main.d.ts +60 -8
  13. package/dist/frontend/mf-order/src/modules/order-setting/kiosk/interface.d.ts +4 -2
  14. package/dist/frontend/mf-order/src/stores/order-setting/index.d.ts +15 -6
  15. package/dist/frontend/mf-order/src/stores/restaurant/index.d.ts +3 -3
  16. package/dist/frontend/mf-order/src/utils/firebase-app.d.ts +4 -0
  17. package/dist/frontend/mf-order/src/utils/firebase-storage.d.ts +2 -0
  18. package/dist/frontend/mf-order/src/views/all-orders/ReflowOrder.vue.d.ts +2 -2
  19. package/dist/frontend/mf-order/src/views/order-settings/delivery/inhouse/InHouseDelivery.vue.d.ts +2 -2
  20. package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +1 -1
  21. package/dist/{index-Bjwv8B4h.js → index-Bj0bCGTm.js} +25 -26
  22. package/dist/menu.dto-DAh1J2ES.js +128627 -0
  23. package/dist/package/entity/booking/booking.do.d.ts +30 -30
  24. package/dist/package/entity/delivery/delivery.dto.d.ts +6 -6
  25. package/dist/package/entity/food-court/order.do.d.ts +24 -4
  26. package/dist/package/entity/incoming-order/incoming-order-to-bill.dto.d.ts +12553 -162
  27. package/dist/package/entity/incoming-order/incoming-order.do.d.ts +43 -22268
  28. package/dist/package/entity/incoming-order/incoming-order.dto.d.ts +14805 -66
  29. package/dist/package/entity/incoming-order/incoming-order.enum.d.ts +1 -1
  30. package/dist/package/entity/index.d.ts +3 -0
  31. package/dist/package/entity/kiosk/marketing/marketing.dto.d.ts +1 -19864
  32. package/dist/package/entity/order/order-item/order-item.dto.d.ts +1 -3721
  33. package/dist/package/entity/order/order.dto.d.ts +342 -7293
  34. package/dist/package/entity/order/payment/payment.dto.d.ts +3 -344
  35. package/dist/package/entity/order-platform/foodpanda/foodpanda-order.dto.d.ts +6 -6
  36. package/dist/package/entity/order-platform/grabfood/grabfood-edit-order.do.d.ts +144 -0
  37. package/dist/package/entity/order-platform/grabfood/grabfood-omni.do.d.ts +282 -47
  38. package/dist/package/entity/order-platform/grabfood/grabfood-order.do.d.ts +6 -6
  39. package/dist/package/entity/order-platform/grabfood/grabfood-webhook.dto.d.ts +69 -14
  40. package/dist/package/entity/order-platform/grabfood/grabfood.enum.d.ts +2 -0
  41. package/dist/package/entity/order-platform/order-platform.dto.d.ts +7306 -3
  42. package/dist/package/entity/order-platform/shopeefood/shopeefood-order.do.d.ts +6 -6
  43. package/dist/package/entity/order-setting/dine-in/dine-in.do.d.ts +12 -0
  44. package/dist/package/entity/order-setting/dine-in/dine-in.dto.d.ts +12 -0
  45. package/dist/package/entity/order-setting/kiosk/kiosk.do.d.ts +6 -3
  46. package/dist/package/entity/order-setting/kiosk/kiosk.dto.d.ts +6 -3
  47. package/dist/package/entity/order-setting/order-setting.do.d.ts +13 -8
  48. package/dist/package/entity/order-setting/order-setting.dto.d.ts +30 -10
  49. package/dist/package/entity/queue/queue.do.d.ts +1 -11
  50. package/dist/package/entity/restaurant/restaurant.dto.d.ts +14 -9
  51. package/dist/package/entity/websocket/websocket.dto.d.ts +24 -12
  52. package/package.json +4 -2
  53. package/src/App.vue +3 -0
  54. package/src/locales/en-US.json +15 -2
  55. package/src/locales/ja-JP.json +31 -18
  56. package/src/locales/th-TH.json +15 -2
  57. package/src/locales/zh-CN.json +15 -2
  58. package/src/main.ts +10 -20
  59. package/src/modules/order-setting/kiosk/interface.ts +4 -2
  60. package/src/stores/order-setting/mapper.ts +63 -60
  61. package/src/utils/firebase-app.ts +34 -0
  62. package/src/utils/firebase-storage.ts +112 -0
  63. package/src/views/kiosk/settings/KioskPaymentTypeSection.vue +1 -19
  64. package/src/views/kiosk/settings/KioskSettingView.vue +136 -43
  65. package/src/views/order-settings/OrderSettingsView.vue +1 -1
  66. package/src/views/order-settings/dine-in/DineInSetting.vue +23 -1
  67. package/src/views/order-settings/drive-thru/DriveThruSetting.vue +19 -18
  68. package/src/views/order-settings/servicecharge/ServiceChargeRule.vue +5 -1
  69. package/dist/KioskSettingView-DS5vEQ07.js +0 -573
  70. package/dist/KioskView-B8NHcyGz.js +0 -477
  71. package/dist/OrderSettingsView-BhEN-2ZZ.js +0 -51602
  72. package/dist/app-BWFBpJUR.js +0 -2986
  73. package/dist/dayjs.min-CD9R3x-Y.js +0 -2092
  74. package/dist/menu.dto-Wzm4Tz0i.js +0 -101874
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feedmepos/mf-order-setting",
3
- "version": "0.0.52",
3
+ "version": "0.0.53",
4
4
  "type": "module",
5
5
  "module": "./dist/app.js",
6
6
  "license": "UNLICENSED",
@@ -17,7 +17,9 @@
17
17
  "build:mf": "vite build --mode fmmf",
18
18
  "build:dts": "NODE_OPTIONS=--max-old-space-size=16384 vue-tsc --declaration --emitDeclarationOnly -p tsconfig.app.json --outDir ./dist",
19
19
  "publish-npm": "yarn build && yarn npm publish --no-git-checks",
20
- "build-only": "dotenv -e .env.dev -- yarn build:mf && yarn build:dts",
20
+ "build-only": "yarn build-only:dev",
21
+ "build-only:dev": "dotenv -e .env.dev -- yarn build:mf && yarn build:dts",
22
+ "build-only:prod": "dotenv -e .env.prod -- yarn build:mf && yarn build:dts",
21
23
  "build:dev": "cross-env NODE_ENV=dev vite build --mode dev",
22
24
  "build:prod": "vite build --mode prod",
23
25
  "type-check": "set NODE_OPTIONS=--max-old-space-size=16384 && vue-tsc --noEmit -p tsconfig.app.json --composite false",
package/src/App.vue CHANGED
@@ -2,6 +2,9 @@
2
2
  import { onMounted } from "vue"
3
3
  import { googleMap, setGoogleMap } from "./helpers/map"
4
4
  import { Loader } from 'google-maps'
5
+ import { ensureFirebaseApp } from '@/utils/firebase-app'
6
+
7
+ ensureFirebaseApp()
5
8
 
6
9
  async function initGoogleMap() {
7
10
  if (googleMap()) return
@@ -95,8 +95,6 @@
95
95
  "deliveryZone": "Delivery Zone",
96
96
  "deviceBinding": "Device Binding",
97
97
  "deviceDetails": "Device Details",
98
- "otaChannel": "OTA channel",
99
- "otaChannelHint": "Optional. Leave blank to use the default OTA feed.",
100
98
  "dineIn": "Dine In",
101
99
  "dineInCanTakeaway": "Allow Takeaway",
102
100
  "driveThru": "Drive Thru",
@@ -168,6 +166,19 @@
168
166
  "kioskSettingFailed": "Failed to update kiosk order setting",
169
167
  "kioskSettingSuccess": "Kiosk order setting updated",
170
168
  "kioskSummary": "Kiosk Summary",
169
+ "coverImage": "Cover Image",
170
+ "coverImageLandscape": "Cover Image (Landscape)",
171
+ "coverImagePortrait": "Cover Image (Portrait)",
172
+ "coverImageLandscapeHint": "Recommended size: 1920 x 1080",
173
+ "coverImagePortraitHint": "Recommended size: 1080 x 1920",
174
+ "coverImageFallbackHint": "If not set, it will default to the Cover Image from restaurant settings.",
175
+ "uploadImage": "Upload Image",
176
+ "imageUrl": "Image URL",
177
+ "imageUrlPlaceholder": "Enter image URL",
178
+ "clearImage": "Clear Image",
179
+ "imageUploadSuccess": "Image uploaded",
180
+ "imageUploadFailed": "Failed to upload image",
181
+ "imageUploadRejected": "Image upload rejected",
171
182
  "lastOrderBeforeClosing": "Last order before closing",
172
183
  "lastOrderBeforeClosingDescription": "Orders must be placed before the designated closing time",
173
184
  "lastOrderMinutesBeforeCloseRule": "Cannot be less than 0 minutes",
@@ -228,6 +239,8 @@
228
239
  "disableRemoteQueue": "Disable Remote Queue",
229
240
  "disableSMS": "Disable SMS",
230
241
  "printChecklist": "Print Checklist",
242
+ "overridePax": "Overwrite Bill Pax",
243
+ "overridePaxSublabel": "Incoming order will overwrite the existing bill pax",
231
244
  "codeSettings": "Queue Code Settings",
232
245
  "addCodeSetting": "Add Queue Code Setting",
233
246
  "current": "Current",
@@ -91,17 +91,15 @@
91
91
  "deliveryOrder": "配達注文",
92
92
  "deliveryTerm": "配達条件",
93
93
  "deliveryTime": "配達時間",
94
- "deliveryTitle": "{company}配達",
95
- "deliveryZone": "配達エリア",
96
- "deviceBinding": "デバイス紐付け",
97
- "deviceDetails": "デバイス詳細",
98
- "otaChannel": "OTAチャネル",
99
- "otaChannelHint": "任意。空欄の場合はデフォルトのOTAフィードを使用します。",
100
- "dineIn": "店内飲食",
101
- "dineInCanTakeaway": "テイクアウト許可",
102
- "driveThru": "ドライブスルー",
103
- "driveThruSettingUpdated": "ドライブスルー設定が更新されました。",
104
- "enableDriveThru": "ドライブスルーを有効にする",
94
+ "deliveryTitle": "{company}配達",
95
+ "deliveryZone": "配達エリア",
96
+ "deviceBinding": "デバイス紐付け",
97
+ "deviceDetails": "デバイス詳細",
98
+ "dineIn": "店内飲食",
99
+ "dineInCanTakeaway": "テイクアウト許可",
100
+ "driveThru": "ドライブスルー",
101
+ "driveThruSettingUpdated": "ドライブスルー設定が更新されました。",
102
+ "enableDriveThru": "ドライブスルーを有効にする",
105
103
  "enableDriveThruSublabel": "お客様向けのドライブスルー注文を有効にする",
106
104
  "dineInType": "店内飲食タイプ",
107
105
  "discountCampaign": "割引キャンペーン",
@@ -159,11 +157,24 @@
159
157
  "item": "商品",
160
158
  "kiosk": "キオスク",
161
159
  "kioskDisplay": "キオスクディスプレイ",
162
- "kioskDisplayDescription": "「デバイスを紐付け」をクリックしてデバイス紐付け用の特別コードを取得してください。",
163
- "kioskSettingFailed": "キオスク注文設定の更新に失敗しました",
164
- "kioskSettingSuccess": "キオスク注文設定が更新されました",
165
- "kioskSummary": "キオスクサマリー",
166
- "lastOrderBeforeClosing": "閉店前の最終注文",
160
+ "kioskDisplayDescription": "「デバイスを紐付け」をクリックしてデバイス紐付け用の特別コードを取得してください。",
161
+ "kioskSettingFailed": "キオスク注文設定の更新に失敗しました",
162
+ "kioskSettingSuccess": "キオスク注文設定が更新されました",
163
+ "kioskSummary": "キオスクサマリー",
164
+ "coverImage": "カバー画像",
165
+ "coverImageLandscape": "カバー画像(横向き)",
166
+ "coverImagePortrait": "カバー画像(縦向き)",
167
+ "coverImageLandscapeHint": "推奨サイズ: 1920 x 1080",
168
+ "coverImagePortraitHint": "推奨サイズ: 1080 x 1920",
169
+ "coverImageFallbackHint": "未設定の場合、レストラン設定のカバー画像が使用されます",
170
+ "uploadImage": "画像をアップロード",
171
+ "imageUrl": "画像URL",
172
+ "imageUrlPlaceholder": "画像URLを入力",
173
+ "clearImage": "画像をクリア",
174
+ "imageUploadSuccess": "画像をアップロードしました",
175
+ "imageUploadFailed": "画像のアップロードに失敗しました",
176
+ "imageUploadRejected": "画像アップロードが拒否されました",
177
+ "lastOrderBeforeClosing": "閉店前の最終注文",
167
178
  "lastOrderBeforeClosingDescription": "指定された閉店時間前に注文を行う必要があります",
168
179
  "lastOrderMinutesBeforeCloseRule": "0分未満にはできません",
169
180
  "lastSync": "最終同期",
@@ -222,8 +233,10 @@
222
233
  "duration": "時間",
223
234
  "disableRemoteQueue": "リモート順番待ちを無効にする",
224
235
  "disableSMS": "SMSを無効にする",
225
- "printChecklist": "チェックリストを印刷",
226
- "codeSettings": "順番待ちコード設定",
236
+ "printChecklist": "チェックリストを印刷",
237
+ "overridePax": "請求書の人数を上書き",
238
+ "overridePaxSublabel": "新しい注文が既存の請求書の人数を上書きします",
239
+ "codeSettings": "順番待ちコード設定",
227
240
  "addCodeSetting": "順番待ちコード設定を追加",
228
241
  "current": "現在",
229
242
  "paxMin": "最小人数",
@@ -95,8 +95,6 @@
95
95
  "deliveryZone": "โซนการจัดส่ง",
96
96
  "deviceBinding": "การผูกอุปกรณ์",
97
97
  "deviceDetails": "รายละเอียดอุปกรณ์",
98
- "otaChannel": "ช่อง OTA",
99
- "otaChannelHint": "ไม่บังคับ เว้นว่างเพื่อใช้ฟีด OTA เริ่มต้น",
100
98
  "dineIn": "รับประทานอาหารที่ร้าน",
101
99
  "dineInCanTakeaway": "อนุญาตให้นำกลับบ้าน",
102
100
  "driveThru": "ไดรฟ์ทรู",
@@ -166,6 +164,19 @@
166
164
  "kioskSettingFailed": "อัปเดตการตั้งค่าคำสั่งซื้อคีออสก์ล้มเหลว",
167
165
  "kioskSettingSuccess": "อัปเดตการตั้งค่าคำสั่งซื้อคีออสก์แล้ว",
168
166
  "kioskSummary": "สรุปคีออสก์",
167
+ "coverImage": "รูปภาพหน้าปก",
168
+ "coverImageLandscape": "รูปภาพหน้าปก (แนวนอน)",
169
+ "coverImagePortrait": "รูปภาพหน้าปก (แนวตั้ง)",
170
+ "coverImageLandscapeHint": "ขนาดที่แนะนำ: 1920 x 1080",
171
+ "coverImagePortraitHint": "ขนาดที่แนะนำ: 1080 x 1920",
172
+ "coverImageFallbackHint": "หากไม่ตั้งค่า ระบบจะใช้ Cover Image จากการตั้งค่าร้านอาหารโดยอัตโนมัติ",
173
+ "uploadImage": "อัปโหลดรูปภาพ",
174
+ "imageUrl": "ลิงก์รูปภาพ",
175
+ "imageUrlPlaceholder": "กรอกลิงก์รูปภาพ",
176
+ "clearImage": "ล้างรูปภาพ",
177
+ "imageUploadSuccess": "อัปโหลดรูปภาพแล้ว",
178
+ "imageUploadFailed": "อัปโหลดรูปภาพไม่สำเร็จ",
179
+ "imageUploadRejected": "การอัปโหลดรูปภาพถูกปฏิเสธ",
169
180
  "lastOrderBeforeClosing": "เวลาสั่งอาหารครั้งสุดท้ายก่อนปิดร้าน",
170
181
  "lastOrderBeforeClosingDescription": "ต้องทำรายการสั่งซื้อก่อนเวลาปิดที่กำหนด",
171
182
  "lastOrderMinutesBeforeCloseRule": "ไม่สามารถน้อยกว่า 0 นาทีได้",
@@ -211,6 +222,8 @@
211
222
  "pleaseSelectRestaurant": "โปรดเลือกร้านอาหาร",
212
223
  "prefix": "คำนำหน้า",
213
224
  "printChecklist": "พิมพ์รายการตรวจสอบ",
225
+ "overridePax": "เขียนทับจำนวนคนในบิล",
226
+ "overridePaxSublabel": "ออเดอร์ใหม่จะเขียนทับจำนวนคนในบิลที่มีอยู่",
214
227
  "preOrder": "สั่งซื้อล่วงหน้า",
215
228
  "preOrderHint": "สั่งซื้อในวันที่ {today} ลูกค้าสามารถสั่งซื้อได้ระหว่าง {start} และ {end} สูงสุด 7 วัน",
216
229
  "pricePerMessage": "ราคาต่อข้อความ",
@@ -96,8 +96,6 @@
96
96
  "deliveryZone": "配送区域",
97
97
  "deviceBinding": "设备绑定",
98
98
  "deviceDetails": "设备详情",
99
- "otaChannel": "OTA 渠道",
100
- "otaChannelHint": "可选。留空将使用默认的 OTA 源。",
101
99
  "dineIn": "堂食",
102
100
  "dineInCanTakeaway": "允许外带",
103
101
  "driveThru": "得来速",
@@ -170,6 +168,19 @@
170
168
  "kioskSettingFailed": "自助点餐设备订单设置更新失败",
171
169
  "kioskSettingSuccess": "自助点餐设备订单设置更新成功",
172
170
  "kioskSummary": "自助点餐设备概览",
171
+ "coverImage": "封面图片",
172
+ "coverImageLandscape": "封面图片(横向)",
173
+ "coverImagePortrait": "封面图片(竖向)",
174
+ "coverImageLandscapeHint": "推荐尺寸:1920 x 1080",
175
+ "coverImagePortraitHint": "推荐尺寸:1080 x 1920",
176
+ "coverImageFallbackHint": "若不设置,将默认使用餐厅设置中的 Cover Image",
177
+ "uploadImage": "上传图片",
178
+ "imageUrl": "图片链接",
179
+ "imageUrlPlaceholder": "请输入图片链接",
180
+ "clearImage": "清除图片",
181
+ "imageUploadSuccess": "图片上传成功",
182
+ "imageUploadFailed": "图片上传失败",
183
+ "imageUploadRejected": "图片上传被拒绝",
173
184
  "lastOrderBeforeClosing": "关闭前最后点餐时间",
174
185
  "lastOrderBeforeClosingDescription": "订单必须在指定的关闭时间前提交",
175
186
  "lastOrderMinutesBeforeCloseRule": "不能少过0分钟",
@@ -215,6 +226,8 @@
215
226
  "pleaseSelectRestaurant": "请选择一个餐厅",
216
227
  "prefix": "前缀",
217
228
  "printChecklist": "打印清单",
229
+ "overridePax": "覆盖账单人数",
230
+ "overridePaxSublabel": "新订单将覆盖现有账单的人数",
218
231
  "preOrder": "预订",
219
232
  "preOrderHint": "于今日 {today} 下单,客户可以在 {start} 和 {end} 之间预订。最多 7 天。",
220
233
  "printReceiptAfterClose": "自动关闭账单后打印收据",
package/src/main.ts CHANGED
@@ -7,8 +7,7 @@ import { router } from './router'
7
7
  import { initialize, useCoreStore } from '@feedmepos/mf-common'
8
8
  import { FeedMeUI } from '@feedmepos/ui-library';
9
9
  import { AuthApi } from './api/auth'
10
- import { getAuth, signInWithCustomToken } from 'firebase/auth'
11
- import { initializeApp } from 'firebase/app'
10
+ import { signInWithCustomToken } from 'firebase/auth'
12
11
  import { useIframeStore } from './stores/iframe'
13
12
  import { createFeedMeI18n, type I18n, detectLocale } from "@feedmepos/mf-common"
14
13
  import enUS from "./locales/en-US.json"
@@ -16,6 +15,8 @@ import zhCN from "./locales/zh-CN.json"
16
15
  import thTH from "./locales/th-TH.json"
17
16
  import jaJP from "./locales/ja-JP.json"
18
17
 
18
+ import { getFirebaseAuth } from '@/utils/firebase-app'
19
+
19
20
  export const i18nMessages = {
20
21
  "en-US": enUS,
21
22
  "zh-CN": zhCN,
@@ -34,15 +35,15 @@ function initI18n(): I18n {
34
35
 
35
36
  const app = createApp(Entry)
36
37
 
38
+ app.use(initI18n())
39
+ app.use(createPinia())
40
+ app.use(router)
41
+
42
+
37
43
  app.use(FeedMeUI, {
38
44
  locale: detectLocale(),
39
45
  })
40
46
 
41
- app.use(createPinia() as any)
42
- app.use(router)
43
- app.use(initI18n() as any)
44
-
45
-
46
47
  const CoreStore = useCoreStore();
47
48
  const iframeMode = computed(() => {
48
49
  return new URLSearchParams(window.location.search).get('iframe') === 'true';
@@ -50,18 +51,7 @@ const iframeMode = computed(() => {
50
51
 
51
52
  useIframeStore().init(iframeMode.value);
52
53
 
53
- const firebaseApp = initializeApp({
54
- apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
55
- authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
56
- projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
57
- storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
58
- messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
59
- appId: import.meta.env.VITE_FIREBASE_APP_ID,
60
- measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID,
61
- databaseURL: import.meta.env.VITE_FIREBASE_DATABASE_URL,
62
- });
63
-
64
- const firebaseAuth = getAuth(firebaseApp);
54
+ const firebaseAuth = getFirebaseAuth()
65
55
 
66
56
  async function signInWithToken(token: string) {
67
57
  const customToken = await AuthApi.getCustomToken(token);
@@ -94,4 +84,4 @@ async function init() {
94
84
  }
95
85
 
96
86
 
97
- init().then(() => app.mount('#app'))
87
+ init().then(() => app.mount('#app'))
@@ -80,7 +80,8 @@ export interface MfKioskOrderSetting {
80
80
  takeaway: MfKioskTakeawaySetting,
81
81
  paymentSetting: MfKioskPaymentSetting,
82
82
  menuItem: MfKioskOrderSettingMenuItemForm,
83
- otaChannel?: string | null,
83
+ coverImageLandscape: string | null,
84
+ coverImagePortrait: string | null,
84
85
  }
85
86
 
86
87
  export interface MfKioskOrderSettingMenuItemForm {
@@ -92,7 +93,8 @@ export interface MfKioskOrderSettingForm {
92
93
  takeaway: KioskTakeawaySettingForm,
93
94
  paymentSetting: KioskPaymentSettingForm,
94
95
  menuItem: KioskMenuItemSettingForm,
95
- otaChannel?: string | null,
96
+ coverImageLandscape: string | null,
97
+ coverImagePortrait: string | null,
96
98
  }
97
99
 
98
100
  export type KioskPickAtCounterSettingForm = MfKioskPickAtCounterSetting;
@@ -50,7 +50,7 @@ export const defaultPickUp: KioskPickAtCounterSettingForm = {
50
50
  enablePaxDialog: false
51
51
  }
52
52
 
53
- export const defaultKioskOrderSetting: MfKioskOrderSettingForm = {
53
+ export const defaultKioskOrderSetting: MfKioskOrderSettingForm = {
54
54
  dineIn: {
55
55
  enabled: false,
56
56
  pickUp: defaultPickUp,
@@ -74,12 +74,13 @@ export const defaultKioskOrderSetting: MfKioskOrderSettingForm = {
74
74
  scanPay: false
75
75
  }
76
76
  }
77
- },
78
- menuItem: {
79
- showAllOnly: false
80
- },
81
- otaChannel: ''
82
- }
77
+ },
78
+ menuItem: {
79
+ showAllOnly: false
80
+ },
81
+ coverImageLandscape: null,
82
+ coverImagePortrait: null
83
+ }
83
84
 
84
85
  export const defaultSubmitOrderInstruction: MfKioskSubmitOrderInstruction = {
85
86
  payAtCounter: null,
@@ -108,10 +109,10 @@ const toKiosk = (
108
109
  enabled: kioskOrderSettings?.canTakeaway ?? false,
109
110
  submitOrderInstruction: kioskOrderSettings?.takeaway?.submitOrderInstruction
110
111
  ? {
111
- payAtCounter:
112
- kioskOrderSettings?.takeaway?.submitOrderInstruction?.payAtCounter?.['en'] ?? null,
113
- paid: kioskOrderSettings?.takeaway?.submitOrderInstruction?.paid?.['en'] ?? null
114
- }
112
+ payAtCounter:
113
+ kioskOrderSettings?.takeaway?.submitOrderInstruction?.payAtCounter?.['en'] ?? null,
114
+ paid: kioskOrderSettings?.takeaway?.submitOrderInstruction?.paid?.['en'] ?? null
115
+ }
115
116
  : defaultSubmitOrderInstruction
116
117
  },
117
118
  paymentSetting: {
@@ -130,12 +131,13 @@ const toKiosk = (
130
131
  }
131
132
  })
132
133
  },
133
- menuItem: {
134
- showAllOnly: kioskOrderSettings?.menuItem?.showAllOnly ?? false
135
- },
136
- otaChannel: kioskOrderSettings?.otaChannel ?? ''
137
- }
138
- }
134
+ menuItem: {
135
+ showAllOnly: kioskOrderSettings?.menuItem?.showAllOnly ?? false
136
+ },
137
+ coverImageLandscape: kioskOrderSettings?.coverImageLandscape ?? null,
138
+ coverImagePortrait: kioskOrderSettings?.coverImagePortrait ?? null
139
+ }
140
+ }
139
141
 
140
142
  const toKioskDineInSetting = (
141
143
  kioskDineInSeq: OrderKioskDineInSequenceDto,
@@ -146,35 +148,35 @@ const toKioskDineInSetting = (
146
148
  sequenceNumber: kioskDineInSeq,
147
149
  pickUp: kioskDineInSetting?.pickUp
148
150
  ? {
149
- ...kioskDineInSetting.pickUp,
150
- submitOrderInstruction: kioskDineInSetting.pickUp.submitOrderInstruction
151
- ? {
152
- payAtCounter:
153
- kioskDineInSetting.pickUp.submitOrderInstruction?.payAtCounter?.['en'] ?? null,
154
- paid: kioskDineInSetting.pickUp.submitOrderInstruction?.paid?.['en'] ?? null
155
- }
156
- : defaultSubmitOrderInstruction,
157
- enablePaxDialog: kioskDineInSetting.pickUp.enablePaxDialog
158
- ? kioskDineInSetting.pickUp.enablePaxDialog
159
- : false
160
- }
151
+ ...kioskDineInSetting.pickUp,
152
+ submitOrderInstruction: kioskDineInSetting.pickUp.submitOrderInstruction
153
+ ? {
154
+ payAtCounter:
155
+ kioskDineInSetting.pickUp.submitOrderInstruction?.payAtCounter?.['en'] ?? null,
156
+ paid: kioskDineInSetting.pickUp.submitOrderInstruction?.paid?.['en'] ?? null
157
+ }
158
+ : defaultSubmitOrderInstruction,
159
+ enablePaxDialog: kioskDineInSetting.pickUp.enablePaxDialog
160
+ ? kioskDineInSetting.pickUp.enablePaxDialog
161
+ : false
162
+ }
161
163
  : defaultPickUp,
162
164
  displayStand: kioskDineInSetting?.displayStand
163
165
  ? {
164
- enabled: kioskDineInSetting.displayStand.enabled,
165
- standSlotRange: kioskDineInSetting.displayStand.standSlotRange,
166
- prefix: kioskDineInSetting.displayStand.prefix,
167
- padDigit: kioskDineInSetting.displayStand.padDigit,
168
- enablePaxDialog: kioskDineInSetting.displayStand.enablePaxDialog ?? false,
169
- submitOrderInstruction: kioskDineInSetting.displayStand.submitOrderInstruction
170
- ? {
171
- payAtCounter:
172
- kioskDineInSetting.displayStand.submitOrderInstruction?.payAtCounter?.['en'] ??
173
- null,
174
- paid: kioskDineInSetting.displayStand.submitOrderInstruction?.paid?.['en'] ?? null
175
- }
176
- : defaultSubmitOrderInstruction
177
- }
166
+ enabled: kioskDineInSetting.displayStand.enabled,
167
+ standSlotRange: kioskDineInSetting.displayStand.standSlotRange,
168
+ prefix: kioskDineInSetting.displayStand.prefix,
169
+ padDigit: kioskDineInSetting.displayStand.padDigit,
170
+ enablePaxDialog: kioskDineInSetting.displayStand.enablePaxDialog ?? false,
171
+ submitOrderInstruction: kioskDineInSetting.displayStand.submitOrderInstruction
172
+ ? {
173
+ payAtCounter:
174
+ kioskDineInSetting.displayStand.submitOrderInstruction?.payAtCounter?.['en'] ??
175
+ null,
176
+ paid: kioskDineInSetting.displayStand.submitOrderInstruction?.paid?.['en'] ?? null
177
+ }
178
+ : defaultSubmitOrderInstruction
179
+ }
178
180
  : defaultDisplayStand
179
181
  }
180
182
  }
@@ -191,13 +193,13 @@ const toOrderKioskSettingsDto = (
191
193
  submitOrderInstruction: {
192
194
  payAtCounter: dineInSetting.displayStand.submitOrderInstruction?.payAtCounter
193
195
  ? {
194
- en: dineInSetting.displayStand.submitOrderInstruction.payAtCounter
195
- }
196
+ en: dineInSetting.displayStand.submitOrderInstruction.payAtCounter
197
+ }
196
198
  : null,
197
199
  paid: dineInSetting.displayStand.submitOrderInstruction?.paid
198
200
  ? {
199
- en: dineInSetting.displayStand.submitOrderInstruction.paid
200
- }
201
+ en: dineInSetting.displayStand.submitOrderInstruction.paid
202
+ }
201
203
  : null
202
204
  }
203
205
  },
@@ -206,26 +208,27 @@ const toOrderKioskSettingsDto = (
206
208
  submitOrderInstruction: {
207
209
  payAtCounter: dineInSetting.pickUp.submitOrderInstruction?.payAtCounter
208
210
  ? {
209
- en: dineInSetting.pickUp.submitOrderInstruction.payAtCounter
210
- }
211
+ en: dineInSetting.pickUp.submitOrderInstruction.payAtCounter
212
+ }
211
213
  : null,
212
214
  paid: dineInSetting.pickUp.submitOrderInstruction?.paid
213
215
  ? {
214
- en: dineInSetting.pickUp.submitOrderInstruction.paid
215
- }
216
+ en: dineInSetting.pickUp.submitOrderInstruction.paid
217
+ }
216
218
  : null
217
219
  }
218
220
  }
219
221
  })
220
222
 
221
- const createKioskSettings = (dineIn?: OrderKioskDineIn): OrderKioskSettings => ({
222
- canTakeaway: kioskSetting.takeaway.enabled,
223
- dineIn,
224
- menuItem: kioskSetting.menuItem,
225
- paymentSetting: createPaymentSettings(kioskSetting.paymentSetting),
226
- otaChannel: kioskSetting.otaChannel || undefined,
227
- takeaway: kioskSetting.takeaway.submitOrderInstruction
228
- ? {
223
+ const createKioskSettings = (dineIn?: OrderKioskDineIn): OrderKioskSettings => ({
224
+ canTakeaway: kioskSetting.takeaway.enabled,
225
+ dineIn,
226
+ menuItem: kioskSetting.menuItem,
227
+ coverImageLandscape: kioskSetting.coverImageLandscape,
228
+ coverImagePortrait: kioskSetting.coverImagePortrait,
229
+ paymentSetting: createPaymentSettings(kioskSetting.paymentSetting),
230
+ takeaway: kioskSetting.takeaway.submitOrderInstruction
231
+ ? {
229
232
  submitOrderInstruction: {
230
233
  payAtCounter: kioskSetting.takeaway.submitOrderInstruction.payAtCounter
231
234
  ? {
@@ -237,8 +240,8 @@ const toOrderKioskSettingsDto = (
237
240
  en: kioskSetting.takeaway.submitOrderInstruction.paid
238
241
  }
239
242
  : null
243
+ }
240
244
  }
241
- }
242
245
  : undefined
243
246
  })
244
247
 
@@ -0,0 +1,34 @@
1
+ import type { FirebaseApp } from 'firebase/app'
2
+ import { getApp, getApps, initializeApp } from 'firebase/app'
3
+ import { getAuth } from 'firebase/auth'
4
+ import { getStorage } from 'firebase/storage'
5
+
6
+ let firebaseApp: FirebaseApp | null = null
7
+
8
+ export function ensureFirebaseApp(): FirebaseApp {
9
+ if (firebaseApp) return firebaseApp
10
+
11
+ firebaseApp =
12
+ getApps().length > 0
13
+ ? getApp()
14
+ : initializeApp({
15
+ apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
16
+ authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
17
+ projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
18
+ storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
19
+ messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
20
+ appId: import.meta.env.VITE_FIREBASE_APP_ID,
21
+ measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID,
22
+ databaseURL: import.meta.env.VITE_FIREBASE_DATABASE_URL
23
+ })
24
+
25
+ return firebaseApp
26
+ }
27
+
28
+ export function getFirebaseAuth() {
29
+ return getAuth(ensureFirebaseApp())
30
+ }
31
+
32
+ export function getFirebaseStorage() {
33
+ return getStorage(ensureFirebaseApp())
34
+ }
@@ -0,0 +1,112 @@
1
+ import { AuthApi } from '@/api/auth'
2
+ import { SvcConfig } from '@/api'
3
+ import { signInWithCustomToken, signOut } from 'firebase/auth'
4
+ import { ref, uploadBytes } from 'firebase/storage'
5
+ import { getFirebaseAuth, getFirebaseStorage } from './firebase-app'
6
+
7
+ let pendingFirebaseSignIn: Promise<void> | null = null
8
+
9
+ function getFileExtension(file: File): string {
10
+ const nameExt = file.name.split('.').pop()?.toLowerCase()
11
+ if (nameExt && /^[a-z0-9]+$/.test(nameExt)) return nameExt
12
+
13
+ const mimeExtMap: Record<string, string> = {
14
+ 'image/jpeg': 'jpg',
15
+ 'image/png': 'png',
16
+ 'image/webp': 'webp',
17
+ 'image/gif': 'gif'
18
+ }
19
+ return mimeExtMap[file.type] ?? 'bin'
20
+ }
21
+
22
+ function getStorageBucketHost(): string {
23
+ const endpointWithFirebase = appEndpoint as typeof appEndpoint & {
24
+ firebase?: { storageBucket?: string }
25
+ }
26
+
27
+ const bucket =
28
+ endpointWithFirebase.firebase?.storageBucket ??
29
+ appApi.firebase.storageBucket ??
30
+ import.meta.env.VITE_FIREBASE_STORAGE_BUCKET
31
+
32
+ return bucket.replace(/^https?:\/\//, '').replace(/\/+$/, '')
33
+ }
34
+
35
+ export function generateItemThumbnailUrl(path: string): string {
36
+ const normalizedPath = path.replace(/^\/+/, '')
37
+ return `https://${getStorageBucketHost()}/${normalizedPath}`
38
+ }
39
+
40
+ async function ensureFirebaseAuth(): Promise<void> {
41
+ const auth = getFirebaseAuth()
42
+ if (auth.currentUser) return
43
+ await auth.authStateReady()
44
+ if (auth.currentUser) return
45
+
46
+ if (!pendingFirebaseSignIn) {
47
+ pendingFirebaseSignIn = (async () => {
48
+ const idToken = SvcConfig.getIdToken()
49
+ if (!idToken) {
50
+ throw new Error('Missing session token for Firebase authentication')
51
+ }
52
+
53
+ const customToken = await AuthApi.getCustomToken(idToken)
54
+ await signInWithCustomToken(auth, customToken)
55
+ })().finally(() => {
56
+ pendingFirebaseSignIn = null
57
+ })
58
+ }
59
+
60
+ await pendingFirebaseSignIn
61
+ if (!auth.currentUser) {
62
+ throw new Error('User not authenticated for image upload')
63
+ }
64
+ }
65
+
66
+ async function forceRefreshFirebaseAuth(): Promise<void> {
67
+ const auth = getFirebaseAuth()
68
+ const idToken = SvcConfig.getIdToken()
69
+ if (!idToken) {
70
+ throw new Error('Missing session token for Firebase authentication')
71
+ }
72
+
73
+ if (auth.currentUser) {
74
+ await signOut(auth)
75
+ }
76
+
77
+ const customToken = await AuthApi.getCustomToken(idToken)
78
+ await signInWithCustomToken(auth, customToken)
79
+ }
80
+
81
+ async function uploadWithAuthRetry(storagePath: string, file: File): Promise<void> {
82
+ const storage = getFirebaseStorage()
83
+ const fileRef = ref(storage, storagePath)
84
+ try {
85
+ await uploadBytes(fileRef, file, {
86
+ contentType: file.type || undefined
87
+ })
88
+ return
89
+ } catch (error) {
90
+ const code = (error as { code?: string })?.code
91
+ if (code !== 'storage/unauthorized') {
92
+ throw error
93
+ }
94
+ }
95
+
96
+ await forceRefreshFirebaseAuth()
97
+ const retriedRef = ref(storage, storagePath)
98
+ await uploadBytes(retriedRef, file, {
99
+ contentType: file.type || undefined
100
+ })
101
+ }
102
+
103
+ export async function uploadImageFile(pathPrefix: string, file: File): Promise<string> {
104
+ await ensureFirebaseAuth()
105
+
106
+ const normalizedPrefix = pathPrefix.replace(/^\/+/, '').replace(/\/+$/, '')
107
+ const ext = getFileExtension(file)
108
+ const fileName = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}.${ext}`
109
+ const fullPath = `${normalizedPrefix}/${fileName}`
110
+ await uploadWithAuthRetry(fullPath, file)
111
+ return generateItemThumbnailUrl(fullPath)
112
+ }