Supra_App/src/lib/components/recipe-details/recipe-detail.svelte
pakintada@gmail.com 274025ed33 feat: add brew app connection
- initialize tcp communication with brew app
- WIP value editor sync

Signed-off-by: pakintada@gmail.com <Pakin>
2026-04-03 17:27:10 +07:00

243 lines
6.6 KiB
Svelte

<script lang="ts">
import * as Tabs from '$lib/components/ui/tabs/index';
import * as Card from '$lib/components/ui/card/index';
import Label from '$lib/components/ui/label/label.svelte';
import Input from '$lib/components/ui/input/input.svelte';
import Button from '$lib/components/ui/button/button.svelte';
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import RecipelistTable from './recipelist-table.svelte';
import * as adb from '$lib/core/adb/adb';
import { columns, type RecipelistMaterial } from './columns';
import { get, readable, writable } from 'svelte/store';
import {
latestRecipeToppingData,
materialFromMachineQuery,
materialFromServerQuery
} from '$lib/core/stores/recipeStore';
import { generateIcing } from '$lib/helpers/icingGen';
import { machineInfoStore } from '$lib/core/stores/machineInfoStore';
import MachineInfo from '../machine-info.svelte';
import { v4 as uuidv4 } from 'uuid';
import { addNotification } from '$lib/core/stores/noti';
import { env } from '$env/dynamic/public';
import { sendCommand, sendReset } from '$lib/core/brew/command';
import { isAdbWriterAvailable } from '$lib/core/stores/adbWriter';
import { sendToAndroid } from '$lib/core/stores/adbWriter';
//
let {
recipeData,
onPendingChange,
refPage
}: { recipeData: any; onPendingChange: any; refPage: string } = $props();
let menuName: string = $state('');
let materialSnapshot: any = $state();
let machineInfoSnapshot: any = $state();
let recipeListMatState: RecipelistMaterial[] = $state([]);
let recipeListOriginal: RecipelistMaterial[] = $state([]);
let toppingSlotState: any = $state([]);
const recipeDetailDispatch = createEventDispatcher();
function remappingToColumn(data: any[]): RecipelistMaterial[] {
let ret: RecipelistMaterial[] = [];
// expect recipelist
if (materialSnapshot) {
let d_cnt = 0;
for (let rpl of data) {
let mat = materialSnapshot.filter(
(x: any) => x['id'].toString() === rpl['materialPathId'].toString()
)[0];
// console.log('mat filter get', Object(mat), Object.keys(mat));
let name = mat ? mat['materialOtherName'] : rpl['materialPathId'];
if (rpl['materialPathId'] === 0) {
name = '-';
}
// let gen_id = generateRowId();
// console.log(`generated for ${rpl['materialPathId']} = ${gen_id}`);
ret.push({
id: d_cnt,
material_id: `${name} (${rpl['materialPathId']})`,
is_use: rpl['isUse'],
values: {
string_param: rpl['StringParam'],
mix_order: rpl['MixOrder'],
stir_time: rpl['stirTime'],
feed: {
pattern: rpl['FeedPattern'],
parameter: rpl['FeedParameter']
},
powder: {
gram: rpl['powderGram'],
time: rpl['powderTime']
},
syrup: {
gram: rpl['syrupGram'],
time: rpl['syrupTime']
},
water: {
cold: rpl['waterCold'],
yield: rpl['waterYield']
}
}
});
d_cnt++;
}
}
return ret;
}
async function getCurrentQueue() {
let inst = adb.getAdbInstance();
if (inst) {
let current_brewing = await adb.pull(env.PUBLIC_BREW_CURRENT_RECIPE);
// console.log(`current brewing queue: ${current_brewing}`);
if (current_brewing === '') {
current_brewing = '{}';
}
return JSON.parse(current_brewing ?? '{}');
}
return {
error: 'instance lost'
};
}
async function resetAllPendingCmds() {
// send reset to brew
try {
await sendReset();
addNotification(`INFO:Reset completed!`);
} catch (e) {
addNotification(`ERR:${e}`);
}
}
async function saveRecipe() {}
async function sendTriggerBrewNow() {
// check queue ready
// let currentBrewingQueue = await getCurrentQueue();
// console.log('checking queue ... ', Object.keys(currentBrewingQueue).length);
await sendToAndroid({
type: 'brew_prep',
payload: {
start: new Date().toLocaleTimeString()
}
});
// if (Object.keys(currentBrewingQueue).length != 0) {
// addNotification('ERR:Brewing queue is full, please check machine or press `reset`');
// return;
// }
//
let inst = adb.getAdbInstance();
if (inst) {
console.log('check adb writer', isAdbWriterAvailable());
recipeDetailDispatch('brewNow');
} else {
console.log('result check fail');
}
}
async function checkChanges(original: any) {
console.log('old', original, 'updated', recipeListMatState);
if (recipeListOriginal.length == 0) {
recipeListOriginal = original;
}
if (original !== recipeListMatState) {
await onPendingChange({
target: 'recipeList',
value: original
});
}
}
onMount(() => {
machineInfoSnapshot = get(machineInfoStore);
if (recipeData) {
menuName =
recipeData['name'] ?? (recipeData['otherName'] ? recipeData['otherName'] : 'Not set');
if (refPage == 'overview') {
materialSnapshot = get(materialFromServerQuery);
} else if (refPage == 'brew') {
materialSnapshot = get(materialFromMachineQuery);
}
recipeListMatState = remappingToColumn(recipeData['recipes']);
toppingSlotState = recipeData['ToppingSet'];
latestRecipeToppingData.set(toppingSlotState);
// save old value\
}
});
</script>
<!-- show info -->
<!-- latest edit date -->
<!-- Menu Status -->
<div class="-mb-4 flex w-full flex-col gap-6">
<Tabs.Root value="info">
<div class="flex flex-row justify-between">
<Tabs.List>
<Tabs.Trigger value="info">Info</Tabs.Trigger>
<Tabs.Trigger value="details">Details</Tabs.Trigger>
</Tabs.List>
{#if refPage === 'brew'}
<div>
<Button type="button" variant="default" onclick={() => resetAllPendingCmds()}
>Force Reset Brewing</Button
>
<Button type="button" variant="default" onclick={() => saveRecipe()}>Save</Button>
<Button type="button" variant="default" onclick={async () => sendTriggerBrewNow()}
>Test Brew</Button
>
</div>
{/if}
</div>
<Tabs.Content value="info">
<Card.Root>
<Card.Header>
<Card.Title>Info</Card.Title>
<Card.Description>Info about this menu</Card.Description>
</Card.Header>
<Card.Content class="grid gap-6">
<div class="grid grid-flow-row gap-3">
<Label for="tabs-menu-name">Name</Label>
<Input id="tabs-menu-name" value={recipeData['name'] ?? ''} />
<Label for="tabs-menu-other-name">Other Name</Label>
<Input id="tabs-menu-other-name" value={recipeData['otherName'] ?? ''} />
</div>
<div class="grid gap-3"></div>
</Card.Content>
</Card.Root>
</Tabs.Content>
<Tabs.Content value="details">
<RecipelistTable data={recipeListMatState} {columns} onStateChange={checkChanges} />
</Tabs.Content>
</Tabs.Root>
</div>