feat: recipe version selector
- fix recipe not show on overview - fix recipe show late after select country - disable queue message on no connection ws - fix infinite topping(s) list if moving between pages Signed-off-by: pakintada@gmail.com <Pakin>
This commit is contained in:
parent
e25881d016
commit
a29ff0be1a
19 changed files with 314 additions and 108 deletions
|
|
@ -5,11 +5,10 @@ Idea, Issue, Work Tracking
|
||||||
|
|
||||||
[Pending]
|
[Pending]
|
||||||
|
|
||||||
- [] #2: Send change value from editing in recipe to machine
|
|
||||||
- [] #3: Save value to recipe
|
- [] #3: Save value to recipe
|
||||||
- [] #5: revert value on close dialog recipe
|
|
||||||
- [] #6: display all recipes with materials from csv [material usages with product code]
|
- [] #6: display all recipes with materials from csv [material usages with product code]
|
||||||
- [] #7: material & menu creation
|
- [] #7: material & menu creation
|
||||||
|
- [x] #8: change recipe version
|
||||||
|
|
||||||
[Rejected]
|
[Rejected]
|
||||||
- [] #4: From #1, will do sync value from server, so that user could save their current edit too
|
- [] #4: From #1, will do sync value from server, so that user could save their current edit too
|
||||||
|
|
@ -17,5 +16,7 @@ Idea, Issue, Work Tracking
|
||||||
|
|
||||||
[Done]
|
[Done]
|
||||||
- [x] #1: Topping value saving bug, fix by snapshot value
|
- [x] #1: Topping value saving bug, fix by snapshot value
|
||||||
|
- [x] #2: Send change value from editing in recipe to machine
|
||||||
|
- [x] #5: revert value on close dialog recipe
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@
|
||||||
<Tabs.Content value="details">
|
<Tabs.Content value="details">
|
||||||
<!-- hide by machine state -->
|
<!-- hide by machine state -->
|
||||||
|
|
||||||
{#if $machineInfoStore?.status == 'IDLE' || $machineInfoStore?.status == ''}
|
{#if $machineInfoStore?.status == 'IDLE' || $machineInfoStore?.status == '' || refPage == 'overview'}
|
||||||
<RecipelistTable
|
<RecipelistTable
|
||||||
data={recipeListMatState}
|
data={recipeListMatState}
|
||||||
{columns}
|
{columns}
|
||||||
|
|
|
||||||
|
|
@ -293,11 +293,11 @@
|
||||||
// console.log('sheet open? ', sheetOpenState);
|
// console.log('sheet open? ', sheetOpenState);
|
||||||
|
|
||||||
let refFrom = get(referenceFromPage);
|
let refFrom = get(referenceFromPage);
|
||||||
categories = get(
|
categories = $state.snapshot(
|
||||||
refFrom === 'overview' ? toppingGroupFromServerQuery : toppingGroupFromMachineQuery
|
get(refFrom === 'overview' ? toppingGroupFromServerQuery : toppingGroupFromMachineQuery)
|
||||||
);
|
);
|
||||||
topping_lists = get(
|
topping_lists = $state.snapshot(
|
||||||
refFrom === 'overview' ? toppingListFromServerQuery : toppingListFromMachineQuery
|
get(refFrom === 'overview' ? toppingListFromServerQuery : toppingListFromMachineQuery)
|
||||||
);
|
);
|
||||||
|
|
||||||
// save ref
|
// save ref
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,9 @@
|
||||||
let current_selected = selectableToppingInGroup.find(
|
let current_selected = selectableToppingInGroup.find(
|
||||||
(x) => x.id === currentToppings[getToppingSlot()]['ListGroupID'][0]
|
(x) => x.id === currentToppings[getToppingSlot()]['ListGroupID'][0]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log('current selected topping list', row_uid, current_selected);
|
||||||
|
|
||||||
if (currentToppings[getToppingSlot()]['ListGroupID'][0] === 0) {
|
if (currentToppings[getToppingSlot()]['ListGroupID'][0] === 0) {
|
||||||
return 'Empty';
|
return 'Empty';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,19 +130,32 @@
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// topping part
|
// topping part
|
||||||
latestRecipeToppingData.set(new_topping_value_for_save);
|
// latestRecipeToppingData.set(new_topping_value_for_save);
|
||||||
console.log('save change', get(latestRecipeToppingData));
|
// console.log('save change', get(latestRecipeToppingData));
|
||||||
currentData['ToppingSet'] = latestRecipeToppingData;
|
// currentData['ToppingSet'] = latestRecipeToppingData;
|
||||||
|
|
||||||
console.log('current data', currentData);
|
// console.log('current data', currentData);
|
||||||
|
|
||||||
if (get(referenceFromPage) == 'brew') {
|
if (get(referenceFromPage) == 'brew') {
|
||||||
// send change to machine
|
// send change to machine
|
||||||
|
|
||||||
|
// apply value
|
||||||
|
|
||||||
|
let recipeDevSnapshot = get(recipeFromMachineQuery) ?? {};
|
||||||
|
let recipe01Snap = recipeDevSnapshot['recipe'];
|
||||||
|
if (recipe01Snap) {
|
||||||
|
recipe01Snap[ready_to_send_brew[0]['productCode']] = ready_to_send_brew[0];
|
||||||
|
|
||||||
|
// save now
|
||||||
|
recipeDevSnapshot['recipe'] = recipe01Snap;
|
||||||
|
recipeFromMachineQuery.set(recipeDevSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
sendToAndroid({
|
sendToAndroid({
|
||||||
type: 'save_recipe_machine',
|
type: 'save_recipe_machine',
|
||||||
payload: {
|
payload: {
|
||||||
time: new Date().toLocaleTimeString(),
|
time: new Date().toLocaleTimeString(),
|
||||||
data: currentData
|
data: ready_to_send_brew[0]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (get(referenceFromPage) == 'overview') {
|
} else if (get(referenceFromPage) == 'overview') {
|
||||||
|
|
@ -294,10 +307,10 @@
|
||||||
<Dialog.Title
|
<Dialog.Title
|
||||||
>Edit Recipe {productCode}
|
>Edit Recipe {productCode}
|
||||||
<Badge class="m-2">
|
<Badge class="m-2">
|
||||||
{#if $machineInfoStore?.status == 'IDLE' || $machineInfoStore?.status == ''}
|
{#if $machineInfoStore?.status == 'IDLE' || $machineInfoStore?.status == '' || refPage == 'overview'}
|
||||||
Ready
|
Ready
|
||||||
{:else}
|
{:else}
|
||||||
<Spinner /> Brewing
|
<Spinner /> Working
|
||||||
{/if}
|
{/if}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
|
|
@ -317,7 +330,7 @@
|
||||||
on:saveRecipe={async () => {
|
on:saveRecipe={async () => {
|
||||||
save_change = true;
|
save_change = true;
|
||||||
|
|
||||||
console.log('save change, check state', callback_revert_value_if_not_save);
|
callback_revert_value_if_not_save(save_change);
|
||||||
|
|
||||||
addNotification('INFO:Save recipe');
|
addNotification('INFO:Save recipe');
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ async function connectToAndroidServer() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await push('/sdcard/coffeevending/enable_adb_block_watch', '1');
|
// await push('/sdcard/coffeevending/enable_adb_block_watch', '1');
|
||||||
|
|
||||||
const stream = await inst.transport.connect(env.PUBLIC_BREW_CONN_PORT);
|
const stream = await inst.transport.connect(env.PUBLIC_BREW_CONN_PORT);
|
||||||
const writer = stream.writable.getWriter();
|
const writer = stream.writable.getWriter();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { extractCookieOnNonBrowser } from '$lib/helpers/cookie';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { permission } from '../stores/permissions';
|
import { permission } from '../stores/permissions';
|
||||||
import { addNotification } from '../stores/noti';
|
import { addNotification } from '../stores/noti';
|
||||||
|
import { recipeData, recipeOverviewData } from '../stores/recipeStore';
|
||||||
|
|
||||||
export async function getRecipes() {
|
export async function getRecipes() {
|
||||||
if (browser && !get(departmentStore)) {
|
if (browser && !get(departmentStore)) {
|
||||||
|
|
@ -31,7 +32,10 @@ export async function getRecipes() {
|
||||||
// construct path. fetch (GET) {server}/recipe/{countryTarget}/{version}
|
// construct path. fetch (GET) {server}/recipe/{countryTarget}/{version}
|
||||||
let idToken = await get(auth)?.getIdToken();
|
let idToken = await get(auth)?.getIdToken();
|
||||||
|
|
||||||
console.log('country target get recipe', country);
|
console.log('country target get recipe', countryTarget);
|
||||||
|
|
||||||
|
recipeData.set([]);
|
||||||
|
recipeOverviewData.set([]);
|
||||||
|
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: 'recipe',
|
type: 'recipe',
|
||||||
|
|
@ -46,3 +50,48 @@ export async function getRecipes() {
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getRecipeWithVersion(version: string) {
|
||||||
|
if (browser && !get(departmentStore)) {
|
||||||
|
console.log('cannot get dep', get(departmentStore));
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let countryTarget = get(departmentStore);
|
||||||
|
let country = '';
|
||||||
|
|
||||||
|
let countryPerm = `document.read.${countryTarget}`;
|
||||||
|
let user_perms = get(permission);
|
||||||
|
|
||||||
|
if (!user_perms.includes(countryPerm)) {
|
||||||
|
addNotification('ERR:Invalid permission');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!countryTarget && !browser) {
|
||||||
|
// countryTarget = extractCookieOnNonBrowser()['department'];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// construct path. fetch (GET) {server}/recipe/{countryTarget}/{version}
|
||||||
|
let idToken = await get(auth)?.getIdToken();
|
||||||
|
|
||||||
|
console.log('country target get recipe', countryTarget);
|
||||||
|
|
||||||
|
recipeData.set([]);
|
||||||
|
recipeOverviewData.set([]);
|
||||||
|
|
||||||
|
// NOTE: although version is provided, actual version field is still need to be latest
|
||||||
|
// Just in case version is not found
|
||||||
|
sendMessage({
|
||||||
|
type: 'recipe',
|
||||||
|
payload: {
|
||||||
|
auth: idToken ?? '',
|
||||||
|
partial: false,
|
||||||
|
country: countryTarget ?? '',
|
||||||
|
version: -1,
|
||||||
|
parameters: 'use_legacy_version=true,version=' + version
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { updateMachineStatus } from '../stores/machineInfoStore';
|
import { updateMachineStatus } from '../stores/machineInfoStore';
|
||||||
import { addNotification } from '../stores/noti';
|
import { addNotification } from '../stores/noti';
|
||||||
|
import { handleIncomingMessages } from './messageHandler';
|
||||||
|
|
||||||
type AdbPayload = { type: string; payload: any };
|
type AdbPayload = { type: string; payload: any };
|
||||||
|
|
||||||
|
|
@ -17,6 +18,26 @@ async function handleAdbPayload(raw_payload: string) {
|
||||||
case 'response':
|
case 'response':
|
||||||
if (payload.payload instanceof String) {
|
if (payload.payload instanceof String) {
|
||||||
// single message response
|
// single message response
|
||||||
|
let raw_payload = payload.payload.toString();
|
||||||
|
|
||||||
|
if (raw_payload.startsWith('save_recipe_machine')) {
|
||||||
|
let res = raw_payload.split('/');
|
||||||
|
|
||||||
|
let pd = res[1] ?? '';
|
||||||
|
let action = res[2] ?? '';
|
||||||
|
let uiAction = res[3] ?? '';
|
||||||
|
|
||||||
|
handleIncomingMessages(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'ui_action',
|
||||||
|
payload: {
|
||||||
|
action: uiAction,
|
||||||
|
from: 'brew',
|
||||||
|
ref: `${pd}.${action}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'ACK':
|
case 'ACK':
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { get, writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
import { addNotification, notiStore } from '../stores/noti';
|
import { addNotification, notiStore } from '../stores/noti';
|
||||||
import {
|
import {
|
||||||
|
currentRecipeVersionsSelector,
|
||||||
materialFromServerQuery,
|
materialFromServerQuery,
|
||||||
recipeData,
|
recipeData,
|
||||||
recipeDataError,
|
recipeDataError,
|
||||||
|
|
@ -12,6 +13,8 @@ import {
|
||||||
} from '../stores/recipeStore';
|
} from '../stores/recipeStore';
|
||||||
import { buildOverviewFromServer } from '$lib/data/recipeService';
|
import { buildOverviewFromServer } from '$lib/data/recipeService';
|
||||||
import { auth } from '../client/firebase';
|
import { auth } from '../client/firebase';
|
||||||
|
import { type RecipeVersion } from '$lib/models/recipe_version.model';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
export const messages = writable<string[]>([]);
|
export const messages = writable<string[]>([]);
|
||||||
|
|
||||||
|
|
@ -32,18 +35,30 @@ const handlers: Record<string, (payload: any) => void> = {
|
||||||
let stream_id = p.stream_id;
|
let stream_id = p.stream_id;
|
||||||
let total_size = p.total_size;
|
let total_size = p.total_size;
|
||||||
let chunk_size = p.chunk_size;
|
let chunk_size = p.chunk_size;
|
||||||
|
let data_meta = p.metadata;
|
||||||
|
|
||||||
if (stream_id) {
|
if (stream_id) {
|
||||||
addNotification('INFO:Start streaming data');
|
addNotification('INFO:Start streaming data');
|
||||||
|
|
||||||
|
let meta_list = data_meta?.split(',');
|
||||||
|
let version = meta_list[0]?.split('=')[1] ?? '';
|
||||||
|
let country = meta_list[1]?.split('=')[1] ?? '';
|
||||||
|
|
||||||
// recipeLoading.set(true);
|
// recipeLoading.set(true);
|
||||||
recipeStreamMeta.set({
|
recipeStreamMeta.set({
|
||||||
id: stream_id,
|
id: stream_id,
|
||||||
total_size: total_size,
|
total_size: total_size,
|
||||||
chunk_size: chunk_size,
|
chunk_size: chunk_size,
|
||||||
progress: 0
|
progress: 0,
|
||||||
|
version,
|
||||||
|
country
|
||||||
});
|
});
|
||||||
recipeData.set([]);
|
recipeData.set([]);
|
||||||
recipeOverviewData.set([]);
|
recipeOverviewData.set([]);
|
||||||
|
|
||||||
|
materialFromServerQuery.set([]);
|
||||||
|
toppingListFromServerQuery.set([]);
|
||||||
|
toppingGroupFromServerQuery.set([]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stream_data_error: (p) => {
|
stream_data_error: (p) => {
|
||||||
|
|
@ -56,6 +71,7 @@ const handlers: Record<string, (payload: any) => void> = {
|
||||||
},
|
},
|
||||||
stream_data_chunk: (p) => {
|
stream_data_chunk: (p) => {
|
||||||
let current_meta = get(recipeStreamMeta);
|
let current_meta = get(recipeStreamMeta);
|
||||||
|
// console.log('current meta', current_meta);
|
||||||
if (current_meta) {
|
if (current_meta) {
|
||||||
let stream_id = current_meta.id;
|
let stream_id = current_meta.id;
|
||||||
|
|
||||||
|
|
@ -92,6 +108,7 @@ const handlers: Record<string, (payload: any) => void> = {
|
||||||
// build overview for recipe from server
|
// build overview for recipe from server
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// console.log('ending stream');
|
||||||
buildOverviewFromServer();
|
buildOverviewFromServer();
|
||||||
},
|
},
|
||||||
stream_data_extra: (p) => {
|
stream_data_extra: (p) => {
|
||||||
|
|
@ -120,7 +137,7 @@ const handlers: Record<string, (payload: any) => void> = {
|
||||||
curr_mat_query.push(m);
|
curr_mat_query.push(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('current materials: ', JSON.stringify(curr_mat_query));
|
// // console.log('current materials: ', JSON.stringify(curr_mat_query));
|
||||||
materialFromServerQuery.set(curr_mat_query);
|
materialFromServerQuery.set(curr_mat_query);
|
||||||
break;
|
break;
|
||||||
case 'topplist':
|
case 'topplist':
|
||||||
|
|
@ -168,6 +185,28 @@ const handlers: Record<string, (payload: any) => void> = {
|
||||||
// broadcast to all
|
// broadcast to all
|
||||||
addNotification(`${noti_level}:${msg}`);
|
addNotification(`${noti_level}:${msg}`);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
ui_action: (p) => {
|
||||||
|
if (p.action == 'refreshNow' && p.from == 'brew') {
|
||||||
|
goto('/tools/brew');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
version_selectors: (p) => {
|
||||||
|
if (p.versions.length > 0) {
|
||||||
|
currentRecipeVersionsSelector.set([]);
|
||||||
|
let result: RecipeVersion[] = [];
|
||||||
|
|
||||||
|
for (let vstr of p.versions) {
|
||||||
|
let pure_version = vstr.split('_')[0];
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
display_version: pure_version,
|
||||||
|
actual_version_name: vstr
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRecipeVersionsSelector.set(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,25 +41,25 @@ export function sendCommandRequest(target: CommandRequest, values: any) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendMessage(msg: OutMessage): boolean {
|
export function sendMessage(msg: OutMessage, ignore_queue_request: boolean = true): boolean {
|
||||||
const socket = get(socketStore);
|
const socket = get(socketStore);
|
||||||
const data = JSON.stringify(msg);
|
const data = JSON.stringify(msg);
|
||||||
|
|
||||||
// console.log('try sending ', data);
|
// console.log('try sending ', data);
|
||||||
|
|
||||||
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
||||||
console.warn('WebSocket not connected, put to queue');
|
// console.warn('WebSocket not connected, put to queue');
|
||||||
|
|
||||||
let currentQueue = get(queue);
|
// let currentQueue = get(queue);
|
||||||
if (currentQueue.length >= 10) {
|
// if (currentQueue.length >= 10) {
|
||||||
while (currentQueue.length >= 10) {
|
// while (currentQueue.length >= 10) {
|
||||||
currentQueue.shift();
|
// currentQueue.shift();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
currentQueue.push(data);
|
// currentQueue.push(data);
|
||||||
queue.set(currentQueue);
|
// queue.set(currentQueue);
|
||||||
|
|
||||||
addNotification('WARN:Queuing request');
|
// if (!ignore_queue_request) addNotification('WARN:Queuing request');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import type { RecipeOverview } from '../../../routes/(authed)/recipe/overview/columns';
|
import type { RecipeOverview } from '../../../routes/(authed)/recipe/overview/columns';
|
||||||
import type { Material } from '$lib/models/material.model';
|
import type { Material } from '$lib/models/material.model';
|
||||||
|
import type { RecipeVersion } from '$lib/models/recipe_version.model';
|
||||||
|
|
||||||
export const recipeData = writable<any>(null);
|
export const recipeData = writable<any>(null);
|
||||||
export const recipeLoading = writable(false);
|
export const recipeLoading = writable(false);
|
||||||
|
|
@ -10,8 +11,12 @@ export const recipeStreamMeta = writable<{
|
||||||
total_size: number;
|
total_size: number;
|
||||||
chunk_size: number;
|
chunk_size: number;
|
||||||
progress: number;
|
progress: number;
|
||||||
|
version?: string;
|
||||||
|
country?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
|
export const currentRecipeVersionsSelector = writable<RecipeVersion[]>([]);
|
||||||
|
|
||||||
// from server
|
// from server
|
||||||
export const recipeOverviewData = writable<RecipeOverview[] | null>(null);
|
export const recipeOverviewData = writable<RecipeOverview[] | null>(null);
|
||||||
export const materialData = writable<Material | undefined>();
|
export const materialData = writable<Material | undefined>();
|
||||||
|
|
@ -45,41 +50,3 @@ export const materialFromMachineQuery = writable<any>({});
|
||||||
|
|
||||||
export const referenceFromPage = writable<string>('');
|
export const referenceFromPage = writable<string>('');
|
||||||
export const currentEditingRecipeProductCode = writable<string>('');
|
export const currentEditingRecipeProductCode = writable<string>('');
|
||||||
|
|
||||||
let worker: Worker | null = null;
|
|
||||||
let initialized = false;
|
|
||||||
|
|
||||||
export function loadRecipe(url: string) {
|
|
||||||
if (initialized) return;
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
recipeLoading.set(true);
|
|
||||||
|
|
||||||
worker = new Worker(new URL('../../workers/data.worker.ts', import.meta.url), {
|
|
||||||
type: 'module'
|
|
||||||
});
|
|
||||||
|
|
||||||
worker.onmessage = (e) => {
|
|
||||||
const { type, payload } = e.data;
|
|
||||||
if (type === 'data') {
|
|
||||||
recipeData.set(payload);
|
|
||||||
recipeLoading.set(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'error') {
|
|
||||||
recipeDataError.set(payload);
|
|
||||||
recipeLoading.set(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
worker.postMessage({ url });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWorker() {
|
|
||||||
if (!worker) {
|
|
||||||
worker = new Worker(new URL('../../workers/data.worker.ts', import.meta.url), {
|
|
||||||
type: 'module'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return worker;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -43,15 +43,15 @@ export function connectToWebsocket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// recover messages on connect, flushing
|
// recover messages on connect, flushing
|
||||||
while (get(msgQueue).length) {
|
// while (get(msgQueue).length) {
|
||||||
let queue = get(msgQueue);
|
// let queue = get(msgQueue);
|
||||||
let current = queue.shift();
|
// let current = queue.shift();
|
||||||
if (current && socket) {
|
// if (current && socket) {
|
||||||
socket.send(current);
|
// socket.send(current);
|
||||||
// set next
|
// // set next
|
||||||
msgQueue.set(queue);
|
// msgQueue.set(queue);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// heartbeat 10s
|
// heartbeat 10s
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ export type OutMessage =
|
||||||
| { type: 'lock'; payload: { field: string } }
|
| { type: 'lock'; payload: { field: string } }
|
||||||
| { type: 'general'; payload: string }
|
| { type: 'general'; payload: string }
|
||||||
| {
|
| {
|
||||||
type: 'recipe';
|
type: 'recipe' | 'recipe_versions';
|
||||||
payload: {
|
payload: {
|
||||||
auth: string;
|
auth: string;
|
||||||
partial: boolean;
|
partial: boolean;
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,10 @@ function buildOverviewFromServer() {
|
||||||
|
|
||||||
let r01_query: any = {};
|
let r01_query: any = {};
|
||||||
|
|
||||||
if (server_recipe01) {
|
// console.log('server recipe 01: ', server_recipe01);
|
||||||
|
|
||||||
|
if (server_recipe01 != null) {
|
||||||
|
// console.log('building overview from server');
|
||||||
recipeOverviewData.set([]);
|
recipeOverviewData.set([]);
|
||||||
for (let rp of server_recipe01) {
|
for (let rp of server_recipe01) {
|
||||||
result.push({
|
result.push({
|
||||||
|
|
|
||||||
4
src/lib/models/recipe_version.model.ts
Normal file
4
src/lib/models/recipe_version.model.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface RecipeVersion {
|
||||||
|
display_version: string;
|
||||||
|
actual_version_name: string;
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
addNotification(`INFO:Selected ${cnt}`);
|
addNotification(`INFO:Selected ${cnt}`);
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
console.log(get(departmentStore));
|
console.log(get(departmentStore));
|
||||||
|
departmentStore.set(cnt);
|
||||||
|
|
||||||
if (refPage === 'sheet') {
|
if (refPage === 'sheet') {
|
||||||
await goto('/sheet/overview');
|
await goto('/sheet/overview');
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,30 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Button from '$lib/components/ui/button/button.svelte';
|
import Button, { buttonVariants } from '$lib/components/ui/button/button.svelte';
|
||||||
import Input from '$lib/components/ui/input/input.svelte';
|
import Input from '$lib/components/ui/input/input.svelte';
|
||||||
import { SearchIcon } from '@lucide/svelte/icons';
|
import { SearchIcon, RefreshCcw } from '@lucide/svelte/icons';
|
||||||
import Spinner from '$lib/components/ui/spinner/spinner.svelte';
|
import Spinner from '$lib/components/ui/spinner/spinner.svelte';
|
||||||
|
import * as Dialog from '$lib/components/ui/dialog/index';
|
||||||
|
import Label from '$lib/components/ui/label/label.svelte';
|
||||||
|
import * as Select from '$lib/components/ui/select/index';
|
||||||
|
|
||||||
import DataTable from './data-table.svelte';
|
import DataTable from './data-table.svelte';
|
||||||
import { columns, type RecipeOverview } from './columns';
|
import { columns, type RecipeOverview } from './columns';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import {
|
import {
|
||||||
loadRecipe,
|
currentRecipeVersionsSelector,
|
||||||
recipeData,
|
recipeData,
|
||||||
recipeFromServerQuery,
|
recipeFromServerQuery,
|
||||||
recipeLoading,
|
recipeLoading,
|
||||||
recipeOverviewData,
|
recipeOverviewData,
|
||||||
|
recipeStreamMeta,
|
||||||
referenceFromPage
|
referenceFromPage
|
||||||
} from '$lib/core/stores/recipeStore.js';
|
} from '$lib/core/stores/recipeStore.js';
|
||||||
import { sendMessage } from '$lib/core/handlers/ws_messageSender.js';
|
import { sendMessage } from '$lib/core/handlers/ws_messageSender.js';
|
||||||
import { auth } from '$lib/core/stores/auth.js';
|
import { auth } from '$lib/core/stores/auth.js';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { getRecipes } from '$lib/core/client/server.js';
|
import { getRecipes, getRecipeWithVersion } from '$lib/core/client/server.js';
|
||||||
import { permission } from '$lib/core/stores/permissions';
|
import { permission } from '$lib/core/stores/permissions';
|
||||||
|
import type { RecipeVersion } from '$lib/models/recipe_version.model';
|
||||||
|
|
||||||
let data: { recipes: RecipeOverview[] } = $state({
|
let data: { recipes: RecipeOverview[] } = $state({
|
||||||
recipes: []
|
recipes: []
|
||||||
|
|
@ -27,15 +32,52 @@
|
||||||
|
|
||||||
let isRecipeLoading = $state(false);
|
let isRecipeLoading = $state(false);
|
||||||
|
|
||||||
|
let version: string = $state('');
|
||||||
|
let country: string = $state('');
|
||||||
|
|
||||||
|
let value = $state('');
|
||||||
|
let version_list: RecipeVersion[] = $state([]);
|
||||||
|
|
||||||
|
const triggerVersion = $derived(
|
||||||
|
version_list.find((f) => f.actual_version_name === value)?.display_version ?? version
|
||||||
|
);
|
||||||
|
|
||||||
let unsubRecipeData = recipeOverviewData.subscribe((rd) => {
|
let unsubRecipeData = recipeOverviewData.subscribe((rd) => {
|
||||||
if (rd) {
|
if (rd) {
|
||||||
data.recipes = rd == null ? [] : rd;
|
data.recipes = rd == null ? [] : rd;
|
||||||
|
|
||||||
|
let meta_snap = $state.snapshot(get(recipeStreamMeta));
|
||||||
|
country = meta_snap?.country ?? '...';
|
||||||
|
version = (meta_snap?.version ?? '...').split('_')[0];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let unsubRecipeVersion = currentRecipeVersionsSelector.subscribe((vs) => {
|
||||||
|
if (vs.length > 0) {
|
||||||
|
version_list = vs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function sendGetRecipeVersions(country: string) {
|
||||||
|
version_list = [];
|
||||||
|
sendMessage({
|
||||||
|
type: 'recipe_versions',
|
||||||
|
payload: {
|
||||||
|
auth: '',
|
||||||
|
partial: false,
|
||||||
|
country: country,
|
||||||
|
version: -1,
|
||||||
|
parameters: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendGetRecipeByVersionSelector() {
|
||||||
|
getRecipeWithVersion(value);
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// do load recipe
|
// do load recipe
|
||||||
// loadRecipe();
|
|
||||||
referenceFromPage.set('overview');
|
referenceFromPage.set('overview');
|
||||||
|
|
||||||
await getRecipes();
|
await getRecipes();
|
||||||
|
|
@ -43,6 +85,7 @@
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
unsubRecipeData();
|
unsubRecipeData();
|
||||||
|
unsubRecipeVersion();
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|
@ -57,6 +100,9 @@
|
||||||
// setTimeout(() => recipeLoading.set(false), 3000);
|
// setTimeout(() => recipeLoading.set(false), 3000);
|
||||||
}
|
}
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
||||||
|
version_list = get(currentRecipeVersionsSelector);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(recipeFetchInterval);
|
clearInterval(recipeFetchInterval);
|
||||||
};
|
};
|
||||||
|
|
@ -68,10 +114,64 @@
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="m-8 text-4xl font-bold">Overview</h1>
|
<h1 class="m-8 text-4xl font-bold">Overview [ {country} ] version [ {version} ]</h1>
|
||||||
|
|
||||||
|
<div class="flex flex-row">
|
||||||
<p class="mx-8 my-0 text-muted-foreground">
|
<p class="mx-8 my-0 text-muted-foreground">
|
||||||
Display menus from the current selected country
|
Display menus from the current selected country
|
||||||
</p>
|
</p>
|
||||||
|
<!-- <Button variant="default" onclick={() => sendGetRecipeVersions(country)}>
|
||||||
|
<span class="flex flex-row gap-2"> <RefreshCcw /> Change Version </span>
|
||||||
|
</Button> -->
|
||||||
|
|
||||||
|
<!-- dialog select version -->
|
||||||
|
|
||||||
|
<Dialog.Root>
|
||||||
|
<Dialog.Trigger
|
||||||
|
type="button"
|
||||||
|
class={buttonVariants({ variant: 'outline' })}
|
||||||
|
onclick={() => sendGetRecipeVersions(country)}
|
||||||
|
>
|
||||||
|
<span class="flex flex-row gap-2"> <RefreshCcw /> Change Version </span>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
|
||||||
|
<Dialog.Content class="sm:max-w-[425px]">
|
||||||
|
<Dialog.Header>
|
||||||
|
<Dialog.Title>Select Legacy Versions</Dialog.Title>
|
||||||
|
<Dialog.Description>Viewing the previous version of recipe.</Dialog.Description>
|
||||||
|
</Dialog.Header>
|
||||||
|
<div class="grid gap-4">
|
||||||
|
<!-- <div class="grid gap-3">
|
||||||
|
<Label for="name-1">Version</Label>
|
||||||
|
<Input id="name-1" name="name" defaultValue="Pedro Duarte" />
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<Select.Root type="single" name="selectVersion" bind:value>
|
||||||
|
<Select.Trigger class="w-[180px]">
|
||||||
|
{triggerVersion}
|
||||||
|
</Select.Trigger>
|
||||||
|
|
||||||
|
<Select.Content>
|
||||||
|
{#each version_list as version_n (version_n.actual_version_name)}
|
||||||
|
<Select.Item
|
||||||
|
value={version_n.actual_version_name}
|
||||||
|
label={version_n.display_version}
|
||||||
|
>
|
||||||
|
{version_n.display_version}
|
||||||
|
</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
</div>
|
||||||
|
<Dialog.Footer>
|
||||||
|
<Dialog.Close type="button" class={buttonVariants({ variant: 'outline' })}>
|
||||||
|
Cancel
|
||||||
|
</Dialog.Close>
|
||||||
|
<Button type="submit" onclick={() => sendGetRecipeByVersionSelector()}>Ok</Button>
|
||||||
|
</Dialog.Footer>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-8 my-4 flex gap-2">
|
<div class="mx-8 my-4 flex gap-2">
|
||||||
<Button variant="default">+ Create Menu</Button>
|
<Button variant="default">+ Create Menu</Button>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import {
|
import {
|
||||||
loadRecipe,
|
|
||||||
recipeData,
|
recipeData,
|
||||||
recipeFromServerQuery,
|
recipeFromServerQuery,
|
||||||
recipeOverviewData,
|
recipeOverviewData,
|
||||||
|
|
@ -21,7 +20,6 @@
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// do load recipe
|
// do load recipe
|
||||||
// loadRecipe();
|
|
||||||
refDepartment = get(departmentStore);
|
refDepartment = get(departmentStore);
|
||||||
referenceFromPage.set('overview');
|
referenceFromPage.set('overview');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
import { deviceCredentialManager } from '$lib/core/adb/deviceCredManager';
|
import { deviceCredentialManager } from '$lib/core/adb/deviceCredManager';
|
||||||
import { afterNavigate } from '$app/navigation';
|
import { afterNavigate } from '$app/navigation';
|
||||||
import { env } from '$env/dynamic/public';
|
import { env } from '$env/dynamic/public';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const sourceDir = '/sdcard/coffeevending';
|
const sourceDir = '/sdcard/coffeevending';
|
||||||
|
|
||||||
|
|
@ -41,6 +42,9 @@
|
||||||
|
|
||||||
let brew_status: string = $state('');
|
let brew_status: string = $state('');
|
||||||
|
|
||||||
|
// refresh command
|
||||||
|
let refresh_counter: number = $state(0);
|
||||||
|
|
||||||
async function startFetchRecipeFromMachine() {
|
async function startFetchRecipeFromMachine() {
|
||||||
let instance = adb.getAdbInstance();
|
let instance = adb.getAdbInstance();
|
||||||
// recipeFromMachineLoading.set(true);
|
// recipeFromMachineLoading.set(true);
|
||||||
|
|
@ -49,7 +53,7 @@
|
||||||
if (instance) {
|
if (instance) {
|
||||||
console.log('instance passed!');
|
console.log('instance passed!');
|
||||||
let dev_recipe = await adb.pull(`${sourceDir}/cfg/recipe_branch_dev.json`);
|
let dev_recipe = await adb.pull(`${sourceDir}/cfg/recipe_branch_dev.json`);
|
||||||
console.log('dev recipe ok', dev_recipe != undefined);
|
console.log('dev recipe ok', dev_recipe != undefined, dev_recipe);
|
||||||
if (dev_recipe) {
|
if (dev_recipe) {
|
||||||
if (dev_recipe.length == 0) {
|
if (dev_recipe.length == 0) {
|
||||||
// case error, do last retry
|
// case error, do last retry
|
||||||
|
|
@ -69,7 +73,7 @@
|
||||||
// from recipe_branch_dev
|
// from recipe_branch_dev
|
||||||
devRecipe = JSON.parse(dev_recipe);
|
devRecipe = JSON.parse(dev_recipe);
|
||||||
// recipeFromMachineLoading.set(false);
|
// recipeFromMachineLoading.set(false);
|
||||||
addNotification('INFO:Fetch recipe success!');
|
// addNotification('INFO:Fetch recipe success!');
|
||||||
|
|
||||||
buildOverviewForBrewing();
|
buildOverviewForBrewing();
|
||||||
}
|
}
|
||||||
|
|
@ -176,6 +180,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
afterNavigate(async () => {
|
afterNavigate(async () => {
|
||||||
|
console.log('after navigate brew');
|
||||||
await startFetchRecipeFromMachine();
|
await startFetchRecipeFromMachine();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -314,14 +319,14 @@
|
||||||
|
|
||||||
let currentQuery = get(recipeFromMachineQuery);
|
let currentQuery = get(recipeFromMachineQuery);
|
||||||
currentQuery = {
|
currentQuery = {
|
||||||
recipe: recipe01_query,
|
...currentQuery,
|
||||||
...currentQuery
|
recipe: recipe01_query
|
||||||
};
|
};
|
||||||
|
|
||||||
let currentMaterialsQuery = materialFromMachine;
|
let currentMaterialsQuery = materialFromMachine;
|
||||||
currentQuery = {
|
currentQuery = {
|
||||||
material: currentMaterialsQuery,
|
...currentQuery,
|
||||||
...currentQuery
|
material: currentMaterialsQuery
|
||||||
};
|
};
|
||||||
|
|
||||||
recipeFromMachineQuery.set(currentQuery);
|
recipeFromMachineQuery.set(currentQuery);
|
||||||
|
|
@ -404,6 +409,7 @@
|
||||||
|
|
||||||
<!-- search bar -->
|
<!-- search bar -->
|
||||||
|
|
||||||
|
{#key refresh_counter}
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
{#if $recipeFromMachineLoading}
|
{#if $recipeFromMachineLoading}
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
|
|
@ -414,5 +420,6 @@
|
||||||
<DataTable data={data.recipes} refPage="brew" {columns} />
|
<DataTable data={data.recipes} refPage="brew" {columns} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue