feat: showing recipe list values
- add displaying for values in recipe list, with some editable fields except topping, string params, feed pattern/level Signed-off-by: pakintada@gmail.com <Pakin>
This commit is contained in:
parent
4866674f26
commit
dbb5ce466c
14 changed files with 640 additions and 37 deletions
|
|
@ -244,7 +244,7 @@
|
|||
connectionButtonVariant = 'default';
|
||||
connectDeviceOk = false;
|
||||
}
|
||||
connectionButtonDisable = false;
|
||||
// connectionButtonDisable = false;
|
||||
}
|
||||
|
||||
async function checkStoredCredentials() {
|
||||
|
|
@ -336,7 +336,7 @@
|
|||
|
||||
onMount(async () => {
|
||||
await checkStoredCredentials();
|
||||
await tryAutoConnect();
|
||||
if (!connectDeviceOk && !adb.getAdbInstance()) await tryAutoConnect();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import RecipelistValue from './recipelist-value.svelte';
|
|||
import { DragHandle } from './recipelist-table.svelte';
|
||||
import { createRawSnippet } from 'svelte';
|
||||
import RecipelistMatSelect from './recipelist-mat-select.svelte';
|
||||
import { recipeDataEvent } from '$lib/core/stores/recipeStore';
|
||||
|
||||
export type RecipelistMaterial = {
|
||||
id: number;
|
||||
|
|
@ -29,6 +30,7 @@ export type RecipelistMaterial = {
|
|||
values: {
|
||||
string_param: string;
|
||||
mix_order: number;
|
||||
stir_time: number;
|
||||
feed: {
|
||||
pattern: number;
|
||||
parameter: number;
|
||||
|
|
@ -69,12 +71,6 @@ export const columns: ColumnDef<RecipelistMaterial>[] = [
|
|||
{
|
||||
accessorKey: 'is_use',
|
||||
id: 'is_use',
|
||||
header: ({ column }) =>
|
||||
renderSnippet(
|
||||
createRawSnippet(() => ({
|
||||
render: () => '<div class="w-0.5">Enable</div>'
|
||||
}))
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
return renderComponent(RecipelistIsuse, {
|
||||
checked: row.original.is_use,
|
||||
|
|
@ -96,6 +92,11 @@ export const columns: ColumnDef<RecipelistMaterial>[] = [
|
|||
onMatChange: (value: any) => {
|
||||
row.original.material_id = value;
|
||||
console.log('change mat', value);
|
||||
recipeDataEvent.set({
|
||||
event_type: 'mat_change',
|
||||
payload: value,
|
||||
index: row.original.id
|
||||
});
|
||||
row.toggleSelected(row.original.is_use);
|
||||
}
|
||||
});
|
||||
|
|
@ -107,6 +108,17 @@ export const columns: ColumnDef<RecipelistMaterial>[] = [
|
|||
header: ({ column }) => 'Values',
|
||||
cell: ({ row }) => {
|
||||
return renderComponent(RecipelistValue, {
|
||||
row_uid: row.original.id,
|
||||
mat_id: row.original.material_id,
|
||||
onEditValue: (changes: any) => {
|
||||
// get change parameters
|
||||
},
|
||||
onDetectMixOrder: () => {
|
||||
// set next
|
||||
},
|
||||
onDetectToppingSlot: () => {
|
||||
// replace display mat with topping name
|
||||
},
|
||||
...row.original.values
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@
|
|||
|
||||
import { columns, type RecipelistMaterial } from './columns';
|
||||
import { get, readable, writable } from 'svelte/store';
|
||||
import { materialFromMachineQuery, materialFromServerQuery } from '$lib/core/stores/recipeStore';
|
||||
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';
|
||||
|
|
@ -29,18 +33,17 @@
|
|||
let recipeListMatState: RecipelistMaterial[] = $state([]);
|
||||
let recipeListOriginal: RecipelistMaterial[] = $state([]);
|
||||
|
||||
let toppingSlotState: any = $state([]);
|
||||
|
||||
function remappingToColumn(data: any[]): RecipelistMaterial[] {
|
||||
let ret: RecipelistMaterial[] = [];
|
||||
// expect recipelist
|
||||
if (materialSnapshot) {
|
||||
let d_cnt = 0;
|
||||
for (let rpl of data) {
|
||||
let mat =
|
||||
refPage == 'brew'
|
||||
? materialSnapshot.filter(
|
||||
(x: any) => x['id'].toString() === rpl['materialPathId'].toString()
|
||||
)[0]
|
||||
: materialSnapshot[rpl['materialPathId']];
|
||||
let mat = materialSnapshot.filter(
|
||||
(x: any) => x['id'].toString() === rpl['materialPathId'].toString()
|
||||
)[0];
|
||||
|
||||
// console.log('mat filter get', Object(mat), Object.keys(mat));
|
||||
|
||||
|
|
@ -61,9 +64,10 @@
|
|||
values: {
|
||||
string_param: rpl['StringParam'],
|
||||
mix_order: rpl['MixOrder'],
|
||||
stir_time: rpl['stirTime'],
|
||||
feed: {
|
||||
pattern: rpl['feedPattern'],
|
||||
parameter: rpl['feedParameter']
|
||||
pattern: rpl['FeedPattern'],
|
||||
parameter: rpl['FeedParameter']
|
||||
},
|
||||
powder: {
|
||||
gram: rpl['powderGram'],
|
||||
|
|
@ -113,9 +117,11 @@
|
|||
materialSnapshot = get(materialFromMachineQuery);
|
||||
}
|
||||
|
||||
console.log(`detail : ${JSON.stringify(recipeData)}`);
|
||||
|
||||
recipeListMatState = remappingToColumn(recipeData['recipes']);
|
||||
toppingSlotState = recipeData['ToppingSet'];
|
||||
|
||||
latestRecipeToppingData.set(toppingSlotState);
|
||||
|
||||
// save old value\
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index';
|
||||
import { get } from 'svelte/store';
|
||||
import {
|
||||
materialData,
|
||||
materialFromServerQuery,
|
||||
materialFromMachineQuery,
|
||||
referenceFromPage
|
||||
} from '$lib/core/stores/recipeStore';
|
||||
|
|
@ -26,7 +26,9 @@
|
|||
onMount(() => {
|
||||
refPage = get(referenceFromPage);
|
||||
if (refPage === 'brew') allMatData = get(materialFromMachineQuery);
|
||||
else if (refPage === 'overview') allMatData = get(materialData);
|
||||
else if (refPage === 'overview') allMatData = get(materialFromServerQuery);
|
||||
|
||||
// console.log('all mat data', JSON.stringify(allMatData));
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@
|
|||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Recipe List</Card.Title>
|
||||
<Card.Description>Material used in this menu's brewing process</Card.Description>
|
||||
<Card.Description>Material used in this menu's brewing process.</Card.Description>
|
||||
|
||||
<Card.Content>
|
||||
<!-- table -->
|
||||
|
|
|
|||
|
|
@ -1,6 +1,360 @@
|
|||
<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
|
||||
} from '$lib/core/stores/recipeStore';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
let { string_param, mix_order, feed, water, powder, syrup }: RecipelistMaterial['values'] =
|
||||
$props();
|
||||
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);
|
||||
|
||||
// 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
|
||||
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 current_row_topping = currentToppings[getToppingSlot()];
|
||||
let groupQuery = get(toppingGroupFromServerQuery);
|
||||
let listQuery = get(toppingListFromServerQuery);
|
||||
|
||||
// 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() {
|
||||
let current_selected = selectableToppingInGroup.find(
|
||||
(x) => x.id === currentToppings[getToppingSlot()]['ListGroupID'][0]
|
||||
);
|
||||
if (currentToppings[getToppingSlot()]['ListGroupID'][0] === 0) {
|
||||
return 'Empty';
|
||||
}
|
||||
if (!current_selected) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
function triggerEditChange(value: any) {
|
||||
console.log('triggered on change editing', JSON.stringify(value));
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
initialize();
|
||||
unsubRecipeDataEvent = recipeDataEvent.subscribe((event) => {
|
||||
if (event && event.index && event.index === row_uid) {
|
||||
// has some event
|
||||
handleEvents(event);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubRecipeDataEvent) {
|
||||
unsubRecipeDataEvent();
|
||||
}
|
||||
});
|
||||
</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>
|
||||
</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="space-y-1">
|
||||
<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>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if water.yield > 0}
|
||||
<div class="space-y-1">
|
||||
<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>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if water.cold > 0}
|
||||
<div class="space-y-1">
|
||||
<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>
|
||||
</div>
|
||||
{/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>
|
||||
<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>
|
||||
</div>
|
||||
{/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>
|
||||
<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>
|
||||
</div>
|
||||
</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}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@
|
|||
let recipe01Snap = recipeServerSnapshot['recipe'];
|
||||
if (recipe01Snap) {
|
||||
currentData = recipe01Snap[productCode] ?? {};
|
||||
console.log(`current data : ${JSON.stringify(Object.keys(recipe01Snap))}`);
|
||||
// console.log(`current data : ${JSON.stringify(Object.keys(recipe01Snap))}`);
|
||||
if (currentData.MenuStatus) {
|
||||
currentMenuStatus = matchMenuStatus(currentData.MenuStatus);
|
||||
}
|
||||
|
|
@ -87,7 +87,9 @@
|
|||
|
||||
{#if isDesktop.current}
|
||||
<Dialog.Root bind:open>
|
||||
<Dialog.Trigger onselect={(e) => e.preventDefault()}>View</Dialog.Trigger>
|
||||
<Dialog.Trigger class="w-full text-start" onselect={(e) => e.preventDefault()}
|
||||
>View</Dialog.Trigger
|
||||
>
|
||||
<Dialog.Content class="sm:max-w-3/4">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Edit Recipe {productCode}</Dialog.Title>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue