parent
dc215addb4
commit
a95e7bbb13
7 changed files with 124 additions and 71 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
|
@ -44,7 +44,7 @@
|
|||
"prettier-plugin-svelte": "^3.5.1",
|
||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
||||
"storybook": "^10.3.5",
|
||||
"svelte": "^5.55.2",
|
||||
"svelte": "^5.56.3",
|
||||
"svelte-adapter-bun": "^1.0.1",
|
||||
"svelte-check": "^4.4.6",
|
||||
"svelte-sonner": "^1.1.0",
|
||||
|
|
|
|||
|
|
@ -83,12 +83,12 @@
|
|||
errors: []
|
||||
});
|
||||
|
||||
handleIncomingMessages(
|
||||
JSON.stringify({
|
||||
type: 'chat',
|
||||
payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has connected to ${boxid}`
|
||||
})
|
||||
);
|
||||
// handleIncomingMessages(
|
||||
// JSON.stringify({
|
||||
// type: 'chat',
|
||||
// payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has connected to ${boxid}`
|
||||
// })
|
||||
// );
|
||||
} else {
|
||||
machineStatus = 'Instance lost, try disconnect and re-connect again';
|
||||
toast.error('Unexpected Error');
|
||||
|
|
@ -221,12 +221,12 @@
|
|||
|
||||
connectDeviceOk = false;
|
||||
|
||||
handleIncomingMessages(
|
||||
JSON.stringify({
|
||||
type: 'chat',
|
||||
payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has disconnected!`
|
||||
})
|
||||
);
|
||||
// handleIncomingMessages(
|
||||
// JSON.stringify({
|
||||
// type: 'chat',
|
||||
// payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has disconnected!`
|
||||
// })
|
||||
// );
|
||||
}
|
||||
|
||||
function checkDeviceConnection() {
|
||||
|
|
|
|||
|
|
@ -59,16 +59,16 @@ async function handleAdbPayload(raw_payload: string) {
|
|||
}
|
||||
|
||||
if (raw_payload.startsWith('save_recipe_machine')) {
|
||||
handleIncomingMessages(
|
||||
JSON.stringify({
|
||||
type: 'ui_action',
|
||||
payload: {
|
||||
action: uiAction,
|
||||
from: 'brew',
|
||||
ref: `${pd}.${action}`
|
||||
}
|
||||
})
|
||||
);
|
||||
// handleIncomingMessages(
|
||||
// JSON.stringify({
|
||||
// type: 'ui_action',
|
||||
// payload: {
|
||||
// action: uiAction,
|
||||
// from: 'brew',
|
||||
// ref: `${pd}.${action}`
|
||||
// }
|
||||
// })
|
||||
// );
|
||||
}
|
||||
} else if (raw_payload.startsWith('state')) {
|
||||
let res = raw_payload.split('/');
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@
|
|||
await loadAvailableProductCodes();
|
||||
|
||||
// Enter room to get lock
|
||||
const entered = enterRoom(country, catalog);
|
||||
const entered = await enterRoom(country, catalog);
|
||||
if (entered) {
|
||||
addNotification(`INFO:Entered ${getCatalogDisplayName(catalog)} for adding menu`);
|
||||
|
||||
|
|
@ -815,13 +815,15 @@
|
|||
<button
|
||||
type="button"
|
||||
onclick={() => selectCode(item.code)}
|
||||
class="flex w-full items-center justify-between rounded-lg border bg-card p-3 text-left transition-colors hover:border-primary/50 hover:bg-primary/5 {item.isNew ? 'border-green-500/50 bg-green-500/5' : ''}"
|
||||
class="flex w-full items-center justify-between rounded-lg border bg-card p-3 text-left transition-colors hover:border-primary/50 hover:bg-primary/5 {item.isNew
|
||||
? 'border-green-500/50 bg-green-500/5'
|
||||
: ''}"
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-mono text-sm">{item.code}</span>
|
||||
{#if item.isNew}
|
||||
<Badge class="bg-green-600 hover:bg-green-600 text-[10px] px-1.5 py-0">NEW</Badge>
|
||||
<Badge class="bg-green-600 px-1.5 py-0 text-[10px] hover:bg-green-600">NEW</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
{#if item.name}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,8 @@
|
|||
const primaryLanguageKey = columnConfig.primaryLanguage;
|
||||
const primaryLanguageColumn = languageColumnMap[primaryLanguageKey] ?? languageColumnMap.en ?? 3;
|
||||
const secondaryLanguageKey = primaryLanguageKey === 'en' ? 'th' : 'en';
|
||||
const secondaryLanguageColumn = languageColumnMap[secondaryLanguageKey] ?? languageColumnMap.en ?? 3;
|
||||
const secondaryLanguageColumn =
|
||||
languageColumnMap[secondaryLanguageKey] ?? languageColumnMap.en ?? 3;
|
||||
|
||||
function getCatalogDisplayName(catalogName: string): string {
|
||||
const match = catalogName.match(/page_catalog_group_(\w+)\.skt/);
|
||||
|
|
@ -797,7 +798,11 @@
|
|||
}
|
||||
|
||||
// Get all prices for an item (hot, cold, blend)
|
||||
function getItemPrices(item: any): { hot: string | null; cold: string | null; blend: string | null } {
|
||||
function getItemPrices(item: any): {
|
||||
hot: string | null;
|
||||
cold: string | null;
|
||||
blend: string | null;
|
||||
} {
|
||||
return {
|
||||
hot: getItemPrice(item, 'hot'),
|
||||
cold: getItemPrice(item, 'cold'),
|
||||
|
|
@ -807,18 +812,23 @@
|
|||
|
||||
// Price edit dialog state
|
||||
let priceEditDialogOpen = $state(false);
|
||||
let priceEditData = $state<{ code: string; type: string; price: string; row: number; col: number; isNew: boolean }[]>([]);
|
||||
let priceEditData = $state<
|
||||
{ code: string; type: string; price: string; row: number; col: number; isNew: boolean }[]
|
||||
>([]);
|
||||
let savingPrice = $state(false);
|
||||
|
||||
// Get price info for a single product code
|
||||
function getPriceInfoForCode(productCode: string): { price: string; row: number; col: number } | null {
|
||||
function getPriceInfoForCode(
|
||||
productCode: string
|
||||
): { price: string; row: number; col: number } | null {
|
||||
const priceCells = sheetPrices[productCode];
|
||||
if (!priceCells || priceCells.length === 0) return null;
|
||||
|
||||
const headers = get(sheetPriceHeader)[countryCode];
|
||||
if (!headers || headers.length === 0) return null;
|
||||
|
||||
const headerNames = PRICE_HEADER_NAMES_BY_COUNTRY[countryCode] || PRICE_HEADER_NAMES_BY_COUNTRY.default;
|
||||
const headerNames =
|
||||
PRICE_HEADER_NAMES_BY_COUNTRY[countryCode] || PRICE_HEADER_NAMES_BY_COUNTRY.default;
|
||||
const colIdx = findHeaderIndex(headers, headerNames.cash_price);
|
||||
if (colIdx < 0) return null;
|
||||
|
||||
|
|
@ -848,7 +858,14 @@
|
|||
function openPriceEditDialog() {
|
||||
if (!editingItem) return;
|
||||
|
||||
const data: { code: string; type: string; price: string; row: number; col: number; isNew: boolean }[] = [];
|
||||
const data: {
|
||||
code: string;
|
||||
type: string;
|
||||
price: string;
|
||||
row: number;
|
||||
col: number;
|
||||
isNew: boolean;
|
||||
}[] = [];
|
||||
|
||||
for (const code of editingItem.product_codes || []) {
|
||||
const info = getPriceInfoForCode(code);
|
||||
|
|
@ -869,7 +886,10 @@
|
|||
|
||||
// Save price changes from dialog
|
||||
async function handleSavePricesFromDialog() {
|
||||
const updates: { row_index: number; cells: { value: string; coord: { row: number; col: number } }[] }[] = [];
|
||||
const updates: {
|
||||
row_index: number;
|
||||
cells: { value: string; coord: { row: number; col: number } }[];
|
||||
}[] = [];
|
||||
const newPrices: { cells: string[] }[] = [];
|
||||
|
||||
// Get all rows data for duplicate handling
|
||||
|
|
@ -877,7 +897,8 @@
|
|||
|
||||
// Get header for this country to build cells array correctly
|
||||
const priceHeaders = get(sheetPriceHeader)[countryCode] || [];
|
||||
const headerNames = PRICE_HEADER_NAMES_BY_COUNTRY[countryCode] || PRICE_HEADER_NAMES_BY_COUNTRY.default;
|
||||
const headerNames =
|
||||
PRICE_HEADER_NAMES_BY_COUNTRY[countryCode] || PRICE_HEADER_NAMES_BY_COUNTRY.default;
|
||||
const priceColIdx = findHeaderIndex(priceHeaders, headerNames.cash_price);
|
||||
|
||||
for (const item of priceEditData) {
|
||||
|
|
@ -887,7 +908,7 @@
|
|||
if (item.price && item.price.trim()) {
|
||||
// Build cells array based on header structure
|
||||
// Find column indices from header
|
||||
const nameColIdx = priceHeaders.findIndex(h => h.toLowerCase() === 'name') + 1;
|
||||
const nameColIdx = priceHeaders.findIndex((h) => h.toLowerCase() === 'name') + 1;
|
||||
|
||||
// Create cells array with correct length
|
||||
const cells: string[] = new Array(priceHeaders.length).fill('');
|
||||
|
|
@ -936,20 +957,24 @@
|
|||
for (const rowEntry of rowsForCode) {
|
||||
updates.push({
|
||||
row_index: rowEntry.row,
|
||||
cells: [{
|
||||
value: item.price,
|
||||
coord: { row: rowEntry.row, col: item.col }
|
||||
}]
|
||||
cells: [
|
||||
{
|
||||
value: item.price,
|
||||
coord: { row: rowEntry.row, col: item.col }
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
} else if (item.row) {
|
||||
// Single row - use the original logic
|
||||
updates.push({
|
||||
row_index: item.row,
|
||||
cells: [{
|
||||
value: item.price,
|
||||
coord: { row: item.row, col: item.col }
|
||||
}]
|
||||
cells: [
|
||||
{
|
||||
value: item.price,
|
||||
coord: { row: item.row, col: item.col }
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -966,12 +991,12 @@
|
|||
|
||||
// Send updates for existing prices
|
||||
if (updates.length > 0) {
|
||||
updateSent = updateSheetPrice(country, updates);
|
||||
updateSent = await updateSheetPrice(country, updates);
|
||||
}
|
||||
|
||||
// Send adds for new prices
|
||||
if (newPrices.length > 0) {
|
||||
addSent = addSheetPrice(country, newPrices);
|
||||
addSent = await addSheetPrice(country, newPrices);
|
||||
}
|
||||
|
||||
if (updateSent && addSent) {
|
||||
|
|
@ -1457,8 +1482,8 @@
|
|||
|
||||
// Step 2: Request menu data (this triggers streaming)
|
||||
// Small delay to ensure enter request is processed first
|
||||
setTimeout(() => {
|
||||
const requested = requestCatalogMenu(country, catalog);
|
||||
setTimeout(async () => {
|
||||
const requested = await requestCatalogMenu(country, catalog);
|
||||
if (requested) {
|
||||
console.log('[Edit] Requested menu data via WebSocket');
|
||||
} else {
|
||||
|
|
@ -1653,7 +1678,11 @@
|
|||
recipe: recipe01_query
|
||||
}));
|
||||
|
||||
console.log('[Edit] Loaded', Object.keys(recipe01_query).length, 'recipes from machine');
|
||||
console.log(
|
||||
'[Edit] Loaded',
|
||||
Object.keys(recipe01_query).length,
|
||||
'recipes from machine'
|
||||
);
|
||||
break;
|
||||
}
|
||||
} catch (parseError) {
|
||||
|
|
@ -1965,13 +1994,21 @@
|
|||
<div class="flex items-center gap-4">
|
||||
<div class="flex flex-wrap gap-4 text-sm">
|
||||
{#if displayPrices.hot}
|
||||
<span><span class="text-muted-foreground">Hot</span> {displayPrices.hot}</span>
|
||||
<span
|
||||
><span class="text-muted-foreground">Hot</span> {displayPrices.hot}</span
|
||||
>
|
||||
{/if}
|
||||
{#if displayPrices.cold}
|
||||
<span><span class="text-muted-foreground">Cold</span> {displayPrices.cold}</span>
|
||||
<span
|
||||
><span class="text-muted-foreground">Cold</span>
|
||||
{displayPrices.cold}</span
|
||||
>
|
||||
{/if}
|
||||
{#if displayPrices.blend}
|
||||
<span><span class="text-muted-foreground">Blend</span> {displayPrices.blend}</span>
|
||||
<span
|
||||
><span class="text-muted-foreground">Blend</span>
|
||||
{displayPrices.blend}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<Button
|
||||
|
|
@ -2113,7 +2150,13 @@
|
|||
<!-- View Mode -->
|
||||
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 xl:grid-cols-4">
|
||||
{#each visibleMenuItems as item, index (item.row_index)}
|
||||
<Card.Root class="group relative transition-shadow hover:shadow-md {newlyAddedRowIndices.has(item.row_index) ? 'ring-2 ring-green-500' : ''}">
|
||||
<Card.Root
|
||||
class="group relative transition-shadow hover:shadow-md {newlyAddedRowIndices.has(
|
||||
item.row_index
|
||||
)
|
||||
? 'ring-2 ring-green-500'
|
||||
: ''}"
|
||||
>
|
||||
<Card.Content class="flex min-h-[340px] flex-col items-center p-5 text-center">
|
||||
<!-- NEW badge for newly added items -->
|
||||
{#if newlyAddedRowIndices.has(item.row_index)}
|
||||
|
|
@ -2194,13 +2237,19 @@
|
|||
{#if prices.hot || prices.cold || prices.blend}
|
||||
<div class="mt-3 flex w-full justify-center gap-3 text-sm">
|
||||
{#if prices.hot}
|
||||
<span title="Hot"><span class="text-muted-foreground">Hot</span> {prices.hot}</span>
|
||||
<span title="Hot"
|
||||
><span class="text-muted-foreground">Hot</span> {prices.hot}</span
|
||||
>
|
||||
{/if}
|
||||
{#if prices.cold}
|
||||
<span title="Cold"><span class="text-muted-foreground">Cold</span> {prices.cold}</span>
|
||||
<span title="Cold"
|
||||
><span class="text-muted-foreground">Cold</span> {prices.cold}</span
|
||||
>
|
||||
{/if}
|
||||
{#if prices.blend}
|
||||
<span title="Blend"><span class="text-muted-foreground">Blend</span> {prices.blend}</span>
|
||||
<span title="Blend"
|
||||
><span class="text-muted-foreground">Blend</span> {prices.blend}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -2610,15 +2659,21 @@
|
|||
|
||||
<div class="max-h-[400px] space-y-3 overflow-y-auto py-4">
|
||||
{#each priceEditData as item, index}
|
||||
<div class="flex items-center gap-3 rounded-lg border p-3 {item.isNew ? 'border-amber-500/50 bg-amber-500/5' : 'bg-muted/30'}">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-lg border p-3 {item.isNew
|
||||
? 'border-amber-500/50 bg-amber-500/5'
|
||||
: 'bg-muted/30'}"
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<Badge variant="outline" class="text-xs">
|
||||
{item.type}
|
||||
</Badge>
|
||||
<span class="font-mono text-sm truncate">{item.code}</span>
|
||||
<span class="truncate font-mono text-sm">{item.code}</span>
|
||||
{#if item.isNew}
|
||||
<Badge class="bg-amber-600 hover:bg-amber-600 text-[10px] px-1.5 py-0">NO PRICE</Badge>
|
||||
<Badge class="bg-amber-600 px-1.5 py-0 text-[10px] hover:bg-amber-600"
|
||||
>NO PRICE</Badge
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -2635,16 +2690,12 @@
|
|||
{/each}
|
||||
|
||||
{#if priceEditData.length === 0}
|
||||
<div class="py-8 text-center text-muted-foreground">
|
||||
No product codes found
|
||||
</div>
|
||||
<div class="py-8 text-center text-muted-foreground">No product codes found</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-3">
|
||||
<Button variant="outline" onclick={() => (priceEditDialogOpen = false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="outline" onclick={() => (priceEditDialogOpen = false)}>Cancel</Button>
|
||||
<Button onclick={handleSavePricesFromDialog} disabled={savingPrice}>
|
||||
{#if savingPrice}
|
||||
<Spinner class="mr-2 h-4 w-4" />
|
||||
|
|
|
|||
|
|
@ -143,12 +143,12 @@
|
|||
errors: []
|
||||
});
|
||||
|
||||
handleIncomingMessages(
|
||||
JSON.stringify({
|
||||
type: 'chat',
|
||||
payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has connected to ${boxid}`
|
||||
})
|
||||
);
|
||||
// handleIncomingMessages(
|
||||
// JSON.stringify({
|
||||
// type: 'chat',
|
||||
// payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has connected to ${boxid}`
|
||||
// })
|
||||
// );
|
||||
} else {
|
||||
addNotification('ERROR:Failed to get machine info');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue