-
-
-
-
- Reboot Machine?
-
- New {mode} prices only take effect after a full device reboot. The machine
- will restart — this takes a few minutes.
-
-
-
-
-
-
-
-
diff --git a/src/lib/core/handlers/messageHandler.ts b/src/lib/core/handlers/messageHandler.ts
index aa3350e..1bd2932 100644
--- a/src/lib/core/handlers/messageHandler.ts
+++ b/src/lib/core/handlers/messageHandler.ts
@@ -261,23 +261,14 @@ const handlers: Record void> = {
// Handle gen-service responses
if (from === 'gen-service') {
switch (level) {
- case 'batch_start': {
- const genRef = p.ref ?? 'gen-layout';
+ case 'batch_start':
handleGenLayoutBatchStart({
batch_id: p.batch_id,
total_files: p.total_files,
- total_size_bytes: p.total_size_bytes,
- ref: genRef
+ total_size_bytes: p.total_size_bytes
});
- const genLabel =
- genRef === 'gen-price'
- ? 'Gen Price'
- : genRef === 'gen-priceslot'
- ? 'Gen PriceSlot'
- : 'Gen Layout';
- addNotification(`INFO:${genLabel} started (${p.total_files} files)`);
+ addNotification(`INFO:Gen Layout started (${p.total_files} files)`);
break;
- }
case 'file':
handleGenLayoutFile({
batch_id: p.batch_id,
@@ -296,11 +287,11 @@ const handlers: Record void> = {
batch_id: p.batch_id,
total_files: p.total_files
});
- addNotification('INFO:Generation complete');
+ addNotification('INFO:Gen Layout complete');
break;
case 'ERROR':
handleGenLayoutError(msg);
- addNotification(`ERR:Generation error: ${msg}`);
+ addNotification(`ERR:Gen Layout error: ${msg}`);
break;
default:
logger.info('[GenService] Received:', level, msg);
diff --git a/src/lib/core/services/sheetService.ts b/src/lib/core/services/sheetService.ts
index 7cb0d05..2034fb7 100644
--- a/src/lib/core/services/sheetService.ts
+++ b/src/lib/core/services/sheetService.ts
@@ -325,7 +325,7 @@ export async function requestGenLayout(country: string): Promise {
};
}
- setGenLayoutGenerating('gen-layout');
+ setGenLayoutGenerating();
logger.info('[sheetService] Sending gen-layout request for country:', country);
@@ -344,79 +344,6 @@ export async function requestGenLayout(country: string): Promise {
});
}
-/**
- * Trigger gen-service to (re)generate the machine price profile from Grist.
- * Backend: taobin_admin app.py `gen-price` branch -> gen_price_supra.gen_price.
- * Produces profile__master.json, commits to recipe-repo, streams files back
- * (ref='gen-price') for the frontend to push to the machine.
- */
-export async function requestGenPrice(country: string): Promise {
- const curr_user = get(auth);
-
- let user_info: any = {};
- if (curr_user) {
- user_info = {
- displayName: curr_user.displayName,
- email: curr_user.email,
- uid: curr_user.uid
- };
- }
-
- setGenLayoutGenerating('gen-price');
-
- logger.info('[sheetService] Sending gen-price request for country:', country);
-
- return await sendMessage({
- type: 'command',
- payload: {
- user_info,
- srv_name: 'gen-service',
- values: {
- file_layout: 'sheet',
- file_desc: 'sheet',
- country: country,
- param: 'gen-price-supra_app'
- }
- }
- });
-}
-
-/**
- * Trigger gen-service to (re)generate the per-slot price profiles from Grist.
- * Backend: taobin_admin app.py `gen-priceslot` branch -> gen_price_supra.gen_priceslot.
- * Produces profile__slot_.json (ref='gen-priceslot').
- */
-export async function requestGenPriceSlot(country: string): Promise {
- const curr_user = get(auth);
-
- let user_info: any = {};
- if (curr_user) {
- user_info = {
- displayName: curr_user.displayName,
- email: curr_user.email,
- uid: curr_user.uid
- };
- }
-
- setGenLayoutGenerating('gen-priceslot');
-
- logger.info('[sheetService] Sending gen-priceslot request for country:', country);
-
- return await sendMessage({
- type: 'command',
- payload: {
- user_info,
- srv_name: 'gen-service',
- values: {
- file_layout: 'sheet',
- file_desc: 'sheet',
- country: country,
- param: 'gen-priceslot-supra_app'
- }
- }
- });
-}
-
/**
* Request price data from sheet for specific product codes
* NOTE: Can only send once per type (price). Use hasSheetPriceBeenSent to check.
diff --git a/src/lib/core/stores/genLayoutStore.ts b/src/lib/core/stores/genLayoutStore.ts
index ccf811c..d0a5e7c 100644
--- a/src/lib/core/stores/genLayoutStore.ts
+++ b/src/lib/core/stores/genLayoutStore.ts
@@ -7,20 +7,12 @@ export interface GenLayoutFile {
file_index: number;
}
-// Kind of gen-service batch. Distinguishes which files were generated so the
-// push logic can map them to the correct machine path.
-// gen-layout -> layout files under taobin_project/
-// gen-price -> profile__master.json -> /sdcard/coffeevending/profile/
-// gen-priceslot -> profile__slot_.json -> .../price/
-export type GenKind = 'gen-layout' | 'gen-price' | 'gen-priceslot';
-
export interface GenLayoutBatch {
batch_id: string;
total_files: number;
total_size_bytes: number;
status: 'idle' | 'generating' | 'receiving' | 'complete' | 'error';
files: GenLayoutFile[];
- ref: GenKind;
error?: string;
}
@@ -37,8 +29,7 @@ const initialState: GenLayoutBatch = {
total_files: 0,
total_size_bytes: 0,
status: 'idle',
- files: [],
- ref: 'gen-layout'
+ files: []
};
export const genLayoutBatch = writable(initialState);
@@ -61,17 +52,15 @@ export function handleGenLayoutBatchStart(payload: {
batch_id: string;
total_files: number;
total_size_bytes: number;
- ref?: GenKind;
}) {
genLayoutBatch.set({
batch_id: payload.batch_id,
total_files: payload.total_files,
total_size_bytes: payload.total_size_bytes,
status: 'receiving',
- files: [],
- ref: payload.ref ?? get(genLayoutBatch).ref ?? 'gen-layout'
+ files: []
});
- logger.info('[GenLayout] Batch started:', payload.batch_id, 'ref:', payload.ref, 'total files:', payload.total_files);
+ logger.info('[GenLayout] Batch started:', payload.batch_id, 'total files:', payload.total_files);
}
export function handleGenLayoutFile(payload: {
@@ -90,10 +79,7 @@ export function handleGenLayoutFile(payload: {
if (payload.is_chunked) {
const fileIndex = payload.file_index;
- // Backend sends part_index 1-based (1..N); normalize to 0-based so it
- // matches the 0..totalParts-1 assembly loop below. Without this, part 1
- // was stored empty and the last chunk was dropped (files > 1MB corrupted).
- const partIndex = (payload.part_index ?? 1) - 1;
+ const partIndex = payload.part_index ?? 0;
const totalParts = payload.total_parts ?? 1;
// Get or create tracker for this file
@@ -256,10 +242,9 @@ export function resetGenLayoutBatch() {
chunkedFiles.clear();
}
-export function setGenLayoutGenerating(ref: GenKind = 'gen-layout') {
+export function setGenLayoutGenerating() {
genLayoutBatch.update((batch) => ({
...batch,
- ref,
status: 'generating'
}));
}
diff --git a/src/routes/(authed)/sheet/price/[country]/+page.svelte b/src/routes/(authed)/sheet/price/[country]/+page.svelte
index 96a1a8a..3667b69 100644
--- a/src/routes/(authed)/sheet/price/[country]/+page.svelte
+++ b/src/routes/(authed)/sheet/price/[country]/+page.svelte
@@ -24,7 +24,6 @@
import * as Table from '$lib/components/ui/table/index.js';
import Spinner from '$lib/components/ui/spinner/spinner.svelte';
import { Plus, RefreshCw, Save } from '@lucide/svelte/icons';
- import GenPricePanel from '$lib/components/gen-price-panel.svelte';
let selectedCountry = $state($page.params.country || get(departmentStore) || '');
let search = $state('');
@@ -212,12 +211,6 @@
- {#if selectedCountry}
-
-
-
- {/if}
-
diff --git a/src/routes/(authed)/tools/video-mainpage/+page.svelte b/src/routes/(authed)/tools/video-mainpage/+page.svelte
index 010fa54..4848f50 100644
--- a/src/routes/(authed)/tools/video-mainpage/+page.svelte
+++ b/src/routes/(authed)/tools/video-mainpage/+page.svelte
@@ -10,7 +10,6 @@
import Badge from '$lib/components/ui/badge/badge.svelte';
import Spinner from '$lib/components/ui/spinner/spinner.svelte';
import Progress from '$lib/components/ui/progress/progress.svelte';
- import { Checkbox } from '$lib/components/ui/checkbox/index.js';
import {
Upload,
X,
@@ -23,8 +22,7 @@
CalendarDays,
Clock,
ChevronDown,
- ImageIcon,
- FlaskConical
+ ImageIcon
} from '@lucide/svelte/icons';
import * as adb from '$lib/core/adb/adb';
import { env } from '$env/dynamic/public';
@@ -34,7 +32,6 @@
const CREATE_ENDPOINT = '/api/video-mainpage';
const LIST_ENDPOINT = '/api/video-mainpage/list';
const UPDATE_ENDPOINT = '/api/video-mainpage/update';
- const TESTMODE_ENDPOINT = '/api/video-mainpage/testmode';
const MACHINE_PROJECT_DIR = '/sdcard/coffeevending/taobin_project';
const GET_IMAGE = env.PUBLIC_GET_IMAGE;
const DURATION_TRIM = 4; // brewing play seconds = video length − 4
@@ -106,26 +103,6 @@
let pushProgress = $state({ percent: 0, name: '', active: false });
let isAdbConnected = $derived(Boolean(AdbInstance.instance));
- // ── test mode (machine-local date/time override) ─────────────────────────
- // '' = unset (leave that clock field on the real value).
- let testMonth = $state('');
- let testDay = $state('');
- let testHour = $state('');
- let testMinute = $state('');
- let testEnabled = $state(true); // emits `Var TestMode = 1` so the gate fires
- let applyingTest = $state(false);
- const hasTestValues = $derived(
- [testMonth, testDay, testHour, testMinute].some((v) => v !== '')
- );
-
- // Dropdown options. Hour/minute are zero-padded for display; the value is the
- // plain number string (the backend parses it with int()).
- const MONTH_OPTS = Array.from({ length: 12 }, (_, i) => String(i + 1));
- const DAY_OPTS = Array.from({ length: 31 }, (_, i) => String(i + 1));
- const HOUR_OPTS = Array.from({ length: 24 }, (_, i) => String(i));
- const MINUTE_OPTS = Array.from({ length: 60 }, (_, i) => String(i));
- const pad2 = (s: string) => s.padStart(2, '0');
-
// ── list ────────────────────────────────────────────────────────────────
let managed = $state([]);
let readonlyList = $state([]);
@@ -309,49 +286,6 @@
}
}
- // Inject (or, when `clear`, remove) the TestMode date/time override in the
- // header of the web scripts and push them to the connected machine over ADB.
- // Never touches git/FTP — this override is machine-local by design.
- async function applyTestMode(clear = false) {
- if (!AdbInstance.instance) return addNotification('ERR:Connect a machine first');
- const body = clear
- ? {}
- : {
- enabled: testEnabled,
- month: testMonth,
- day: testDay,
- hour: testHour,
- minute: testMinute
- };
- applyingTest = true;
- pushProgress = { percent: 0, name: '', active: true };
- try {
- const res = await fetch(`${TESTMODE_ENDPOINT}?country=${encodeURIComponent(country)}`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(body)
- });
- const result = await res.json().catch(() => ({}));
- if (!res.ok) throw new Error(result?.message || result?.detail || 'Test-mode apply failed');
- await pushScripts(result?.content?.scripts ?? []);
- if (result?.cleared) {
- testMonth = testDay = testHour = testMinute = '';
- testEnabled = false;
- addNotification(`INFO:Test mode cleared on machine (${country})`);
- } else {
- const o = result?.override ?? {};
- addNotification(
- `INFO:Test mode pushed to machine — ${o.month ?? '–'}/${o.day ?? '–'} ${o.hour ?? '–'}:${o.minute ?? '–'} (${country})`
- );
- }
- } catch (error) {
- addNotification(`ERR:${error instanceof Error ? error.message : 'Test-mode failed'}`);
- } finally {
- applyingTest = false;
- pushProgress = { percent: 0, name: '', active: false };
- }
- }
-
async function handleSubmit() {
const user = $auth;
if (!user) return addNotification('ERR:Not logged in');
@@ -402,15 +336,9 @@
);
await pushScripts(result?.content?.scripts ?? []);
- if (result?.duplicate) {
- addNotification(
- `WARN:"${name.trim()}" already exists as brewing_adv${result.n} — pushed to machine, no duplicate created`
- );
- } else {
- if (result?.sftp?.error)
- addNotification(`WARN:Uploaded but FTP sync failed: ${result.sftp.error}`);
- addNotification(`INFO:Added "${name.trim()}" as brewing_adv${result.n} (${country})`);
- }
+ if (result?.sftp?.error)
+ addNotification(`WARN:Uploaded but FTP sync failed: ${result.sftp.error}`);
+ addNotification(`INFO:Added "${name.trim()}" as brewing_adv${result.n} (${country})`);
clearMain();
clearBrewing();
clearBrewingTxt();
@@ -781,100 +709,6 @@
-
-
-
-
- Test mode
-
-
- Override the machine clock (AdvSystem*) to preview
- date-gated videos. Gated by TestMode = 1 and pushed to
- the connected machine only — never committed or synced. Fill only what you need.
-
-
-
-