Supra_App/src/lib/components/recipe-details/recipelist-value.svelte
pakintada@gmail.com e9192c8607 change: remove loading while request recipe
progress: WIP editing flow

Signed-off-by: pakintada@gmail.com <Pakin>
2026-03-24 17:52:53 +07:00

406 lines
11 KiB
Svelte

<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import type { RecipelistMaterial } from './columns';
import {
convertFromInterProductCode,
extractMaterialIdFromDisplay,
getMaterialType
} from '$lib/data/recipeService';
import Label from '../ui/label/label.svelte';
import Input from '../ui/input/input.svelte';
import Separator from '$lib/components/ui/separator/separator.svelte';
import Button from '../ui/button/button.svelte';
import { PencilIcon } from '@lucide/svelte/icons';
import * as Tooltip from '../ui/tooltip/index';
import {
latestRecipeToppingData,
recipeDataEvent,
toppingGroupFromServerQuery,
toppingListFromServerQuery,
toppingGroupFromMachineQuery,
toppingListFromMachineQuery,
referenceFromPage
} from '$lib/core/stores/recipeStore';
import { get } from 'svelte/store';
let {
row_uid,
mat_id,
onDetectToppingSlot,
onDetectMixOrder,
onEditValue,
string_param,
mix_order,
feed,
water,
powder,
syrup,
stir_time
}: {
row_uid: number;
mat_id: string;
onDetectToppingSlot: any;
onDetectMixOrder: any;
onEditValue: any;
} & RecipelistMaterial['values'] = $props();
let currentMaterialType = $state('');
let hasMixOrder = $state(false);
let currentStringParams: any = $state({});
let currentMaterialInt: number = $state(0);
// toppings
let currentToppings: any = $state([]);
// current topping of this row
let currentToppingInRow: any = $state();
let selectableToppingInGroup: any[] = $state([]);
let unsubRecipeDataEvent: any;
function isToppingId(mat_id: string): boolean {
let mat_num = extractMaterialIdFromDisplay(mat_id);
currentMaterialInt = mat_num;
return mat_num > 8110 && mat_num < 8131;
}
function getToppingSlot() {
return currentMaterialInt - 8110 - 1;
}
function extractStringParam() {
if (string_param && string_param !== '') {
let plist = string_param.split(',');
for (let param of plist) {
if (param !== '') {
let kv = param.split('=');
let key = kv[0];
let value = kv[1] ?? '';
console.log('key', key, 'value', value);
currentStringParams[key] = value;
}
}
}
}
function getPowderSyrupValue() {
if (currentMaterialType === 'powder' || currentMaterialType === 'bean') {
return powder.gram;
} else if (currentMaterialType === 'syrup') {
return syrup.gram;
}
return 0;
}
function getStirTimeName() {
switch (currentMaterialType) {
case 'whipper':
return 'Mix';
case 'bean':
return 'Grinder';
case 'others':
if (currentMaterialInt == 8001 || currentMaterialInt == 8002) {
return 'Clean';
}
default:
return '';
}
}
function getToppingDisplay() {
let refFrom = get(referenceFromPage);
let current_row_topping = currentToppings[getToppingSlot()];
let groupQuery = get(
refFrom === 'overview' ? toppingGroupFromServerQuery : toppingGroupFromMachineQuery
);
let listQuery = get(
refFrom === 'overview' ? toppingListFromServerQuery : toppingListFromMachineQuery
);
// console.log(JSON.stringify(groupQuery[0]));
// console.log(JSON.stringify(listQuery[0]));
let groupData = groupQuery.find(
(x: any) => x.groupID.toString() === current_row_topping['groupID']
);
let listData = listQuery.filter(
(x: any) =>
groupData &&
Object.keys(groupData).includes('idInGroup') &&
groupData['idInGroup'].split(',').includes(x.id.toString())
);
console.log('topping data', JSON.stringify(groupData), 'list', listData.length);
currentToppingInRow =
current_row_topping['groupID'] === 0 || current_row_topping['groupID'] === '0'
? {
name: 'Empty',
otherName: 'Empty',
id: 0
}
: groupData;
selectableToppingInGroup =
currentToppingInRow.id === 0
? [
{
name: 'Empty',
otherName: 'Empty',
id: 0
}
]
: listData;
}
function getCurrentSelectedToppingGroup() {
if (currentToppingInRow) {
return currentToppingInRow['otherName'] ?? currentToppingInRow['name'];
} else {
return 'Unknown';
}
}
function getCurrentSelectedToppingList() {
// FIXME: show unknown on preview
let current_selected = selectableToppingInGroup.find(
(x) => x.id === currentToppings[getToppingSlot()]['ListGroupID'][0]
);
if (currentToppings[getToppingSlot()]['ListGroupID'][0] === 0) {
return 'Empty';
}
if (current_selected === undefined || current_selected === null) {
return 'Unknown';
}
return current_selected.otherName ?? current_selected.name;
}
function initialize() {
currentToppings = get(latestRecipeToppingData);
hasMixOrder = mix_order == 1;
extractStringParam();
if (isToppingId(mat_id) && onDetectToppingSlot) {
currentMaterialType = 'topping';
getToppingDisplay();
onDetectToppingSlot();
} else {
let mat_num = extractMaterialIdFromDisplay(mat_id);
currentMaterialInt = mat_num;
let mat_type_t1 = getMaterialType(mat_num);
let mat_type_t2 = getMaterialType(convertFromInterProductCode(mat_num));
// console.log('type get', mat_type_t1, mat_type_t2);
currentMaterialType = mat_type_t1 === mat_type_t2 ? mat_type_t1 : mat_type_t2;
if (hasMixOrder) {
console.log('detect mix order', mat_num);
}
if (feed.parameter > 0 || feed.pattern > 0) {
console.log('has feed fields', JSON.stringify(feed));
}
}
}
function handleEvents(event: { event_type: string; payload: any; index: number | undefined }) {
// console.log('triggered event', event.event_type, JSON.stringify(event.payload));
if (event.event_type === 'mat_change') {
// update value, do re-render
initialize();
} else if (event.event_type === 'edit_mat_field') {
console.log('request edit mat');
// pack all shown data
recipeDataEvent.set({
event_type: 'edit_mat_field_prep',
payload: {
mat_id: currentMaterialInt,
mat_type: currentMaterialType,
mat_name: mat_id,
params: currentStringParams,
toppings: currentToppings,
current_topping_group: currentToppingInRow,
current_topping_list: selectableToppingInGroup,
has_mix_ord: hasMixOrder,
feed,
water,
powder,
syrup,
stir_time
},
index: row_uid
});
} else if (event.event_type === 'save_mat_field') {
console.log('receive saving process mat, do refresh...');
let change_values = event.payload.change;
// apply now
let keys = Object.keys(change_values);
console.log('change keys', JSON.stringify(keys));
}
}
onMount(() => {
initialize();
unsubRecipeDataEvent = recipeDataEvent.subscribe((event) => {
if (event !== null && event.index !== undefined && event.index === row_uid) {
// has some event
handleEvents(event);
}
});
});
onDestroy(() => {
if (unsubRecipeDataEvent) {
unsubRecipeDataEvent();
}
});
</script>
<div>
{#if currentMaterialType === 'topping'}
<!-- do topping layout -->
<div>
<!-- get name of topping -->
<div class="mx-auto my-4 flex flex-row gap-8">
<p class="text-muted-foreground">
<b>
{getCurrentSelectedToppingGroup()}
</b>, {getCurrentSelectedToppingList()}
</p>
</div>
</div>
{:else}
<div>
<div class="flex w-full flex-row gap-4">
<!-- string param -->
{#if !hasMixOrder}
<!-- check if param is esp-v2-press-value -->
{#if currentStringParams['esp-v2-press-value']}
<div class="my-4">
<!-- <Label class="font-bold" for={`esp_v2_press_${row_uid}`}>Press</Label>
<div class="flex flex-row items-center space-x-2 text-center">
<Input
class="w-16"
id={`esp_v2_press_${row_uid}`}
type="number"
value={currentStringParams['esp-v2-press-value']}
onchangecapture={(v) =>
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>mA</p>
</div> -->
<p class="text-muted-foreground">
<b> Press </b>
{currentStringParams['esp-v2-press-value']} mA
</p>
</div>
{/if}
{#if water.yield > 0}
<div class="my-4">
<!-- <Label class="font-bold" for={`water_yield_volume_${row_uid}`}>Hot</Label>
<div class="flex flex-row items-center space-x-2 text-center">
<Input
class="w-16"
type="number"
id={`water_yield_volume_${row_uid}`}
value={water.yield}
onchangecapture={(v) =>
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>ml</p>
</div> -->
<p class="text-muted-foreground">
<b> Hot </b>
{water.yield} ml
</p>
</div>
{/if}
{#if water.cold > 0}
<div class="my-4">
<!-- <Label class="font-bold" for={`water_cold_volume_${row_uid}`}>Cold</Label>
<div class="flex flex-row items-center space-x-2 text-center">
<Input
class="w-16"
type="number"
id={`water_cold_volume_${row_uid}`}
value={water.cold}
onchangecapture={(v) =>
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>ml</p>
</div> -->
<p class="text-muted-foreground">
<b> Hot </b>
{water.cold} ml
</p>
</div>
{/if}
{#if currentMaterialType !== 'cup' && currentMaterialType !== 'topping' && stir_time > 0}
<div class="my-4">
<!-- <Label class="font-bold" for={`stir_time_${row_uid}`}>{getStirTimeName()}</Label>
<div class="flex flex-row items-center space-x-2 text-center">
<Input
class="w-16"
type="number"
id={`stir_time_${row_uid}`}
value={stir_time / 10}
onchangecapture={(v) =>
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>sec</p>
</div> -->
<p class="text-muted-foreground">
<b>{getStirTimeName()}</b>
{stir_time / 10} sec(s)
</p>
</div>
{/if}
{/if}
<!-- display powder/syrup -->
{#if currentMaterialType === 'syrup' || currentMaterialType === 'powder' || currentMaterialType === 'bean'}
<div class="my-4">
<!-- <Label class="font-bold" for={`powder_syrup_volume_${row_uid}`}>Volume</Label>
<div class="flex flex-row items-center space-x-2 text-center">
<Input
class="w-16"
type="number"
id={`powder_syrup_volume_${row_uid}`}
value={getPowderSyrupValue()}
onchangecapture={(v) =>
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>gram</p> -->
<p class="text-muted-foreground">
<b> Volume </b>
{getPowderSyrupValue()} gram
</p>
</div>
{/if}
</div>
<!-- feed pattern -->
{#if feed.parameter > 0 || feed.pattern > 0}
<div class="mx-auto my-4 flex items-center gap-8 text-center">
<p class="text-muted-foreground">Style {feed.pattern}</p>
<p class="text-muted-foreground">Level {feed.parameter}</p>
</div>
{/if}
</div>
{/if}
<!-- show sheet -->
</div>