feature: show recipe edit form sheet

- move from edit directly in table to side-sheet instead

Signed-off-by: pakintada@gmail.com <Pakin>
This commit is contained in:
pakintada@gmail.com 2026-03-16 14:29:53 +07:00
parent 3f5eb8d07d
commit e6d1ac9a99
36 changed files with 1307 additions and 71 deletions

View file

@ -22,6 +22,7 @@ import { DragHandle } from './recipelist-table.svelte';
import { createRawSnippet } from 'svelte';
import RecipelistMatSelect from './recipelist-mat-select.svelte';
import { recipeDataEvent } from '$lib/core/stores/recipeStore';
import RecipelistValueEditor from './recipelist-value-editor.svelte';
export type RecipelistMaterial = {
id: number;
@ -122,5 +123,13 @@ export const columns: ColumnDef<RecipelistMaterial>[] = [
...row.original.values
});
}
},
{
id: 'actions',
cell: ({ row }) => {
return renderComponent(RecipelistValueEditor, {
row_id: row.original.id
});
}
}
];

View file

@ -0,0 +1,491 @@
<script lang="ts">
import { CupSodaIcon, PencilIcon, ChevronRightIcon, StarsIcon } from '@lucide/svelte/icons';
import Button from '$lib/components/ui/button/button.svelte';
import * as Select from '$lib/components/ui/select/index';
import * as Sheet from '$lib/components/ui/sheet/index';
import * as Field from '$lib/components/ui/field/index';
import * as Collapsible from '$lib/components/ui/collapsible/index';
import Input from '../ui/input/input.svelte';
import Textarea from '../ui/textarea/textarea.svelte';
import {
latestRecipeToppingData,
recipeDataEvent,
toppingGroupFromServerQuery,
toppingListFromServerQuery
} from '$lib/core/stores/recipeStore';
import { onMount } from 'svelte';
import { addNotification } from '$lib/core/stores/noti';
import { get } from 'svelte/store';
import { ValueEvent } from './value_event';
import ScrollArea from '../ui/scroll-area/scroll-area.svelte';
let { row_id }: { row_id: number } = $props();
let current_editing_data: any = $state();
let changed_data: any = $state();
// --------------------------------------------------
let toggledOpenPowder = $state(false);
let toggledOpenSyrup = $state(false);
let toggledOpenWater = $state(false);
let toggledOpenFeed = $state(false);
// --------------------------------------------------
let categories: any[] = $state([]);
let topping_lists: any[] = $state([]);
let selected_category_id = $state('');
let selected_topping_list_id = $state('');
const selected_category = $derived(categories.find((c) => c.groupID === selected_category_id));
const available_topping_lists = $derived(
selected_category
? topping_lists.filter((tpl) =>
selected_category.idInGroup
.split(',')
.map((x: string) => parseInt(x))
.includes(tpl.id)
)
: []
);
const selected_topping_list = $derived(
topping_lists.find((tpl) => tpl.id === selected_topping_list_id)
);
// let group_categories: any = $state({});
// let selected_topping_group = $state('');
// let selected_topping_list = $state('');
// let selectable_topping_list = $state<any[]>();
let value_event_state = $state<ValueEvent>(ValueEvent.NONE);
function getToppingSlotIndex(mat_id: number) {
return mat_id - 8110 - 1;
}
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 === 'edit_mat_field_prep') {
// update value, do re-render
if (event.payload) {
current_editing_data = event.payload;
console.log(`GET requested data: ${JSON.stringify(current_editing_data)}`);
// default topping
if (
current_editing_data['mat_type'] === 'topping' &&
current_editing_data['current_topping_group'] &&
current_editing_data['toppings']
) {
let tg_default_data = current_editing_data['current_topping_group'];
let default_gid = tg_default_data['groupID'];
let default_tid = tg_default_data['idDefault'];
// get topping slot data
let idx = getToppingSlotIndex(current_editing_data['mat_id']);
let default_from_recipe = current_editing_data['toppings'][idx]['ListGroupID'][0];
selected_category_id = default_gid;
selected_topping_list_id = default_from_recipe ?? default_tid ?? 0;
}
// default first open if has value
toggledOpenPowder =
current_editing_data.powder.gram > 0 || current_editing_data.powder.time > 0;
toggledOpenSyrup =
current_editing_data.syrup.gram > 0 || current_editing_data.syrup.time > 0;
toggledOpenWater =
current_editing_data.water.yield > 0 || current_editing_data.water.cold > 0;
toggledOpenFeed =
current_editing_data.feed.pattern > 0 || current_editing_data.feed.parameter > 0;
}
}
}
function requestDataFromDisplay() {
console.log('sending request edit', row_id);
recipeDataEvent.set({
event_type: 'edit_mat_field',
payload: row_id,
index: row_id
});
setTimeout(() => {
if (current_editing_data === undefined) {
addNotification('ERR:Unable to edit');
}
}, 5000);
}
function onFieldValueChange(field_name: string[], new_value: any) {
console.log('change on', JSON.stringify(field_name), new_value);
// validate if field value changes
let curr_val;
for (let field of field_name) {
if (!curr_val) {
curr_val = current_editing_data[field];
continue;
}
try {
curr_val = curr_val[field];
} catch (e) {
console.error('try get field error: ', e);
}
}
let has_changed = curr_val !== new_value;
if (has_changed) {
if (value_event_state === ValueEvent.NONE) {
value_event_state = ValueEvent.EDITED;
// save change
let single_key = '';
for (let field of field_name) {
single_key += field + '_';
}
single_key = single_key.slice(0, single_key.length - 1);
console.log('save to key', single_key);
changed_data[single_key] = new_value;
}
} else {
// revert value in key
}
}
function saveEditingValue() {
console.log('saving value ...');
if (value_event_state === ValueEvent.EDITED) {
recipeDataEvent.set({
event_type: 'save_mat_field',
payload: current_editing_data,
index: row_id
});
}
}
function handleToppingGroupChange(v: any) {
console.log('change topping group', JSON.stringify(v));
selected_category_id = v.groupID;
// get default
selected_topping_list_id = v.idDefault ?? 0;
}
function handleToppingListChange(v: any) {
console.log('Topping list chose: ', JSON.stringify(v));
selected_topping_list_id = v.id;
}
// recipelist-value-editor.svelte?t=1773541064272:57 GET requested data: {"mat_id":1002,"mat_type":"bean","mat_name":"medium-roasts (1002)","params":{"esp-v2-press-value":"24"},"toppings":[{"ListGroupID":[1,0,0,0],"defaultIDSelect":1,"groupID":"1","isUse":true},{"ListGroupID":[6,0,0,0],"defaultIDSelect":31,"groupID":"6","isUse":true},{"ListGroupID":[7,0,0,0],"defaultIDSelect":33,"groupID":"7","isUse":true},{"ListGroupID":[0,0,0,0],"defaultIDSelect":0,"groupID":"0","isUse":false},{"ListGroupID":[0,0,0,0],"defaultIDSelect":0,"groupID":"0","isUse":false},{"ListGroupID":[0,0,0,0],"defaultIDSelect":0,"groupID":"0","isUse":false},{"ListGroupID":[530,0,0,0],"defaultIDSelect":532,"groupID":"530","isUse":true},{"ListGroupID":[500,0,0,0],"defaultIDSelect":502,"groupID":"500","isUse":true}],"current_topping_list":[],"has_mix_ord":false,"feed":{"pattern":0,"parameter":0},"water":{"cold":0,"yield":30},"powder":{"gram":7,"time":9},"syrup":{"gram":0,"time":0},"stir_time":120}
// recipelist-value-editor.svelte?t=1773541064272:79 sending request edit 0
// recipelist-value.svelte:224 request edit mat
// recipelist-value-editor.svelte?t=1773541064272:57 GET requested data: {"mat_id":9501,"mat_type":"cup","mat_name":"CUP paper (9501)","params":{},"toppings":[{"ListGroupID":[1,0,0,0],"defaultIDSelect":1,"groupID":"1","isUse":true},{"ListGroupID":[6,0,0,0],"defaultIDSelect":31,"groupID":"6","isUse":true},{"ListGroupID":[7,0,0,0],"defaultIDSelect":33,"groupID":"7","isUse":true},{"ListGroupID":[0,0,0,0],"defaultIDSelect":0,"groupID":"0","isUse":false},{"ListGroupID":[0,0,0,0],"defaultIDSelect":0,"groupID":"0","isUse":false},{"ListGroupID":[0,0,0,0],"defaultIDSelect":0,"groupID":"0","isUse":false},{"ListGroupID":[530,0,0,0],"defaultIDSelect":532,"groupID":"530","isUse":true},{"ListGroupID":[500,0,0,0],"defaultIDSelect":502,"groupID":"500","isUse":true}],"current_topping_list":[],"has_mix_ord":false,"feed":{"pattern":0,"parameter":0},"water":{"cold":0,"yield":0},"powder":{"gram":0,"time":0},"syrup":{"gram":0,"time":0},"stir_time":0}
onMount(() => {
categories = get(toppingGroupFromServerQuery);
topping_lists = get(toppingListFromServerQuery);
return recipeDataEvent.subscribe((event) => {
if (event !== null && event.index !== undefined && event.index === row_id) {
handleEvents(event);
}
});
});
</script>
<Sheet.Root>
<Sheet.Trigger>
<Button
variant="default"
size="icon"
class="relative size-8 p-0"
onclick={() => requestDataFromDisplay()}
>
<PencilIcon />
</Button>
</Sheet.Trigger>
<Sheet.Content side="right">
<Sheet.Header>
<Sheet.Title>Value Editor</Sheet.Title>
<Sheet.Description>
Make changes to current order of material. Click save when you're done.
</Sheet.Description>
</Sheet.Header>
<!-- form -->
<div class="max-w-md min-w-sm p-4">
<form>
<Field.Group>
<Field.Set>
<Field.Group>
<Field.Field>
<Field.Label for="material_name">Current Material</Field.Label>
<Input
id="material_name"
placeholder="Material Name"
value={`${current_editing_data.mat_name}`}
disabled={true}
/>
<Field.Description>
<div class="my-2 flex gap-2">
Material Type: {current_editing_data.mat_type.toString().toUpperCase()}
{#if current_editing_data.mat_type === 'cup'}
<CupSodaIcon />
{:else if current_editing_data.mat_type === 'topping'}
<StarsIcon />
{/if}
</div>
</Field.Description>
</Field.Field>
</Field.Group>
</Field.Set>
<ScrollArea class="h-[60vh] w-full" type="always">
<!-- topping layout -->
<div
class={current_editing_data.mat_type === 'topping'
? 'my-4 grid grid-cols-2 gap-4'
: 'hidden'}
>
<Field.Field>
<!-- topping group -->
<Field.Label for="topping_group">Group</Field.Label>
<Select.Root
type="single"
value={selected_category_id}
onValueChange={(v: any) => handleToppingGroupChange(v)}
>
<Select.Trigger id="topping_group">
<span>
{selected_category?.otherName ?? selected_category?.name ?? 'Select Category'}
</span>
</Select.Trigger>
<Select.Content>
{#each categories as tg}
<Select.Item value={tg}>{tg.otherName ?? tg.name}</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</Field.Field>
<Field.Field>
<!-- topping list -->
<Field.Label for="topping_list">Select</Field.Label>
<Select.Root
type="single"
disabled={!selected_category_id}
value={selected_topping_list_id}
onValueChange={(v: any) => handleToppingListChange(v)}
>
<Select.Trigger id="topping_list">
<span>
{selected_topping_list?.otherName ??
selected_topping_list?.name ??
'Select Topping'}
</span>
</Select.Trigger>
<Select.Content>
{#each available_topping_lists as tl}
<Select.Item value={tl}>{tl.otherName ?? tl.name}</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</Field.Field>
</div>
<!-- main fields -->
<Collapsible.Root class="group/collapsible" open={toggledOpenPowder}>
<Collapsible.Trigger>
<div
class="flex flex-row items-center justify-between gap-4 rounded-xl p-2 text-sm text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
>
Powder
<ChevronRightIcon
class="ms-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
/>
</div>
</Collapsible.Trigger>
<Collapsible.Content>
<div class="grid grid-cols-2 gap-2 rounded-3xl border p-4">
<!-- powder -->
<Field.Field>
<Field.Label for="powder_gram">Powder (g)</Field.Label>
<Input
id="powder_gram"
placeholder="Powder material in gram"
value={`${current_editing_data.powder.gram}`}
onchange={(v) =>
onFieldValueChange(['powder', 'gram'], v.currentTarget.value)}
/>
</Field.Field>
<Field.Field>
<Field.Label for="powder_time">Powder (sec)</Field.Label>
<Input
id="powder_time"
placeholder="Powder material released by seconds"
value={`${current_editing_data.powder.time}`}
onchange={(v) =>
onFieldValueChange(['powder', 'time'], v.currentTarget.value)}
/>
</Field.Field>
</div>
</Collapsible.Content>
</Collapsible.Root>
<Field.Separator />
<Collapsible.Root class="group/collapsible" open={toggledOpenSyrup}>
<Collapsible.Trigger>
<div
class="flex flex-row items-center justify-between gap-4 rounded-xl p-2 text-sm text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
>
Syrup
<ChevronRightIcon
class="ms-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
/>
</div>
</Collapsible.Trigger>
<Collapsible.Content>
<div class="grid grid-cols-2 gap-2">
<!-- syrup -->
<Field.Field>
<Field.Label for="syrup_gram">Syrup (g)</Field.Label>
<Input
id="syrup_gram"
placeholder="Syrup material in gram"
value={`${current_editing_data.syrup.gram}`}
onchange={(v) => onFieldValueChange(['syrup', 'gram'], v.currentTarget.value)}
/>
</Field.Field>
<Field.Field>
<Field.Label for="syrup_time">Syrup (sec)</Field.Label>
<Input
id="syrup_time"
placeholder="Syrup material released by seconds"
value={`${current_editing_data.syrup.time}`}
onchange={(v) => onFieldValueChange(['syrup', 'time'], v.currentTarget.value)}
/>
</Field.Field>
</div>
</Collapsible.Content>
</Collapsible.Root>
<Field.Separator />
<Collapsible.Root class="group/collapsible" open={toggledOpenWater}>
<Collapsible.Trigger>
<div
class="flex flex-row items-center justify-between gap-4 rounded-xl p-2 text-sm text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
>
Water
<ChevronRightIcon
class="ms-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
/>
</div>
</Collapsible.Trigger>
<Collapsible.Content>
<div class="grid grid-cols-2 gap-2">
<!-- water -->
<Field.Field>
<Field.Label for="water_hot">Water (Hot)</Field.Label>
<Input
id="water_hot"
value={`${current_editing_data.water.yield}`}
onchange={(v) =>
onFieldValueChange(['water', 'yield'], v.currentTarget.value)}
/>
</Field.Field>
<Field.Field>
<Field.Label for="water_cold">Water (Cold)</Field.Label>
<Input
id="water_cold"
value={`${current_editing_data.water.cold}`}
onchange={(v) => onFieldValueChange(['water', 'cold'], v.currentTarget.value)}
/>
</Field.Field>
</div>
</Collapsible.Content>
</Collapsible.Root>
<Field.Separator />
<Collapsible.Root class="group/collapsible" open={toggledOpenFeed}>
<Collapsible.Trigger>
<div
class="flex flex-row items-center justify-between gap-4 rounded-xl p-2 text-sm text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
>
Feed
<ChevronRightIcon
class="ms-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
/>
</div>
</Collapsible.Trigger>
<Collapsible.Content>
<div class="grid grid-cols-2 gap-2">
<!-- feed -->
<Field.Field>
<Field.Label for="feed_pattern">Feed Pattern</Field.Label>
<Input
id="feed_pattern"
value={`${current_editing_data.feed.pattern}`}
onchange={(v) =>
onFieldValueChange(['feed', 'pattern'], v.currentTarget.value)}
/>
</Field.Field>
<Field.Field>
<Field.Label for="feed_param">Feed Level</Field.Label>
<Input
id="feed_param"
value={`${current_editing_data.feed.parameter}`}
onchange={(v) =>
onFieldValueChange(['feed', 'parameter'], v.currentTarget.value)}
/>
</Field.Field>
</div>
</Collapsible.Content>
</Collapsible.Root>
<Field.Separator />
<Field.Field class="my-4">
<Field.Label for="stir_time">Stirring time</Field.Label>
<Input
id="stir_time"
value={`${current_editing_data.stir_time}`}
onchange={(v) => onFieldValueChange(['stir_time'], v.currentTarget.value)}
/>
<Field.Description>
<div class="my-2 flex gap-2"></div>
</Field.Description>
</Field.Field>
<Field.Separator />
<Field.Set>
<Field.Group>
<Field.Field class="my-2">
<Field.Label for="string_params">Parameters</Field.Label>
<Textarea
id="string_params"
placeholder="Additional parameters i.e. press value, etc."
class="resize-none"
value={JSON.stringify(current_editing_data.params)}
/>
<Field.Description>
Special keys/values used for controlling specific behavior
</Field.Description>
</Field.Field>
</Field.Group>
</Field.Set>
</ScrollArea>
<!-- final -->
<Field.Field orientation="horizontal">
<Button type="button" onclick={() => saveEditingValue()}>Save</Button>
<Button variant="outline" type="button">Cancel</Button>
</Field.Field>
</Field.Group>
</form>
</div>
</Sheet.Content>
</Sheet.Root>

View file

@ -46,21 +46,6 @@
let currentStringParams: any = $state({});
let currentMaterialInt: number = $state(0);
// All ui states
let showBlenderParam = $state(false);
let showMixParam = $state(false);
let showSyrupParam = $state(false);
let showWaterParam = $state(false);
let showPowderParam = $state(false);
let showIceParam = $state(false);
let showAdjustGrinder = $state(false);
let showEspressoParam = $state(false);
let showToppingIdx = $state(false);
let showEquipmentLayout = $state(false);
let showToppingParam = $state(false);
let showSubtractPreWater = $state(false);
let showCleanOptionParam = $state(false);
// toppings
let currentToppings: any = $state([]);
// current topping of this row
@ -169,6 +154,8 @@
}
function getCurrentSelectedToppingList() {
// FIXME: show unknown on preview
let current_selected = selectableToppingInGroup.find(
(x) => x.id === currentToppings[getToppingSlot()]['ListGroupID'][0]
);
@ -216,10 +203,32 @@
}
function handleEvents(event: { event_type: string; payload: any; index: number | undefined }) {
console.log('triggered event', event.event_type, JSON.stringify(event.payload));
// 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
});
}
}
@ -230,7 +239,7 @@
onMount(() => {
initialize();
unsubRecipeDataEvent = recipeDataEvent.subscribe((event) => {
if (event && event.index && event.index === row_uid) {
if (event !== null && event.index !== undefined && event.index === row_uid) {
// has some event
handleEvents(event);
}
@ -244,27 +253,29 @@
});
</script>
{#if currentMaterialType === 'topping'}
<!-- do topping layout -->
<div>
<!-- get name of topping -->
<div class="mx-auto my-4 flex flex-row gap-8">
<!-- popup selector for topping group -->
<Button variant="default">{getCurrentSelectedToppingGroup()}</Button>
<!-- popup selector for topping list -->
<Button variant="default">{getCurrentSelectedToppingList()}</Button>
<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>
</div>
{:else}
<div>
<div class="flex w-full flex-row gap-4">
<!-- string param -->
{#if !hasMixOrder}
<!-- check if param is esp-v2-press-value -->
{: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="space-y-1">
<Label class="font-bold" for={`esp_v2_press_${row_uid}`}>Press</Label>
{#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"
@ -275,13 +286,18 @@
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>mA</p>
</div>
</div>
{/if}
</div> -->
{#if water.yield > 0}
<div class="space-y-1">
<Label class="font-bold" for={`water_yield_volume_${row_uid}`}>Hot</Label>
<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"
@ -292,13 +308,18 @@
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>ml</p>
</div>
</div>
{/if}
</div> -->
{#if water.cold > 0}
<div class="space-y-1">
<Label class="font-bold" for={`water_cold_volume_${row_uid}`}>Cold</Label>
<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"
@ -309,13 +330,17 @@
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>ml</p>
</div> -->
<p class="text-muted-foreground">
<b> Hot </b>
{water.cold} ml
</p>
</div>
</div>
{/if}
{/if}
{#if currentMaterialType !== 'cup' && currentMaterialType !== 'topping' && stir_time > 0}
<div class="space-y-1">
<Label class="font-bold" for={`stir_time_${row_uid}`}>{getStirTimeName()}</Label>
{#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"
@ -326,15 +351,19 @@
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>
</div>
{/if}
{/if}
{/if}
<!-- display powder/syrup -->
{#if currentMaterialType === 'syrup' || currentMaterialType === 'powder' || currentMaterialType === 'bean'}
<div class="space-y-1">
<Label class="font-bold" for={`powder_syrup_volume_${row_uid}`}>Volume</Label>
<!-- 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"
@ -344,17 +373,23 @@
onchangecapture={(v) =>
triggerEditChange(`${v.currentTarget.id}=${v.currentTarget.value}`)}
/>
<p>gram</p>
<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>
<!-- feed pattern -->
{#if feed.parameter > 0 || feed.pattern > 0}
<div class="mx-auto my-4 flex items-center gap-8 text-center">
<Button variant="outline">Style {feed.pattern}</Button>
<Button variant="outline">Level {feed.parameter}</Button>
</div>
{/if}
</div>
{/if}
{/if}
<!-- show sheet -->
</div>

View file

@ -0,0 +1,6 @@
enum ValueEvent {
NONE,
EDITED
}
export { ValueEvent };