@feedmepos/mf-order-setting 0.0.90 → 0.0.91
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/DisplaySystemView-n7Kqdw-h.js +6564 -0
- package/dist/{KioskDevicesView-WuOTJ9Fi.js → KioskDevicesView-Bz6seroM.js} +1 -1
- package/dist/{KioskDevicesView.vue_vue_type_script_setup_true_lang-DdHfATyN.js → KioskDevicesView.vue_vue_type_script_setup_true_lang-I39sE8Pe.js} +3 -3
- package/dist/{KioskSettingView-Bsja2OqT.js → KioskSettingView-Cajjh4Zm.js} +3 -3
- package/dist/{KioskView-BH5qGTM_.js → KioskView-BPSbsMi5.js} +93 -89
- package/dist/{OrderSettingsView-D6V1ztiS.js → OrderSettingsView-jPxZ3JDM.js} +5 -5
- package/dist/{app-DsMk7W2h.js → app-DIhv5ssA.js} +266 -254
- package/dist/app.js +1 -1
- package/dist/frontend/mf-order/src/app.d.ts +18 -6
- package/dist/frontend/mf-order/src/main.d.ts +18 -6
- package/dist/frontend/mf-order/src/modules/display-system/interface.d.ts +6 -3
- package/dist/frontend/mf-order/src/modules/kiosk/bindDeviceError.d.ts +20 -0
- package/dist/frontend/mf-order/src/views/display-system/CanvasArea.vue.d.ts +48 -0
- package/dist/frontend/mf-order/src/views/display-system/DisplayLayoutCanvas.vue.d.ts +17 -14
- package/dist/frontend/mf-order/src/views/display-system/DisplayModulePickerDialog.vue.d.ts +2 -0
- package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +1 -1
- package/dist/{index-DGDiI0fr.js → index-Clp_YRJ2.js} +1 -1
- package/dist/{index-BUDM0Rqn.js → index-CmqNrkNl.js} +1 -1
- package/dist/{index-C2n1MV_1.js → index-vaSLX6r3.js} +2 -2
- package/dist/{order.dto-BRDWWgMV.js → order.dto-DV1qL3Q_.js} +21 -1
- package/dist/package/entity/alert/investigation.dto.d.ts +26 -0
- package/dist/package/entity/alert/lark-callback.dto.d.ts +19 -0
- package/dist/package/entity/alert/record-resolution.dto.d.ts +35 -0
- package/dist/package/entity/index.d.ts +1 -0
- package/dist/package/entity/kiosk/kiosk.do.d.ts +105 -0
- package/dist/package/entity/kiosk/kiosk.dto.d.ts +76 -0
- package/dist/style.css +1 -1
- package/package.json +3 -1
- package/src/locales/en-US.json +11 -4
- package/src/locales/zh-CN.json +9 -4
- package/src/modules/display-system/interface.spec.ts +90 -5
- package/src/modules/display-system/interface.ts +31 -21
- package/src/modules/kiosk/bindDeviceError.spec.ts +42 -0
- package/src/modules/kiosk/bindDeviceError.ts +40 -0
- package/src/views/display-system/CanvasArea.vue +355 -0
- package/src/views/display-system/DisplayLayoutCanvas.spec.ts +57 -0
- package/src/views/display-system/DisplayLayoutCanvas.vue +173 -298
- package/src/views/display-system/DisplayModulePickerDialog.spec.ts +71 -0
- package/src/views/display-system/DisplayModulePickerDialog.vue +107 -0
- package/src/views/display-system/DisplayTemplateEditor.spec.ts +151 -0
- package/src/views/display-system/DisplayTemplateEditor.vue +233 -193
- package/src/views/kiosk/KioskView.vue +2 -4
- package/vitest.config.ts +2 -0
- package/dist/DisplaySystemView-tosCa7Ej.js +0 -6485
- package/dist/frontend/mf-order/src/views/display-system/DisplayModulePalette.vue.d.ts +0 -13
- package/src/views/display-system/DisplayModulePalette.vue +0 -140
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.integration-details[data-v-64de12e4]{display:flex;flex-direction:column;gap:.625rem;padding:1rem;margin-top:1rem;border:1px solid #e5e7eb;border-radius:.5rem}.integration-details--owned[data-v-64de12e4]{border-color:var(--fm-color-primary-oats);box-shadow:inset .25rem 0 0 var(--fm-color-primary)}.integration-details__header[data-v-64de12e4]{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.5rem}.integration-details__title[data-v-64de12e4]{display:flex;align-items:center;gap:.375rem}.integration-details__store-label[data-v-64de12e4]{padding:.25rem .625rem;color:var(--fm-color-primary);background:var(--fm-color-primary-oats);border-radius:999px}.integration-details__row[data-v-64de12e4]{display:grid;grid-template-columns:10rem minmax(0,1fr);align-items:center;gap:1rem}.integration-details__value[data-v-64de12e4]{display:flex;min-width:0;align-items:center;gap:.5rem}.integration-details__action[data-v-64de12e4]{display:inline-flex;flex:0 0 auto}.integration-details__value>code[data-v-64de12e4]{flex:1 1 auto}.integration-details__summary[data-v-64de12e4]{display:flex;justify-content:flex-end;margin-top:.25rem}.integration-details__message[data-v-64de12e4]{max-height:9rem;overflow-y:auto;white-space:pre-wrap}code[data-v-64de12e4]{min-width:0;overflow-wrap:anywhere}@media (max-width: 460px){.integration-details__row[data-v-64de12e4]{grid-template-columns:1fr;gap:.25rem}.integration-details__header[data-v-64de12e4]{align-items:flex-start}}.store-section[data-v-64f1ea7c]{border-top:1px solid #e5e7eb;padding-top:1.5rem}.store-card[data-v-64f1ea7c]{padding:1.25rem;margin-bottom:1rem}.store-card__top[data-v-64f1ea7c]{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:1rem}.store-card__title[data-v-64f1ea7c]{min-width:0;word-break:break-all}.store-card__actions[data-v-64f1ea7c]{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.5rem;flex-shrink:0}.store-card__fields[data-v-64f1ea7c]{display:flex;flex-direction:column;gap:.625rem}.store-field[data-v-64f1ea7c]{display:grid;grid-template-columns:9rem minmax(0,1fr);gap:1rem}.store-field>span[data-v-64f1ea7c]:first-child{color:var(--fm-color-neutral-gray-400)}.store-field>span[data-v-64f1ea7c]:last-child{word-break:break-word}.store-sheet-body[data-v-64f1ea7c]{display:flex;flex-direction:column;gap:1rem;padding-top:.5rem}@media (max-width: 460px){.store-field[data-v-64f1ea7c]{grid-template-columns:1fr}}.store-section[data-v-fe141f25]{border-top:1px solid #e5e7eb;padding-top:1.5rem}.store-card[data-v-fe141f25]{padding:1.25rem;margin-bottom:1rem}.store-card__top[data-v-fe141f25]{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:1.25rem}.store-card__title[data-v-fe141f25]{min-width:0;word-break:break-all}.store-card__actions[data-v-fe141f25]{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.5rem;flex-shrink:0}.store-card__fields[data-v-fe141f25]{display:flex;flex-direction:column;gap:.625rem}.store-field[data-v-fe141f25]{display:grid;grid-template-columns:9rem minmax(0,1fr);align-items:start;-moz-column-gap:1rem;column-gap:1rem;row-gap:.125rem}.store-field>span[data-v-fe141f25]:last-child{word-break:break-word}.store-sheet-body[data-v-fe141f25]{display:flex;flex-direction:column;gap:1rem;padding-top:.5rem}.store-sheet-activate[data-v-fe141f25]{padding-top:1.25rem;border-top:1px solid #e5e7eb}.store-sheet-activate__hint[data-v-fe141f25]{margin-top:.5rem}@media (max-width: 460px){.store-field[data-v-fe141f25]{grid-template-columns:1fr}}.store-section[data-v-b94bbdb0]{border-top:1px solid #e5e7eb;padding-top:1.5rem}.store-card[data-v-b94bbdb0]{padding:1.25rem;margin-bottom:1rem}.store-card__top[data-v-b94bbdb0]{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:1rem}.store-card__title[data-v-b94bbdb0]{min-width:0;word-break:break-all}.store-card__actions[data-v-b94bbdb0]{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.5rem;flex-shrink:0}.store-card__fields[data-v-b94bbdb0]{display:flex;flex-direction:column;gap:.625rem}.store-field[data-v-b94bbdb0]{display:grid;grid-template-columns:9rem minmax(0,1fr);gap:1rem}.store-field>span[data-v-b94bbdb0]:first-child{color:var(--fm-color-neutral-gray-400)}.store-field>span[data-v-b94bbdb0]:last-child{word-break:break-word}.store-sheet-body[data-v-b94bbdb0]{display:flex;flex-direction:column;gap:1rem;padding-top:.5rem}@media (max-width: 460px){.store-field[data-v-b94bbdb0]{grid-template-columns:1fr}}[data-v-91393c58] .fm-checkbox--wrapper:has(input:disabled){pointer-events:none}[data-v-a959025e] .flex.flex-col.gap-8>:first-child,[data-v-a959025e] .flex.flex-col.gap-8>:last-child{display:none}.toggle[data-v-5266c20e]{position:relative;flex-shrink:0;width:52px;height:32px;padding:0;border:none;border-radius:9999px;background:var(--fm-color-neutral-gray-200);cursor:pointer;transition:background-color .2s ease-out}.toggle[data-v-5266c20e]:focus{outline:none}.toggle[data-v-5266c20e]:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--fm-color-primary) 40%,transparent)}.toggle--on[data-v-5266c20e]{background:var(--fm-color-primary)}.toggle__thumb[data-v-5266c20e]{position:absolute;top:50%;left:8px;width:18px;height:18px;border-radius:9999px;background:#fff;box-shadow:0 1px 2px #0003;transform:translateY(-50%);transition:all .2s ease-out}.toggle--on .toggle__thumb[data-v-5266c20e]{left:24px;width:24px;height:24px}.toggle:active .toggle__thumb[data-v-5266c20e]{transform:translateY(-50%) scale(1.1)}.delivery-company-filter-toolbar[data-v-e2763a83]{display:flex;flex-direction:row;justify-content:space-between}@media (max-width: 647px){.delivery-company-filter-toolbar[data-v-e2763a83]{flex-direction:column;flex-wrap:wrap}}.preview-frame[data-v-bd8d2976]{position:relative;width:100%;overflow:hidden;border:1px solid #d1d5db;border-radius:12px;aspect-ratio:16 / 9;background:#111827;box-shadow:0 16px 40px #0f172a1f}.preview-iframe[data-v-bd8d2976]{display:block;width:100%;height:100%;border:0;background:#f3f4f6}.preview-status[data-v-bd8d2976]{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-content:center;gap:12px;background:#f9fafbf0;color:#4b5563;text-align:center;font-size:13px}.preview-status p[data-v-bd8d2976]{margin:0}.preview-status[data-v-bd8d2976] button{justify-self:center}.preview-loading[data-v-bd8d2976]{display:grid;justify-items:center;gap:10px}.preview-loading span[data-v-bd8d2976]{width:24px;height:24px;border:3px solid #fed7aa;border-top-color:#ef661f;border-radius:50%;animation:preview-spin-bd8d2976 .8s linear infinite}@keyframes preview-spin-bd8d2976{to{transform:rotate(360deg)}}.canvas[data-v-18083337]{display:flex;min-height:0;flex:1;border-radius:12px;background:#f3f4f6;padding:8px}.canvas-row[data-v-18083337]{flex-direction:row}.canvas-column[data-v-18083337]{flex-direction:column}.canvas-group[data-v-18083337]{display:flex;min-width:0;min-height:0;gap:0}.canvas-area[data-v-18083337]{display:flex;min-width:0;min-height:78px;flex-direction:column;gap:8px;overflow:hidden;border:1.5px dashed #d1d5db;border-radius:10px;padding:10px;background:#fff;text-align:left}.canvas-area-nested[data-v-18083337]{min-height:62px}.canvas-area[data-v-18083337]:hover{border-color:#fdba74}.canvas-area-active[data-v-18083337]{border-style:solid;border-color:#f97316;background:#fffbf5;box-shadow:0 0 0 3px #f973161f}.canvas-area-context[data-v-18083337]{border-style:solid;border-color:#fed7aa;background:#fffdfa}.canvas-area-droppable[data-v-18083337]{border-color:#f97316;background:#fff7ed}.canvas-area-drop-over[data-v-18083337]{border-style:solid;box-shadow:0 0 0 3px #f9731633}.canvas-area-head[data-v-18083337]{display:flex;align-items:center;gap:8px}.canvas-area-name[data-v-18083337]{color:#6b7280;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.canvas-area-share[data-v-18083337]{margin-left:auto;color:#374151;font-size:12px;font-variant-numeric:tabular-nums;font-weight:700}.canvas-area-action[data-v-18083337]{display:grid;flex:0 0 auto;place-items:center;border-radius:6px;padding:2px;color:#9ca3af}.canvas-area-action[data-v-18083337]:hover{background:#fff7ed;color:#f97316}.canvas-chips[data-v-18083337]{display:flex;min-height:0;flex:1;flex-direction:column;gap:4px}.canvas-chip[data-v-18083337]{display:flex;align-items:center;gap:6px;border:1px solid #e5e7eb;border-radius:8px;padding:6px 8px;background:#fff;color:#374151;font-size:12px;text-align:left;cursor:grab}.canvas-chip[data-v-18083337]:hover{border-color:#fdba74}.canvas-chip-active[data-v-18083337]{border-color:#f97316;background:#fff;box-shadow:inset 4px 0 #f97316,0 0 0 3px #f973161f;font-weight:600}.canvas-chip-active .canvas-chip-grip[data-v-18083337]{color:#f97316}.canvas-chip-context[data-v-18083337]{border-color:#fed7aa;background:#fffdfa}.canvas-chip[data-v-18083337]:active{cursor:grabbing}.canvas-chip-grip[data-v-18083337]{flex:0 0 auto;color:#9ca3af}.canvas-chip-label[data-v-18083337]{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.canvas-area-empty[data-v-18083337]{display:grid;flex:1;place-items:center;border:1px dashed #d1d5db;border-radius:8px;color:#9ca3af;font-size:11px}.canvas-divider[data-v-18083337]{display:grid;flex:0 0 auto;place-items:center}.divider-vertical[data-v-18083337]{width:10px;cursor:col-resize}.divider-horizontal[data-v-18083337]{height:10px;cursor:row-resize}.canvas-divider-grip[data-v-18083337]{border-radius:999px;background:#d1d5db}.divider-vertical .canvas-divider-grip[data-v-18083337]{height:34px;width:3px}.divider-horizontal .canvas-divider-grip[data-v-18083337]{height:3px;width:34px}.canvas-divider:hover .canvas-divider-grip[data-v-18083337]{background:#f97316}.palette[data-v-29c6bf88]{display:flex;flex-direction:column;gap:16px}.palette-head[data-v-29c6bf88]{display:flex;flex-direction:column;gap:4px}.palette-title[data-v-29c6bf88]{color:#111827;font-size:13px;font-weight:600}.palette-hint[data-v-29c6bf88]{color:#6b7280;font-size:11px;line-height:1.4}.palette-group[data-v-29c6bf88]{display:flex;flex-direction:column;gap:6px}.palette-group-label[data-v-29c6bf88]{color:#9ca3af;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.palette-card[data-v-29c6bf88]{display:flex;align-items:center;gap:8px;border:1px solid #e5e7eb;border-radius:8px;padding:8px 10px;background:#fff;color:#374151;font-size:13px;text-align:left;cursor:grab}.palette-card[data-v-29c6bf88]:hover:not(:disabled){border-color:#f97316;background:#fffbf5}.palette-card[data-v-29c6bf88]:disabled{cursor:not-allowed;opacity:.5}.palette-card[data-v-29c6bf88]:active{cursor:grabbing}.palette-icon[data-v-29c6bf88]{display:grid;height:24px;width:24px;flex:0 0 auto;place-items:center;border-radius:6px;background:#f3f4f6;color:#4b5563}.palette-label[data-v-29c6bf88]{min-width:0;flex:1;line-height:1.25;overflow-wrap:anywhere}.media-field[data-v-839ff846]{display:flex;flex-direction:column;gap:6px}.media-label[data-v-839ff846]{color:#4b5563;font-size:12px}.media-thumb[data-v-839ff846]{height:72px;width:100%;border-radius:8px;border:1px solid #e5e7eb;-o-object-fit:cover;object-fit:cover}.media-empty[data-v-839ff846]{display:grid;height:72px;place-items:center;border:1px dashed #d1d5db;border-radius:8px;color:#9ca3af;font-size:12px}.media-warning[data-v-839ff846]{display:grid;height:72px;place-items:center;border:1px dashed #d1d5db;border-radius:8px;color:#b54708;font-size:12px}.media-actions[data-v-839ff846]{display:flex;gap:6px}.inspector[data-v-4cc442cf]{display:flex;flex-direction:column;gap:14px}.inspector-scope[data-v-4cc442cf]{align-self:stretch}.inspector-breadcrumb[data-v-4cc442cf]{display:flex;align-items:center;gap:2px;color:#6b7280;font-size:11px}.inspector-section[data-v-4cc442cf]{display:flex;flex-direction:column;gap:10px;border-top:1px solid #e5e7eb;padding-top:12px}.inspector-section-title[data-v-4cc442cf]{color:#9ca3af;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.inspector-note[data-v-4cc442cf]{color:#6b7280;font-size:11px;line-height:1.45}.inspector-warning[data-v-4cc442cf]{display:flex;align-items:flex-start;gap:6px;border-radius:8px;background:#fffbeb;padding:8px 10px;color:#92400e;font-size:12px}.inspector-list[data-v-4cc442cf]{display:flex;flex-direction:column;gap:8px}.inspector-list-head[data-v-4cc442cf]{display:flex;align-items:baseline;gap:6px}.inspector-list-count[data-v-4cc442cf]{border-radius:999px;background:#f3f4f6;padding:0 6px;color:#6b7280;font-size:10px;font-variant-numeric:tabular-nums}.inspector-item[data-v-4cc442cf]{display:flex;flex-direction:column;gap:10px;border:1px solid #e5e7eb;border-radius:10px;padding:10px;background:#fcfcfd}.inspector-item-head[data-v-4cc442cf]{display:flex;align-items:center;gap:2px}.inspector-item-name[data-v-4cc442cf]{flex:1;color:#6b7280;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.inspector-item-action[data-v-4cc442cf]{display:grid;flex:0 0 auto;place-items:center;border-radius:6px;padding:2px;color:#9ca3af}.inspector-item-action[data-v-4cc442cf]:hover:not(:disabled){background:#fff7ed;color:#f97316}.inspector-item-action[data-v-4cc442cf]:disabled{opacity:.35}.inspector-list-add[data-v-4cc442cf]{display:flex;gap:6px}.media-field[data-v-4cc442cf]{display:flex;flex-direction:column;gap:6px}.media-label[data-v-4cc442cf]{color:#4b5563;font-size:12px}.media-thumb[data-v-4cc442cf]{height:72px;width:100%;border-radius:8px;border:1px solid #e5e7eb;-o-object-fit:cover;object-fit:cover}.media-empty[data-v-4cc442cf]{display:grid;height:72px;place-items:center;border:1px dashed #d1d5db;border-radius:8px;color:#9ca3af;font-size:12px}.media-actions[data-v-4cc442cf]{display:flex;gap:6px}.editor-head[data-v-fe5dec51]{display:flex;shrink:0;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid #e5e7eb;padding:12px 20px}.editor-identity[data-v-fe5dec51]{display:flex;min-width:0;align-items:center;gap:10px}.editor-name[data-v-fe5dec51]{width:min(320px,40vw)}.editor-orientation[data-v-fe5dec51]{width:160px}.editor-error[data-v-fe5dec51]{flex-shrink:0;margin:12px 16px 0;border-radius:8px;background:#fef2f2;padding:8px 12px;color:#b91c1c;font-size:13px}.rail-scale[data-v-fe5dec51]{display:flex;flex-direction:column;gap:6px;margin-bottom:18px;border-bottom:1px solid #e5e7eb;padding-bottom:16px}.rail-scale-label[data-v-fe5dec51]{color:#4b5563;font-size:12px;font-weight:600}.rail-scale-hint[data-v-fe5dec51]{margin:0;color:#6b7280;font-size:11px;line-height:1.4}.rail-scale-warning[data-v-fe5dec51]{margin:0;border-radius:8px;padding:6px 8px;color:#9a3412;background:#fff7ed;font-size:11px;line-height:1.4}.editor-body[data-v-fe5dec51]{display:grid;min-height:0;flex:1;grid-template-columns:268px minmax(0,1fr) 320px}.editor-rail[data-v-fe5dec51],.editor-inspector[data-v-fe5dec51]{min-height:0;overflow:auto;padding:16px}.editor-rail[data-v-fe5dec51]{border-right:1px solid #e5e7eb;background:#f9fafb}.editor-inspector[data-v-fe5dec51]{border-left:1px solid #e5e7eb;background:#f9fafb}.editor-stage[data-v-fe5dec51]{display:flex;min-height:0;flex-direction:column;gap:12px;overflow:auto;padding:16px}.editor-stage-portrait[data-v-fe5dec51]{flex-direction:row;align-items:flex-start;gap:16px;height:100%}.editor-stage-portrait .stage-preview[data-v-fe5dec51]{min-height:0;flex:0 0 auto;align-self:stretch}.editor-stage-portrait .preview-portrait[data-v-fe5dec51]{height:100%;max-height:none}.editor-stage-portrait .stage-layout[data-v-fe5dec51]{min-width:0;flex:1}.editor-stage-portrait .layout-bar[data-v-fe5dec51]{flex-direction:column;align-items:stretch;gap:8px}.editor-stage-portrait .layout-actions[data-v-fe5dec51]{flex-wrap:wrap}.stage-preview[data-v-fe5dec51]{display:flex;flex-direction:column}.preview-frame[data-v-fe5dec51]{overflow:hidden;border:1px solid #e5e7eb;border-radius:12px;background:#101828;margin:0 auto}.preview-landscape[data-v-fe5dec51]{width:100%;max-height:52vh;aspect-ratio:16 / 9}.preview-portrait[data-v-fe5dec51]{height:52vh;max-width:100%;aspect-ratio:9 / 16}.preview-frame[data-v-fe5dec51]>*{width:100%;height:100%}.stage-layout[data-v-fe5dec51]{display:flex;min-height:0;flex:0 0 auto;flex-direction:column;gap:10px}.stage-layout[data-v-fe5dec51] .canvas-row{min-height:176px;max-height:260px}.stage-layout[data-v-fe5dec51] .canvas-column{min-height:calc(var(--area-count, 2) * 96px + 16px)}.layout-bar[data-v-fe5dec51]{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.layout-bar-title[data-v-fe5dec51]{display:flex;min-width:0;flex-direction:column;gap:2px}.layout-title[data-v-fe5dec51]{color:#111827;font-size:13px;font-weight:600}.layout-hint[data-v-fe5dec51]{color:#9ca3af;font-size:11px}.layout-actions[data-v-fe5dec51]{display:flex;flex-shrink:0;align-items:center;gap:6px}.layout-conflict[data-v-fe5dec51]{display:flex;align-items:flex-start;gap:6px;border-radius:8px;background:#fffbeb;padding:8px 10px;color:#92400e;font-size:12px;cursor:pointer}.preset-row[data-v-fe5dec51]{display:flex;flex-wrap:wrap;gap:8px;border-radius:10px;background:#f9fafb;padding:10px}.preset-card[data-v-fe5dec51]{display:flex;width:108px;flex-direction:column;gap:6px;border:1px solid #d1d5db;border-radius:10px;padding:8px;background:#fff;text-align:left}.preset-card[data-v-fe5dec51]:hover{border-color:#f97316;background:#fff7ed}.preset-diagram[data-v-fe5dec51]{display:flex;height:34px;gap:3px;border-radius:6px;background:#f3f4f6;padding:3px}.preset-row-dir[data-v-fe5dec51]{flex-direction:row}.preset-column-dir[data-v-fe5dec51]{flex-direction:column}.preset-cell[data-v-fe5dec51]{flex-basis:0;border-radius:4px;background:#fdba74}.preset-label[data-v-fe5dec51]{color:#374151;font-size:11px;line-height:1.3}@media (max-width: 1280px){.editor-body[data-v-fe5dec51]{grid-template-columns:220px minmax(0,1fr)}.editor-inspector[data-v-fe5dec51]{grid-column:1 / -1;border-left:0;border-top:1px solid #e5e7eb}}.layout-split[data-v-5595b416]{display:flex;min-width:0;min-height:0;flex:1;gap:6px}.layout-row[data-v-5595b416]{flex-direction:row}.layout-column[data-v-5595b416]{flex-direction:column}.layout-zone[data-v-5595b416]{display:flex;min-width:0;min-height:36px;flex:1;flex-direction:row;align-items:center;gap:8px;overflow:hidden;border:1.5px dashed #d1d5db;border-radius:10px;padding:6px 10px;background:#fff;text-align:left;transition:border-color .12s ease,background .12s ease}.layout-zone[data-v-5595b416]:hover{border-color:#fdba74;background:#fffbf5}.layout-zone-selected[data-v-5595b416]{border-style:solid;border-color:#f97316;background:#fff7ed;box-shadow:inset 0 0 0 1px #f97316}.layout-zone-index[data-v-5595b416]{flex:none;color:#6b7280;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.layout-zone-blocks[data-v-5595b416]{display:flex;min-width:0;gap:4px}.layout-zone-chip[data-v-5595b416]{display:inline-flex;max-width:100%;align-items:center;gap:4px;border-radius:999px;background:#f3f4f6;padding:3px 8px;color:#374151;font-size:11px}.display-toolbar[data-v-a2dda95a]{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid #e5e7eb;padding-bottom:14px}.display-controls[data-v-a2dda95a]{display:flex;align-items:center;justify-content:space-between;gap:16px;padding-top:12px}.display-restaurant-select[data-v-a2dda95a]{width:min(360px,100%)}.display-outlet[data-v-a2dda95a]{display:flex;align-items:center;gap:16px}.display-entitlement[data-v-a2dda95a]{color:#6b7280;font-size:12px;white-space:nowrap}.restriction-note[data-v-a2dda95a]{margin-top:12px;border:1px solid #fed7aa;border-radius:10px;padding:10px 12px;color:#9a3412;background:#fff7ed;font-size:13px}.empty-state[data-v-a2dda95a]{display:grid;justify-items:center;gap:6px;border:1px dashed #d1d5db;border-radius:12px;padding:56px 20px;color:#6b7280;text-align:center}.empty-state strong[data-v-a2dda95a]{color:#111827}.layout-thumb[data-v-a2dda95a]{display:flex;height:auto;min-height:96px;margin-top:16px;border-radius:10px;background:#f3f4f6;padding:5px;pointer-events:none}.thumb-caption[data-v-a2dda95a]{margin-top:8px;color:#6b7280;font-size:11px}.resource-card[data-v-a2dda95a]{border:1px solid #e5e7eb;border-radius:12px;padding:20px;background:#fff;box-shadow:0 2px 8px #0000000a}.mode-badge[data-v-a2dda95a],.status-badge[data-v-a2dda95a]{border-radius:999px;padding:3px 8px;background:#f3f4f6;font-size:11px;font-weight:700}.status-badge.active[data-v-a2dda95a]{color:#166534;background:#dcfce7}.status-badge.disabled[data-v-a2dda95a]{color:#991b1b;background:#fee2e2}.status-badge.unlicensed[data-v-a2dda95a]{color:#b42318;background:#fef3f2;border:1px solid #fecdca}.online-dot[data-v-a2dda95a]{color:#6b7280;font-size:11px}.online-dot[data-v-a2dda95a]:before{display:inline-block;width:7px;height:7px;margin-right:5px;border-radius:50%;background:#9ca3af;content:""}.online-dot.online[data-v-a2dda95a]{color:#15803d}.online-dot.online[data-v-a2dda95a]:before{background:#22c55e}.meta-grid[data-v-a2dda95a]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:18px;border-top:1px solid #f0f0f0;padding-top:16px}.meta-grid dt[data-v-a2dda95a]{color:#6b7280;font-size:11px}.meta-grid dd[data-v-a2dda95a]{overflow:hidden;margin-top:2px;font-size:12px;text-overflow:ellipsis;white-space:nowrap}@media (max-width:640px){.display-toolbar[data-v-a2dda95a]{align-items:flex-start}.display-controls[data-v-a2dda95a]{align-items:stretch;flex-direction:column}.display-outlet[data-v-a2dda95a]{align-items:stretch;flex-direction:column;gap:8px}.display-restaurant-select[data-v-a2dda95a]{width:100%}}
|
|
1
|
+
.integration-details[data-v-64de12e4]{display:flex;flex-direction:column;gap:.625rem;padding:1rem;margin-top:1rem;border:1px solid #e5e7eb;border-radius:.5rem}.integration-details--owned[data-v-64de12e4]{border-color:var(--fm-color-primary-oats);box-shadow:inset .25rem 0 0 var(--fm-color-primary)}.integration-details__header[data-v-64de12e4]{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.5rem}.integration-details__title[data-v-64de12e4]{display:flex;align-items:center;gap:.375rem}.integration-details__store-label[data-v-64de12e4]{padding:.25rem .625rem;color:var(--fm-color-primary);background:var(--fm-color-primary-oats);border-radius:999px}.integration-details__row[data-v-64de12e4]{display:grid;grid-template-columns:10rem minmax(0,1fr);align-items:center;gap:1rem}.integration-details__value[data-v-64de12e4]{display:flex;min-width:0;align-items:center;gap:.5rem}.integration-details__action[data-v-64de12e4]{display:inline-flex;flex:0 0 auto}.integration-details__value>code[data-v-64de12e4]{flex:1 1 auto}.integration-details__summary[data-v-64de12e4]{display:flex;justify-content:flex-end;margin-top:.25rem}.integration-details__message[data-v-64de12e4]{max-height:9rem;overflow-y:auto;white-space:pre-wrap}code[data-v-64de12e4]{min-width:0;overflow-wrap:anywhere}@media (max-width: 460px){.integration-details__row[data-v-64de12e4]{grid-template-columns:1fr;gap:.25rem}.integration-details__header[data-v-64de12e4]{align-items:flex-start}}.store-section[data-v-64f1ea7c]{border-top:1px solid #e5e7eb;padding-top:1.5rem}.store-card[data-v-64f1ea7c]{padding:1.25rem;margin-bottom:1rem}.store-card__top[data-v-64f1ea7c]{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:1rem}.store-card__title[data-v-64f1ea7c]{min-width:0;word-break:break-all}.store-card__actions[data-v-64f1ea7c]{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.5rem;flex-shrink:0}.store-card__fields[data-v-64f1ea7c]{display:flex;flex-direction:column;gap:.625rem}.store-field[data-v-64f1ea7c]{display:grid;grid-template-columns:9rem minmax(0,1fr);gap:1rem}.store-field>span[data-v-64f1ea7c]:first-child{color:var(--fm-color-neutral-gray-400)}.store-field>span[data-v-64f1ea7c]:last-child{word-break:break-word}.store-sheet-body[data-v-64f1ea7c]{display:flex;flex-direction:column;gap:1rem;padding-top:.5rem}@media (max-width: 460px){.store-field[data-v-64f1ea7c]{grid-template-columns:1fr}}.store-section[data-v-fe141f25]{border-top:1px solid #e5e7eb;padding-top:1.5rem}.store-card[data-v-fe141f25]{padding:1.25rem;margin-bottom:1rem}.store-card__top[data-v-fe141f25]{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:1.25rem}.store-card__title[data-v-fe141f25]{min-width:0;word-break:break-all}.store-card__actions[data-v-fe141f25]{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.5rem;flex-shrink:0}.store-card__fields[data-v-fe141f25]{display:flex;flex-direction:column;gap:.625rem}.store-field[data-v-fe141f25]{display:grid;grid-template-columns:9rem minmax(0,1fr);align-items:start;-moz-column-gap:1rem;column-gap:1rem;row-gap:.125rem}.store-field>span[data-v-fe141f25]:last-child{word-break:break-word}.store-sheet-body[data-v-fe141f25]{display:flex;flex-direction:column;gap:1rem;padding-top:.5rem}.store-sheet-activate[data-v-fe141f25]{padding-top:1.25rem;border-top:1px solid #e5e7eb}.store-sheet-activate__hint[data-v-fe141f25]{margin-top:.5rem}@media (max-width: 460px){.store-field[data-v-fe141f25]{grid-template-columns:1fr}}.store-section[data-v-b94bbdb0]{border-top:1px solid #e5e7eb;padding-top:1.5rem}.store-card[data-v-b94bbdb0]{padding:1.25rem;margin-bottom:1rem}.store-card__top[data-v-b94bbdb0]{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:1rem}.store-card__title[data-v-b94bbdb0]{min-width:0;word-break:break-all}.store-card__actions[data-v-b94bbdb0]{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.5rem;flex-shrink:0}.store-card__fields[data-v-b94bbdb0]{display:flex;flex-direction:column;gap:.625rem}.store-field[data-v-b94bbdb0]{display:grid;grid-template-columns:9rem minmax(0,1fr);gap:1rem}.store-field>span[data-v-b94bbdb0]:first-child{color:var(--fm-color-neutral-gray-400)}.store-field>span[data-v-b94bbdb0]:last-child{word-break:break-word}.store-sheet-body[data-v-b94bbdb0]{display:flex;flex-direction:column;gap:1rem;padding-top:.5rem}@media (max-width: 460px){.store-field[data-v-b94bbdb0]{grid-template-columns:1fr}}[data-v-91393c58] .fm-checkbox--wrapper:has(input:disabled){pointer-events:none}[data-v-a959025e] .flex.flex-col.gap-8>:first-child,[data-v-a959025e] .flex.flex-col.gap-8>:last-child{display:none}.toggle[data-v-5266c20e]{position:relative;flex-shrink:0;width:52px;height:32px;padding:0;border:none;border-radius:9999px;background:var(--fm-color-neutral-gray-200);cursor:pointer;transition:background-color .2s ease-out}.toggle[data-v-5266c20e]:focus{outline:none}.toggle[data-v-5266c20e]:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--fm-color-primary) 40%,transparent)}.toggle--on[data-v-5266c20e]{background:var(--fm-color-primary)}.toggle__thumb[data-v-5266c20e]{position:absolute;top:50%;left:8px;width:18px;height:18px;border-radius:9999px;background:#fff;box-shadow:0 1px 2px #0003;transform:translateY(-50%);transition:all .2s ease-out}.toggle--on .toggle__thumb[data-v-5266c20e]{left:24px;width:24px;height:24px}.toggle:active .toggle__thumb[data-v-5266c20e]{transform:translateY(-50%) scale(1.1)}.delivery-company-filter-toolbar[data-v-e2763a83]{display:flex;flex-direction:row;justify-content:space-between}@media (max-width: 647px){.delivery-company-filter-toolbar[data-v-e2763a83]{flex-direction:column;flex-wrap:wrap}}.preview-frame[data-v-bd8d2976]{position:relative;width:100%;overflow:hidden;border:1px solid #d1d5db;border-radius:12px;aspect-ratio:16 / 9;background:#111827;box-shadow:0 16px 40px #0f172a1f}.preview-iframe[data-v-bd8d2976]{display:block;width:100%;height:100%;border:0;background:#f3f4f6}.preview-status[data-v-bd8d2976]{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-content:center;gap:12px;background:#f9fafbf0;color:#4b5563;text-align:center;font-size:13px}.preview-status p[data-v-bd8d2976]{margin:0}.preview-status[data-v-bd8d2976] button{justify-self:center}.preview-loading[data-v-bd8d2976]{display:grid;justify-items:center;gap:10px}.preview-loading span[data-v-bd8d2976]{width:24px;height:24px;border:3px solid #fed7aa;border-top-color:#ef661f;border-radius:50%;animation:preview-spin-bd8d2976 .8s linear infinite}@keyframes preview-spin-bd8d2976{to{transform:rotate(360deg)}}.canvas-area[data-v-cdd742c6]{position:relative;display:flex;min-width:0;min-height:0;flex-direction:column}.canvas-area-card[data-v-cdd742c6]{display:flex;flex:1;min-width:0;min-height:0;flex-direction:column;gap:8px;overflow:visible;border:1.5px dashed rgb(255 255 255 / 55%);border-radius:10px;padding:6px;background:#11182752;text-align:left}.canvas-area-card[data-v-cdd742c6]:hover{background:#1118276b}.canvas-area-active[data-v-cdd742c6]{border-style:solid;border-color:#f97316;background:#f973164d;box-shadow:0 0 0 3px #f973161f}.canvas-area-context[data-v-cdd742c6]{border-style:solid;border-color:#fed7aa;background:#fdba7433}.canvas-area-droppable[data-v-cdd742c6]{border-color:#f97316;background:#f973165c}.canvas-area-drop-over[data-v-cdd742c6]{border-style:solid;box-shadow:0 0 0 3px #f9731633}.canvas-area-head[data-v-cdd742c6]{display:flex;align-items:center;gap:8px}.canvas-area-name[data-v-cdd742c6],.canvas-area-share[data-v-cdd742c6]{color:#f9fafb;text-shadow:0 1px 3px rgb(0 0 0 / 70%);font-weight:700}.canvas-area-name[data-v-cdd742c6]{font-size:10px;letter-spacing:.06em;text-transform:uppercase}.canvas-area-share[data-v-cdd742c6]{margin-left:auto;font-size:12px;font-variant-numeric:tabular-nums}.canvas-area-remove[data-v-cdd742c6]{display:grid;flex:0 0 auto;place-items:center;border-radius:999px;padding:2px;background:#1118278c;color:#f9fafb;opacity:0;transition:opacity .1s ease}.canvas-area-remove[data-v-cdd742c6]:hover{background:#fef2f2;color:#dc2626}.canvas-edge[data-v-cdd742c6]{position:absolute;display:grid;place-items:center;width:20px;height:20px;border-radius:999px;background:#fff;box-shadow:0 0 0 1.5px #d1d5db;color:#9ca3af;opacity:0;transition:opacity .1s ease;z-index:1}.canvas-area:hover .canvas-edge[data-v-cdd742c6],.canvas-area:focus-within .canvas-edge[data-v-cdd742c6],.canvas-area:hover .canvas-area-remove[data-v-cdd742c6],.canvas-area:focus-within .canvas-area-remove[data-v-cdd742c6]{opacity:1}.canvas-edge[data-v-cdd742c6]:hover{box-shadow:0 0 0 1.5px #f97316;color:#f97316}.canvas-edge-right[data-v-cdd742c6]{top:50%;right:-10px;transform:translateY(-50%)}.canvas-edge-bottom[data-v-cdd742c6]{bottom:-10px;left:50%;transform:translate(-50%)}.canvas-chips[data-v-cdd742c6]{display:flex;min-height:0;flex:1;flex-direction:column}.canvas-chip[data-v-cdd742c6]{display:flex;align-items:center;align-self:flex-start;max-width:100%;gap:6px;border:1px solid #e5e7eb;border-radius:8px;padding:6px 8px;background:#ffffffeb;color:#374151;font-size:12px;text-align:left;cursor:grab}.canvas-chip[data-v-cdd742c6]:hover{border-color:#fdba74}.canvas-chip-active[data-v-cdd742c6]{border-color:#f97316;box-shadow:inset 4px 0 #f97316,0 0 0 3px #f973161f;font-weight:600}.canvas-chip-active .canvas-chip-grip[data-v-cdd742c6]{color:#f97316}.canvas-chip-context[data-v-cdd742c6]{border-color:#fed7aa;background:#fffdfa}.canvas-chip[data-v-cdd742c6]:active{cursor:grabbing}.canvas-chip-grip[data-v-cdd742c6]{flex:0 0 auto;color:#9ca3af}.canvas-chip-label[data-v-cdd742c6]{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.canvas-chip-swap[data-v-cdd742c6]{display:grid;flex:0 0 auto;place-items:center;margin-left:2px;border-radius:6px;padding:2px;color:#9ca3af;opacity:0;transition:opacity .1s ease}.canvas-chip:hover .canvas-chip-swap[data-v-cdd742c6],.canvas-chip:focus-within .canvas-chip-swap[data-v-cdd742c6]{opacity:1}.canvas-chip-swap[data-v-cdd742c6]:hover{background:#fff;color:#f97316}.canvas-area-empty[data-v-cdd742c6]{display:grid;flex:1;place-items:center;border:1px dashed rgb(255 255 255 / 60%);border-radius:8px;background:transparent;color:#f3f4f6;font-size:11px;font-family:inherit;cursor:pointer}.canvas-area-empty[data-v-cdd742c6]:hover{border-color:#f97316;color:#f97316}@media (hover: none),(pointer: coarse){.canvas-edge[data-v-cdd742c6],.canvas-area-remove[data-v-cdd742c6],.canvas-chip-swap[data-v-cdd742c6]{opacity:1}}.canvas[data-v-eb790981]{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;min-height:0}.canvas-row[data-v-eb790981]{flex-direction:row}.canvas-column[data-v-eb790981]{flex-direction:column}.canvas-group[data-v-eb790981]{position:relative;display:flex;min-width:0;min-height:0}.canvas-divider[data-v-eb790981]{position:absolute;z-index:2;display:grid;place-items:center;touch-action:none}.divider-vertical[data-v-eb790981]{top:0;bottom:0;width:10px;transform:translate(-50%);cursor:col-resize}.divider-horizontal[data-v-eb790981]{left:0;right:0;height:10px;transform:translateY(-50%);cursor:row-resize}.canvas-divider-grip[data-v-eb790981]{border-radius:999px;background:#d1d5db}.divider-vertical .canvas-divider-grip[data-v-eb790981]{height:34px;width:3px}.divider-horizontal .canvas-divider-grip[data-v-eb790981]{height:3px;width:34px}.canvas-divider:hover .canvas-divider-grip[data-v-eb790981]{background:#f97316}.module-picker[data-v-2892f182]{display:flex;flex-direction:column;gap:18px}.module-picker-hint[data-v-2892f182]{margin:0;color:#6b7280;font-size:12px}.module-picker-group[data-v-2892f182]{display:flex;flex-direction:column;gap:8px}.module-picker-group-label[data-v-2892f182]{margin:0;color:#9ca3af;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.module-picker-grid[data-v-2892f182]{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px}.module-picker-card[data-v-2892f182]{display:flex;flex-direction:column;align-items:center;gap:8px;border:1px solid #e5e7eb;border-radius:10px;padding:14px 10px;background:#fff;color:#374151;font-size:13px;text-align:center;cursor:pointer}.module-picker-card[data-v-2892f182]:hover{border-color:#f97316;background:#fffbf5}.module-picker-icon[data-v-2892f182]{display:grid;height:36px;width:36px;place-items:center;border-radius:8px;background:#f3f4f6;color:#4b5563}.module-picker-label[data-v-2892f182]{line-height:1.3;overflow-wrap:anywhere}.media-field[data-v-839ff846]{display:flex;flex-direction:column;gap:6px}.media-label[data-v-839ff846]{color:#4b5563;font-size:12px}.media-thumb[data-v-839ff846]{height:72px;width:100%;border-radius:8px;border:1px solid #e5e7eb;-o-object-fit:cover;object-fit:cover}.media-empty[data-v-839ff846]{display:grid;height:72px;place-items:center;border:1px dashed #d1d5db;border-radius:8px;color:#9ca3af;font-size:12px}.media-warning[data-v-839ff846]{display:grid;height:72px;place-items:center;border:1px dashed #d1d5db;border-radius:8px;color:#b54708;font-size:12px}.media-actions[data-v-839ff846]{display:flex;gap:6px}.inspector[data-v-4cc442cf]{display:flex;flex-direction:column;gap:14px}.inspector-scope[data-v-4cc442cf]{align-self:stretch}.inspector-breadcrumb[data-v-4cc442cf]{display:flex;align-items:center;gap:2px;color:#6b7280;font-size:11px}.inspector-section[data-v-4cc442cf]{display:flex;flex-direction:column;gap:10px;border-top:1px solid #e5e7eb;padding-top:12px}.inspector-section-title[data-v-4cc442cf]{color:#9ca3af;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.inspector-note[data-v-4cc442cf]{color:#6b7280;font-size:11px;line-height:1.45}.inspector-warning[data-v-4cc442cf]{display:flex;align-items:flex-start;gap:6px;border-radius:8px;background:#fffbeb;padding:8px 10px;color:#92400e;font-size:12px}.inspector-list[data-v-4cc442cf]{display:flex;flex-direction:column;gap:8px}.inspector-list-head[data-v-4cc442cf]{display:flex;align-items:baseline;gap:6px}.inspector-list-count[data-v-4cc442cf]{border-radius:999px;background:#f3f4f6;padding:0 6px;color:#6b7280;font-size:10px;font-variant-numeric:tabular-nums}.inspector-item[data-v-4cc442cf]{display:flex;flex-direction:column;gap:10px;border:1px solid #e5e7eb;border-radius:10px;padding:10px;background:#fcfcfd}.inspector-item-head[data-v-4cc442cf]{display:flex;align-items:center;gap:2px}.inspector-item-name[data-v-4cc442cf]{flex:1;color:#6b7280;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.inspector-item-action[data-v-4cc442cf]{display:grid;flex:0 0 auto;place-items:center;border-radius:6px;padding:2px;color:#9ca3af}.inspector-item-action[data-v-4cc442cf]:hover:not(:disabled){background:#fff7ed;color:#f97316}.inspector-item-action[data-v-4cc442cf]:disabled{opacity:.35}.inspector-list-add[data-v-4cc442cf]{display:flex;gap:6px}.media-field[data-v-4cc442cf]{display:flex;flex-direction:column;gap:6px}.media-label[data-v-4cc442cf]{color:#4b5563;font-size:12px}.media-thumb[data-v-4cc442cf]{height:72px;width:100%;border-radius:8px;border:1px solid #e5e7eb;-o-object-fit:cover;object-fit:cover}.media-empty[data-v-4cc442cf]{display:grid;height:72px;place-items:center;border:1px dashed #d1d5db;border-radius:8px;color:#9ca3af;font-size:12px}.media-actions[data-v-4cc442cf]{display:flex;gap:6px}.editor-head[data-v-a673333e]{display:flex;shrink:0;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid #e5e7eb;padding:12px 20px}.editor-identity[data-v-a673333e]{display:flex;min-width:0;align-items:center;gap:10px}.editor-name[data-v-a673333e]{width:min(320px,40vw)}.editor-orientation[data-v-a673333e]{width:160px}.editor-error[data-v-a673333e]{flex-shrink:0;margin:12px 16px 0;border-radius:8px;background:#fef2f2;padding:8px 12px;color:#b91c1c;font-size:13px}.rail-scale[data-v-a673333e]{display:flex;flex-direction:column;gap:6px;margin-bottom:18px;border-bottom:1px solid #e5e7eb;padding-bottom:16px}.rail-scale-label[data-v-a673333e]{color:#4b5563;font-size:12px;font-weight:600}.rail-scale-hint[data-v-a673333e]{margin:0;color:#6b7280;font-size:11px;line-height:1.4}.rail-scale-warning[data-v-a673333e]{margin:0;border-radius:8px;padding:6px 8px;color:#9a3412;background:#fff7ed;font-size:11px;line-height:1.4}.editor-body[data-v-a673333e]{display:grid;min-height:0;flex:1;grid-template-columns:268px minmax(0,1fr) 320px}.editor-rail[data-v-a673333e],.editor-inspector[data-v-a673333e]{min-height:0;overflow:auto;padding:16px}.editor-rail[data-v-a673333e]{border-right:1px solid #e5e7eb;background:#f9fafb}.editor-inspector[data-v-a673333e]{border-left:1px solid #e5e7eb;background:#f9fafb}.editor-stage[data-v-a673333e]{display:flex;min-height:0;flex-direction:column;gap:12px;overflow:auto;padding:16px}.stage-preview[data-v-a673333e]{display:flex;min-height:0;flex:1;align-items:center;justify-content:center}.preview-box[data-v-a673333e]{position:relative;margin:0 auto}.preview-box.preview-landscape[data-v-a673333e]{width:100%;max-height:72vh;aspect-ratio:16 / 9}.preview-box.preview-portrait[data-v-a673333e]{height:72vh;max-width:100%;aspect-ratio:9 / 16}.preview-frame[data-v-a673333e]{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden;border:1px solid #e5e7eb;border-radius:12px;background:#101828}.preview-frame[data-v-a673333e]>*{width:100%;height:100%}.preview-edit-overlay[data-v-a673333e]{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;pointer-events:none;transition:opacity .15s ease}.preview-box:hover .preview-edit-overlay[data-v-a673333e],.preview-box:focus-within .preview-edit-overlay[data-v-a673333e],.preview-edit-overlay.preview-edit-overlay-active[data-v-a673333e]{opacity:1;pointer-events:auto}@media (hover: none),(pointer: coarse){.preview-edit-overlay[data-v-a673333e]{opacity:1;pointer-events:auto}}.layout-bar[data-v-a673333e]{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.layout-bar-title[data-v-a673333e]{display:flex;min-width:0;flex-direction:column;gap:2px}.layout-title[data-v-a673333e]{color:#111827;font-size:13px;font-weight:600}.layout-hint[data-v-a673333e]{color:#9ca3af;font-size:11px}.layout-actions[data-v-a673333e]{display:flex;flex-shrink:0;align-items:center;gap:6px}.layout-conflict[data-v-a673333e]{display:flex;align-items:flex-start;gap:6px;border-radius:8px;background:#fffbeb;padding:8px 10px;color:#92400e;font-size:12px;cursor:pointer}.preset-row[data-v-a673333e]{display:flex;flex-wrap:wrap;gap:8px;border-radius:10px;background:#f9fafb;padding:10px}.preset-card[data-v-a673333e]{display:flex;width:108px;flex-direction:column;gap:6px;border:1px solid #d1d5db;border-radius:10px;padding:8px;background:#fff;text-align:left}.preset-card[data-v-a673333e]:hover{border-color:#f97316;background:#fff7ed}.preset-diagram[data-v-a673333e]{display:flex;height:34px;gap:3px;border-radius:6px;background:#f3f4f6;padding:3px}.preset-row-dir[data-v-a673333e]{flex-direction:row}.preset-column-dir[data-v-a673333e]{flex-direction:column}.preset-cell[data-v-a673333e]{flex-basis:0;border-radius:4px;background:#fdba74}.preset-label[data-v-a673333e]{color:#374151;font-size:11px;line-height:1.3}@media (max-width: 1280px){.editor-body[data-v-a673333e]{grid-template-columns:220px minmax(0,1fr)}.editor-inspector[data-v-a673333e]{grid-column:1 / -1;border-left:0;border-top:1px solid #e5e7eb}}.layout-split[data-v-5595b416]{display:flex;min-width:0;min-height:0;flex:1;gap:6px}.layout-row[data-v-5595b416]{flex-direction:row}.layout-column[data-v-5595b416]{flex-direction:column}.layout-zone[data-v-5595b416]{display:flex;min-width:0;min-height:36px;flex:1;flex-direction:row;align-items:center;gap:8px;overflow:hidden;border:1.5px dashed #d1d5db;border-radius:10px;padding:6px 10px;background:#fff;text-align:left;transition:border-color .12s ease,background .12s ease}.layout-zone[data-v-5595b416]:hover{border-color:#fdba74;background:#fffbf5}.layout-zone-selected[data-v-5595b416]{border-style:solid;border-color:#f97316;background:#fff7ed;box-shadow:inset 0 0 0 1px #f97316}.layout-zone-index[data-v-5595b416]{flex:none;color:#6b7280;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.layout-zone-blocks[data-v-5595b416]{display:flex;min-width:0;gap:4px}.layout-zone-chip[data-v-5595b416]{display:inline-flex;max-width:100%;align-items:center;gap:4px;border-radius:999px;background:#f3f4f6;padding:3px 8px;color:#374151;font-size:11px}.display-toolbar[data-v-a2dda95a]{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid #e5e7eb;padding-bottom:14px}.display-controls[data-v-a2dda95a]{display:flex;align-items:center;justify-content:space-between;gap:16px;padding-top:12px}.display-restaurant-select[data-v-a2dda95a]{width:min(360px,100%)}.display-outlet[data-v-a2dda95a]{display:flex;align-items:center;gap:16px}.display-entitlement[data-v-a2dda95a]{color:#6b7280;font-size:12px;white-space:nowrap}.restriction-note[data-v-a2dda95a]{margin-top:12px;border:1px solid #fed7aa;border-radius:10px;padding:10px 12px;color:#9a3412;background:#fff7ed;font-size:13px}.empty-state[data-v-a2dda95a]{display:grid;justify-items:center;gap:6px;border:1px dashed #d1d5db;border-radius:12px;padding:56px 20px;color:#6b7280;text-align:center}.empty-state strong[data-v-a2dda95a]{color:#111827}.layout-thumb[data-v-a2dda95a]{display:flex;height:auto;min-height:96px;margin-top:16px;border-radius:10px;background:#f3f4f6;padding:5px;pointer-events:none}.thumb-caption[data-v-a2dda95a]{margin-top:8px;color:#6b7280;font-size:11px}.resource-card[data-v-a2dda95a]{border:1px solid #e5e7eb;border-radius:12px;padding:20px;background:#fff;box-shadow:0 2px 8px #0000000a}.mode-badge[data-v-a2dda95a],.status-badge[data-v-a2dda95a]{border-radius:999px;padding:3px 8px;background:#f3f4f6;font-size:11px;font-weight:700}.status-badge.active[data-v-a2dda95a]{color:#166534;background:#dcfce7}.status-badge.disabled[data-v-a2dda95a]{color:#991b1b;background:#fee2e2}.status-badge.unlicensed[data-v-a2dda95a]{color:#b42318;background:#fef3f2;border:1px solid #fecdca}.online-dot[data-v-a2dda95a]{color:#6b7280;font-size:11px}.online-dot[data-v-a2dda95a]:before{display:inline-block;width:7px;height:7px;margin-right:5px;border-radius:50%;background:#9ca3af;content:""}.online-dot.online[data-v-a2dda95a]{color:#15803d}.online-dot.online[data-v-a2dda95a]:before{background:#22c55e}.meta-grid[data-v-a2dda95a]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:18px;border-top:1px solid #f0f0f0;padding-top:16px}.meta-grid dt[data-v-a2dda95a]{color:#6b7280;font-size:11px}.meta-grid dd[data-v-a2dda95a]{overflow:hidden;margin-top:2px;font-size:12px;text-overflow:ellipsis;white-space:nowrap}@media (max-width:640px){.display-toolbar[data-v-a2dda95a]{align-items:flex-start}.display-controls[data-v-a2dda95a]{align-items:stretch;flex-direction:column}.display-outlet[data-v-a2dda95a]{align-items:stretch;flex-direction:column;gap:8px}.display-restaurant-select[data-v-a2dda95a]{width:100%}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feedmepos/mf-order-setting",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.91",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "./dist/app.js",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -48,12 +48,14 @@
|
|
|
48
48
|
"@vitejs/plugin-vue": "^4.3.4",
|
|
49
49
|
"@vue/eslint-config-prettier": "^8.0.0",
|
|
50
50
|
"@vue/eslint-config-typescript": "^12.0.0",
|
|
51
|
+
"@vue/test-utils": "^2.4.6",
|
|
51
52
|
"@vue/tsconfig": "^0.4.0",
|
|
52
53
|
"autoprefixer": "^10.4.16",
|
|
53
54
|
"cross-env": "^7.0.3",
|
|
54
55
|
"dotenv-cli": "^7.4.2",
|
|
55
56
|
"eslint": "^8.49.0",
|
|
56
57
|
"eslint-plugin-vue": "^9.17.0",
|
|
58
|
+
"happy-dom": "^9.20.3",
|
|
57
59
|
"npm-run-all2": "^6.0.6",
|
|
58
60
|
"postcss": "^8.4.31",
|
|
59
61
|
"prettier": "^3.0.3",
|
package/src/locales/en-US.json
CHANGED
|
@@ -125,6 +125,8 @@
|
|
|
125
125
|
"bindDevice": "Bind device",
|
|
126
126
|
"bindDeviceFailed": "Couldn't start device binding. Please try again.",
|
|
127
127
|
"bindDeviceMessage": "Enter this code to your kiosk device to bind.",
|
|
128
|
+
"bindDeviceNetworkError": "Couldn't reach the server. Check your internet connection and try again.",
|
|
129
|
+
"bindDeviceNoQuota": "No kiosk license left for this business. Add kiosk devices in the license portal, or unbind a kiosk you no longer use.",
|
|
128
130
|
"bindDevicePermissionDenied": "You don't have permission to bind devices.",
|
|
129
131
|
"cancel": "Cancel",
|
|
130
132
|
"cancelResetPin": "Cancel reset PIN",
|
|
@@ -724,21 +726,23 @@
|
|
|
724
726
|
},
|
|
725
727
|
"ui": {
|
|
726
728
|
"splitAreaHint": "Split this area {direction}",
|
|
727
|
-
"mergeGroupHint": "Merge back into one area",
|
|
728
729
|
"splitLimit": "An area inside a group cannot be split again.",
|
|
729
730
|
"moveItemUp": "Move up",
|
|
730
731
|
"moveItemDown": "Move down",
|
|
731
732
|
"removeItem": "Remove slide",
|
|
732
733
|
"presetAds": "Board + ads",
|
|
733
|
-
"emptyArea": "
|
|
734
|
+
"emptyArea": "Click to add a module",
|
|
735
|
+
"pickModuleTitle": "Choose a module",
|
|
736
|
+
"pickModuleHint": "Pick what this area should show.",
|
|
737
|
+
"replaceModuleConfirmTitle": "Replace this area's module?",
|
|
738
|
+
"replaceModuleConfirmMessage": "This area already has {current}. Adding a new module replaces it, and the current one cannot be recovered.",
|
|
739
|
+
"replaceModuleConfirmAction": "Replace",
|
|
734
740
|
"areaNeedsModule": "{area} has no module yet. Add one before saving.",
|
|
735
741
|
"sectionContent": "Content",
|
|
736
742
|
"sectionAnnounce": "Announcements",
|
|
737
743
|
"inspectorArea": "Area",
|
|
738
744
|
"inspectorModule": "Module",
|
|
739
745
|
"inspectorEmpty": "Pick an area or a module on the canvas.",
|
|
740
|
-
"paletteTitle": "Modules",
|
|
741
|
-
"paletteHint": "Drag onto an area, or click to add to the selected area.",
|
|
742
746
|
"uiScale": "UI scale",
|
|
743
747
|
"uiScaleHint": "Shrinks every element on the TV so more orders and queue numbers fit. A screen can override this.",
|
|
744
748
|
"hideFooter": "Hide the FeedMe footer",
|
|
@@ -749,7 +753,10 @@
|
|
|
749
753
|
"directionColumn": "Stacked",
|
|
750
754
|
"resetLayout": "Start from a layout",
|
|
751
755
|
"areaOnly": "Only area",
|
|
756
|
+
"removeAreaConfirmTitle": "Remove this area?",
|
|
757
|
+
"removeAreaConfirmMessage": "This area and its configured module will be deleted and cannot be recovered.",
|
|
752
758
|
"removeAreaTooltip": "Remove this area",
|
|
759
|
+
"changeModuleTooltip": "Change module",
|
|
753
760
|
"splitArea": "Split this area",
|
|
754
761
|
"templateName": "Template name",
|
|
755
762
|
"orientation": "Screen orientation",
|
package/src/locales/zh-CN.json
CHANGED
|
@@ -672,21 +672,23 @@
|
|
|
672
672
|
},
|
|
673
673
|
"ui": {
|
|
674
674
|
"splitAreaHint": "把这个区域{direction}拆成两个",
|
|
675
|
-
"mergeGroupHint": "合并回一个区域",
|
|
676
675
|
"splitLimit": "组内的区域不能再拆分。",
|
|
677
676
|
"moveItemUp": "上移",
|
|
678
677
|
"moveItemDown": "下移",
|
|
679
678
|
"removeItem": "删除这一项",
|
|
680
679
|
"presetAds": "看板 + 广告",
|
|
681
|
-
"emptyArea": "
|
|
680
|
+
"emptyArea": "点击添加模块",
|
|
681
|
+
"pickModuleTitle": "选择一个模块",
|
|
682
|
+
"pickModuleHint": "选一个这个区域要展示的内容。",
|
|
683
|
+
"replaceModuleConfirmTitle": "替换这个区域的模块?",
|
|
684
|
+
"replaceModuleConfirmMessage": "这个区域已经有「{current}」了,新增模块会直接替换掉它,替换后无法恢复。",
|
|
685
|
+
"replaceModuleConfirmAction": "替换",
|
|
682
686
|
"areaNeedsModule": "{area}还没有模块,保存前请先添加一个。",
|
|
683
687
|
"sectionContent": "内容",
|
|
684
688
|
"sectionAnnounce": "语音播报",
|
|
685
689
|
"inspectorArea": "区域",
|
|
686
690
|
"inspectorModule": "模块",
|
|
687
691
|
"inspectorEmpty": "在画布上选择一个区域或模块。",
|
|
688
|
-
"paletteTitle": "模块",
|
|
689
|
-
"paletteHint": "拖到某个区域,或点击加入当前选中的区域。",
|
|
690
692
|
"uiScale": "界面缩放",
|
|
691
693
|
"uiScaleHint": "等比缩小电视上的所有元素,从而放下更多订单和排队号码。单个显示屏可以覆盖此设置。",
|
|
692
694
|
"hideFooter": "隐藏 FeedMe 页脚",
|
|
@@ -697,7 +699,10 @@
|
|
|
697
699
|
"directionColumn": "上下堆叠",
|
|
698
700
|
"resetLayout": "套用起步版面",
|
|
699
701
|
"areaOnly": "唯一区域",
|
|
702
|
+
"removeAreaConfirmTitle": "删除这个区域?",
|
|
703
|
+
"removeAreaConfirmMessage": "该区域及已配置的模块将被删除,删除后无法恢复。",
|
|
700
704
|
"removeAreaTooltip": "删除该区域",
|
|
705
|
+
"changeModuleTooltip": "更换模块",
|
|
701
706
|
"splitArea": "拆分该区域",
|
|
702
707
|
"templateName": "模板名称",
|
|
703
708
|
"orientation": "屏幕方向",
|
|
@@ -2,7 +2,7 @@ import { DISPLAY_BLOCK_DEFINITIONS } from '@entity'
|
|
|
2
2
|
import { reactive } from 'vue'
|
|
3
3
|
import { describe, expect, it } from 'vitest'
|
|
4
4
|
|
|
5
|
-
import { addArea, clearBlock, crossDirection, defaultBlock, defaultDisplayTemplateDraft, displayTemplatePayload,
|
|
5
|
+
import { addArea, addAreaBeside, applyDividerRatio, setLayoutDirection, clearBlock, crossDirection, defaultBlock, defaultDisplayTemplateDraft, displayTemplatePayload, removeArea, setBlock, splitArea, swapBlocks, templateCapabilities, type DisplaySplitDraft, unlicensedScreenIds, walkZones, zoneFitsMinimum, zoneParent } from './interface'
|
|
6
6
|
|
|
7
7
|
describe('display template drafts', () => {
|
|
8
8
|
it('serializes a reactive editor draft into an API payload', () => {
|
|
@@ -43,6 +43,80 @@ describe('display template drafts', () => {
|
|
|
43
43
|
expect(templateCapabilities(draft)).toEqual(['PICKUP', 'MEDIA'])
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
+
it('rotates every nested group across its parent without changing content or weights', () => {
|
|
47
|
+
const draft = defaultDisplayTemplateDraft('ads')
|
|
48
|
+
const root = draft.root as DisplaySplitDraft
|
|
49
|
+
splitArea(draft, 'main_zone', defaultBlock('info.text'))
|
|
50
|
+
const original = structuredClone(draft.root)
|
|
51
|
+
for (const direction of ['COLUMN', 'ROW'] as const) {
|
|
52
|
+
setLayoutDirection(root, direction)
|
|
53
|
+
expect(root.direction).toBe(direction)
|
|
54
|
+
for (const child of root.children) {
|
|
55
|
+
expect((child.node as DisplaySplitDraft).direction).toBe(crossDirection(direction))
|
|
56
|
+
}
|
|
57
|
+
expect(walkZones(root)).toEqual(walkZones(original))
|
|
58
|
+
expect(root.children.map((child) => child.weight)).toEqual([700, 300])
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('adds top-level areas after a trailing group up to the existing cap', () => {
|
|
63
|
+
const draft = defaultDisplayTemplateDraft('ads')
|
|
64
|
+
const root = draft.root as DisplaySplitDraft
|
|
65
|
+
const original = structuredClone(walkZones(root))
|
|
66
|
+
for (let count = 2; count < 6; count += 1) {
|
|
67
|
+
expect(addArea(draft, 'ROW', defaultBlock('info.text'))).toBeTruthy()
|
|
68
|
+
}
|
|
69
|
+
expect(root.children).toHaveLength(6)
|
|
70
|
+
expect(addArea(draft, 'ROW')).toBeUndefined()
|
|
71
|
+
expect(walkZones(root).slice(0, 3)).toEqual(original)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('funds an added top-level area from the last slot, preserving all other weights', () => {
|
|
75
|
+
const draft = defaultDisplayTemplateDraft('ads')
|
|
76
|
+
const root = draft.root as DisplaySplitDraft
|
|
77
|
+
addArea(draft, 'ROW')
|
|
78
|
+
expect(root.children.map((child) => child.weight)).toEqual([700, 150, 150])
|
|
79
|
+
expect((root.children[1]!.node as DisplaySplitDraft).children.map((child) => child.weight)).toEqual([500, 500])
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('funds a nested addition from the clicked area and keeps sibling proportions', () => {
|
|
83
|
+
const draft = defaultDisplayTemplateDraft('ads')
|
|
84
|
+
const group = zoneParent(draft.root, 'promo_zone')!.split
|
|
85
|
+
group.children[0]!.weight = 800
|
|
86
|
+
group.children[1]!.weight = 200
|
|
87
|
+
addAreaBeside(draft, 'promo_zone')
|
|
88
|
+
expect(group.children.map((child) => child.weight)).toEqual([400, 200, 400])
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('keeps valid positive integer weights when splitting a one-unit slot', () => {
|
|
92
|
+
const draft = defaultDisplayTemplateDraft('mixed')
|
|
93
|
+
const root = draft.root as DisplaySplitDraft
|
|
94
|
+
root.children[0]!.weight = 999
|
|
95
|
+
root.children[1]!.weight = 1
|
|
96
|
+
addArea(draft, 'ROW')
|
|
97
|
+
expect(root.children.map((child) => child.weight)).toEqual([999, 1, 1])
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('removes only the clicked area without equalizing remaining siblings', () => {
|
|
101
|
+
const draft = defaultDisplayTemplateDraft('mixed')
|
|
102
|
+
addArea(draft, 'ROW')
|
|
103
|
+
const root = draft.root as DisplaySplitDraft
|
|
104
|
+
root.children.forEach((child, index) => { child.weight = [600, 300, 100][index]! })
|
|
105
|
+
removeArea(draft, walkZones(root)[2]!.id)
|
|
106
|
+
expect(root.children.map((child) => child.weight)).toEqual([600, 300])
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('resizes only the adjacent pair and preserves its total weight', () => {
|
|
110
|
+
const draft = defaultDisplayTemplateDraft('mixed')
|
|
111
|
+
addArea(draft, 'ROW')
|
|
112
|
+
const root = draft.root as DisplaySplitDraft
|
|
113
|
+
root.children.forEach((child, index) => { child.weight = [333, 333, 334][index]! })
|
|
114
|
+
applyDividerRatio(root.children, 1, 333 / 667)
|
|
115
|
+
expect(root.children.map((child) => child.weight)).toEqual([333, 333, 334])
|
|
116
|
+
applyDividerRatio(root.children, 1, 0.6)
|
|
117
|
+
expect(root.children.map((child) => child.weight)).toEqual([333, 400, 267])
|
|
118
|
+
})
|
|
119
|
+
|
|
46
120
|
it('splits a top-level area across its parent and refuses a third level', () => {
|
|
47
121
|
const draft = defaultDisplayTemplateDraft('sidebar')
|
|
48
122
|
const [, side] = walkZones(draft.root)
|
|
@@ -57,6 +131,18 @@ describe('display template drafts', () => {
|
|
|
57
131
|
expect(splitArea(draft, created!, defaultBlock('info.text'))).toBeUndefined()
|
|
58
132
|
})
|
|
59
133
|
|
|
134
|
+
it('grows a nested group past two areas, up to the same cap as the top level', () => {
|
|
135
|
+
const draft = defaultDisplayTemplateDraft('sidebar')
|
|
136
|
+
const [, side] = walkZones(draft.root)
|
|
137
|
+
const created = splitArea(draft, side!.id, defaultBlock('media.carousel'))!
|
|
138
|
+
|
|
139
|
+
for (let count = 0; count < 4; count += 1) {
|
|
140
|
+
expect(addAreaBeside(draft, created, defaultBlock('info.text'))).toBeTruthy()
|
|
141
|
+
}
|
|
142
|
+
expect(zoneParent(draft.root, created)?.split.children).toHaveLength(6)
|
|
143
|
+
expect(addAreaBeside(draft, created, defaultBlock('info.text'))).toBeUndefined()
|
|
144
|
+
})
|
|
145
|
+
|
|
60
146
|
it('collapses a group when it drops to one area', () => {
|
|
61
147
|
const draft = defaultDisplayTemplateDraft('sidebar')
|
|
62
148
|
const [, side] = walkZones(draft.root)
|
|
@@ -68,14 +154,13 @@ describe('display template drafts', () => {
|
|
|
68
154
|
expect(walkZones(draft.root)).toHaveLength(2)
|
|
69
155
|
})
|
|
70
156
|
|
|
71
|
-
it('
|
|
157
|
+
it('removing one area of a group keeps the other, module and all', () => {
|
|
72
158
|
const draft = defaultDisplayTemplateDraft('ads')
|
|
73
159
|
const [, promo, ads] = walkZones(draft.root)
|
|
74
160
|
|
|
75
|
-
expect(
|
|
161
|
+
expect(removeArea(draft, promo!.id)).toBe(true)
|
|
76
162
|
const remaining = walkZones(draft.root)
|
|
77
|
-
expect(remaining.
|
|
78
|
-
expect(remaining.some((zone) => zone.id === promo!.id)).toBe(false)
|
|
163
|
+
expect(remaining).toEqual([expect.objectContaining({ id: 'main_zone' }), ads])
|
|
79
164
|
})
|
|
80
165
|
|
|
81
166
|
it('collapses the top level when only one area is left', () => {
|
|
@@ -316,11 +316,13 @@ function newZone(block?: DisplayBlockDraft): DisplayZoneDraft {
|
|
|
316
316
|
return { kind: 'ZONE', id: uniqueLayoutId('zone'), block }
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
319
|
+
/** Take the new area's share from its neighbor, keeping every other sibling's
|
|
320
|
+
* weight. At the one-unit minimum both areas need one unit to remain valid. */
|
|
321
|
+
function appendArea(split: DisplaySplitDraft, index: number, node: DisplayZoneDraft): void {
|
|
322
|
+
const neighbor = split.children[index]!
|
|
323
|
+
const share = Math.max(1, Math.floor(neighbor.weight / 2))
|
|
324
|
+
neighbor.weight = Math.max(1, neighbor.weight - share)
|
|
325
|
+
split.children.push({ weight: share, node })
|
|
324
326
|
}
|
|
325
327
|
|
|
326
328
|
/** Direction a container nested inside `direction` must run. Same-direction
|
|
@@ -331,6 +333,14 @@ export function crossDirection(
|
|
|
331
333
|
return direction === 'ROW' ? 'COLUMN' : 'ROW'
|
|
332
334
|
}
|
|
333
335
|
|
|
336
|
+
/** Rotate a layout while preserving the API's alternating container directions. */
|
|
337
|
+
export function setLayoutDirection(split: DisplaySplitDraft, direction: DisplaySplitDraft['direction']): void {
|
|
338
|
+
split.direction = direction
|
|
339
|
+
for (const child of split.children) {
|
|
340
|
+
if (child.node.kind === 'SPLIT') setLayoutDirection(child.node, crossDirection(direction))
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
334
344
|
export const DISPLAY_CONTAINER_CHILD_LIMIT = 6
|
|
335
345
|
|
|
336
346
|
/** Turns a single-area template into a two-area split, or adds one more area to
|
|
@@ -353,8 +363,22 @@ export function addArea(
|
|
|
353
363
|
return created.id
|
|
354
364
|
}
|
|
355
365
|
if (draft.root.children.length >= DISPLAY_CONTAINER_CHILD_LIMIT) return undefined
|
|
356
|
-
draft.root.children.
|
|
357
|
-
|
|
366
|
+
appendArea(draft.root, draft.root.children.length - 1, created)
|
|
367
|
+
return created.id
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Adds a sibling to whichever container holds `zoneId` -- the top-level split
|
|
371
|
+
* or a nested group -- up to the same limit as the top level. Returns the id
|
|
372
|
+
* of the area that was created. */
|
|
373
|
+
export function addAreaBeside(
|
|
374
|
+
draft: DisplayTemplateDraft,
|
|
375
|
+
zoneId: string,
|
|
376
|
+
block?: DisplayBlockDraft
|
|
377
|
+
): string | undefined {
|
|
378
|
+
const parent = zoneParent(draft.root, zoneId)
|
|
379
|
+
if (!parent || parent.split.children.length >= DISPLAY_CONTAINER_CHILD_LIMIT) return undefined
|
|
380
|
+
const created = newZone(block)
|
|
381
|
+
appendArea(parent.split, parent.index, created)
|
|
358
382
|
return created.id
|
|
359
383
|
}
|
|
360
384
|
|
|
@@ -395,7 +419,6 @@ export function removeArea(draft: DisplayTemplateDraft, zoneId: string): boolean
|
|
|
395
419
|
if (remaining.length === parent.split.children.length) return false
|
|
396
420
|
if (remaining.length >= 2) {
|
|
397
421
|
parent.split.children = remaining
|
|
398
|
-
normalizeWeights(parent.split)
|
|
399
422
|
return true
|
|
400
423
|
}
|
|
401
424
|
// A container with one child is not a container: collapse it into whatever
|
|
@@ -413,19 +436,6 @@ export function removeArea(draft: DisplayTemplateDraft, zoneId: string): boolean
|
|
|
413
436
|
return true
|
|
414
437
|
}
|
|
415
438
|
|
|
416
|
-
/** Collapses the nested container holding `zoneId` back into a single area,
|
|
417
|
-
* keeping that area's module. Returns the id of the area that remains. */
|
|
418
|
-
export function mergeGroup(draft: DisplayTemplateDraft, zoneId: string): string | undefined {
|
|
419
|
-
const parent = zoneParent(draft.root, zoneId)
|
|
420
|
-
if (!parent || parent.depth !== 2) return undefined
|
|
421
|
-
const kept = parent.split.children[parent.index]!.node as DisplayZoneDraft
|
|
422
|
-
const outer = draft.root as DisplaySplitDraft
|
|
423
|
-
const slot = outer.children.find((child) => child.node === parent.split)
|
|
424
|
-
if (!slot) return undefined
|
|
425
|
-
slot.node = kept
|
|
426
|
-
return kept.id
|
|
427
|
-
}
|
|
428
|
-
|
|
429
439
|
// --- Module operations ----------------------------------------------------
|
|
430
440
|
|
|
431
441
|
function findZone(node: DisplayLayoutDraft, zoneId: string): DisplayZoneDraft | undefined {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { KIOSK_LICENSE_QUOTA_EXHAUSTED, bindDeviceErrorKey } from './bindDeviceError'
|
|
3
|
+
|
|
4
|
+
describe('bindDeviceErrorKey', () => {
|
|
5
|
+
it('explains a missing kiosk license from the error code', () => {
|
|
6
|
+
expect(
|
|
7
|
+
bindDeviceErrorKey({
|
|
8
|
+
status: 400,
|
|
9
|
+
code: KIOSK_LICENSE_QUOTA_EXHAUSTED,
|
|
10
|
+
message: 'No quota available for binding'
|
|
11
|
+
})
|
|
12
|
+
).toBe('order.bindDeviceNoQuota')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('explains a missing kiosk license from the message when the backend sends no code', () => {
|
|
16
|
+
expect(
|
|
17
|
+
bindDeviceErrorKey({ status: 400, message: 'No quota available for binding' })
|
|
18
|
+
).toBe('order.bindDeviceNoQuota')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('reports a permission problem for 403, even if the text mentions quota', () => {
|
|
22
|
+
expect(
|
|
23
|
+
bindDeviceErrorKey({ status: 403, message: 'No quota available for binding' })
|
|
24
|
+
).toBe('order.bindDevicePermissionDenied')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('reports a network problem when no response came back', () => {
|
|
28
|
+
expect(bindDeviceErrorKey({ status: undefined, message: 'Network Error' })).toBe(
|
|
29
|
+
'order.bindDeviceNetworkError'
|
|
30
|
+
)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('falls back to the generic message for a server error', () => {
|
|
34
|
+
expect(bindDeviceErrorKey({ status: 500, message: 'Internal Server Error' })).toBe(
|
|
35
|
+
'order.bindDeviceFailed'
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('falls back to the generic message for an error that is not from the API', () => {
|
|
40
|
+
expect(bindDeviceErrorKey(null)).toBe('order.bindDeviceFailed')
|
|
41
|
+
})
|
|
42
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error code kiosk-backend puts on the 400 it returns when the business has no
|
|
3
|
+
* free, unexpired kiosk license (see KIOSK_LICENSE_QUOTA_EXHAUSTED in
|
|
4
|
+
* backend/apps/kiosk-backend/src/kiosk/kiosk.service.ts).
|
|
5
|
+
*/
|
|
6
|
+
export const KIOSK_LICENSE_QUOTA_EXHAUSTED = 'KIOSK_LICENSE_QUOTA_EXHAUSTED'
|
|
7
|
+
|
|
8
|
+
export type BindDeviceErrorKey =
|
|
9
|
+
| 'order.bindDevicePermissionDenied'
|
|
10
|
+
| 'order.bindDeviceNoQuota'
|
|
11
|
+
| 'order.bindDeviceNetworkError'
|
|
12
|
+
| 'order.bindDeviceFailed'
|
|
13
|
+
|
|
14
|
+
/** The parts of an `SvcCompanyApiError` that decide which message to show. */
|
|
15
|
+
export interface BindDeviceApiError {
|
|
16
|
+
status?: number
|
|
17
|
+
code?: string
|
|
18
|
+
message?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Picks the message for a failed "Bind device" (bind code request), so the
|
|
23
|
+
* merchant learns WHY it failed instead of a bare "please try again".
|
|
24
|
+
*
|
|
25
|
+
* `apiError` is null when the failure was not an API error at all.
|
|
26
|
+
*/
|
|
27
|
+
export function bindDeviceErrorKey(apiError: BindDeviceApiError | null): BindDeviceErrorKey {
|
|
28
|
+
if (!apiError) return 'order.bindDeviceFailed'
|
|
29
|
+
if (apiError.status === 403) return 'order.bindDevicePermissionDenied'
|
|
30
|
+
// The message check covers a kiosk-backend that does not send the code yet.
|
|
31
|
+
if (
|
|
32
|
+
apiError.code === KIOSK_LICENSE_QUOTA_EXHAUSTED ||
|
|
33
|
+
/no quota available/i.test(apiError.message ?? '')
|
|
34
|
+
) {
|
|
35
|
+
return 'order.bindDeviceNoQuota'
|
|
36
|
+
}
|
|
37
|
+
// An API error with no HTTP status never got a response: offline, DNS, CORS.
|
|
38
|
+
if (apiError.status === undefined) return 'order.bindDeviceNetworkError'
|
|
39
|
+
return 'order.bindDeviceFailed'
|
|
40
|
+
}
|