import { logger } from '$lib/core/utils/logger'; import { sendCommandRequest, sendMessage } from '../handlers/ws_messageSender'; import { get } from 'svelte/store'; import { auth } from '../stores/auth'; import { productCodesLoading, hasSheetPriceBeenSent, markSheetPriceAsSent, sheetPriceLoading, streamingRawData, setPendingProductCodesCountry, setPendingPriceSlotsCountry, priceSlotsLoading, resetPriceSlotsCountry } from '../stores/sheetStore'; import type { PriceSlot } from '../stores/sheetStore'; import { setGenLayoutGenerating } from '../stores/genLayoutStore'; type SheetCellUpdate = { value: string; coord: { row: number; col: number } }; type SheetRowUpdate = { row_index: number; cells: SheetCellUpdate[] }; type SheetRowCreate = { header?: string[]; cells: string[] }; export async function requestCatalogs(country: string): Promise { return await sendCommandRequest('sheet', { country: country, param: 'catalogs' }); } /** * Register a newly created catalog as a Grist table so it shows in the overview * and menus can be added to it. `catalog` is the .skt filename produced by * /api/catalog-create (e.g. "page_catalog_group_pro_summer_splash.skt"). */ export async function addCatalog( country: string, catalogName: string, catalog: string ): Promise { return await sendCommandRequest('sheet', { country: country, catalog: catalog, catalog_name: catalogName, param: 'add/catalog' }); } export async function requestPriceSlots(country: string): Promise { setPendingPriceSlotsCountry(country); resetPriceSlotsCountry(country); return requestPriceSlotOption(country, 'PriceSlot'); } export async function requestPriceSlot(country: string, slotNumber: number): Promise { setPendingPriceSlotsCountry(country); return requestPriceSlotOption(country, `PriceSlot${slotNumber}`); } async function requestPriceSlotOption(country: string, option: string): Promise { const request_id = crypto.randomUUID(); streamingRawData.update((data) => ({ ...data, priceslot: { request_id, country, chunks: [], rawParts: [] } })); priceSlotsLoading.set(true); const values = { country: country, param: 'price', option, stream: true, request_id }; console.log('[sheetService] Sending PriceSlot request:', values); const sent = await sendCommandRequest('sheet', values); console.log('[sheetService] PriceSlot request sent:', sent); if (!sent) { priceSlotsLoading.set(false); } return sent; } export async function refreshPriceSlotList(country: string): Promise { return requestPriceSlotOption(country, 'PriceSlot'); } export async function updatePriceSlot( country: string, slot: PriceSlot, content: SheetRowUpdate[] ): Promise { // console.log('[sheetService] Sending PriceSlot update:', { // country, // slot: slot.slot, // name: slot.name, // description: slot.description, // kind: slot.kind, // rows: content.length, // param: 'update/price', // option: `PriceSlot${slot.slot}` // }); const sent = await sendCommandRequest('sheet', { country: country, content: content, param: 'update/price', option: `PriceSlot${slot.slot}` }); console.log('[sheetService] PriceSlot update sent:', { country, slot: slot.slot, sent }); return sent; } export async function addPriceSlot( country: string, slot: PriceSlot, content: SheetRowCreate[] ): Promise { console.log('[sheetService] Sending PriceSlot create:', { country, slot: slot.slot, name: slot.name, description: slot.description, kind: slot.kind, rows: content.length, param: 'add/price', option: `PriceSlot${slot.slot}` }); const sent = await sendCommandRequest('sheet', { country: country, content: content, param: 'add/price', option: `PriceSlot${slot.slot}` }); console.log('[sheetService] PriceSlot create sent:', { country, slot: slot.slot, sent }); return sent; } export async function addPriceSlotRows( country: string, slot: PriceSlot, content: SheetRowCreate[] ): Promise { if (!content || content.length === 0) return true; const sent = await sendCommandRequest('sheet', { country: country, content: content, param: 'add/price', option: `PriceSlot${slot.slot}` }); console.log('[sheetService] PriceSlot rows add sent:', { country, slot: slot.slot, rows: content.length, sent }); return sent; } export async function deletePriceSlotRows( country: string, slot: PriceSlot, rowIds: number[] ): Promise { if (!rowIds || rowIds.length === 0) return true; const sent = await sendCommandRequest('sheet', { country: country, content: rowIds.map((target_id) => ({ target_id })), param: 'delete/price', option: `PriceSlot${slot.slot}` }); console.log('[sheetService] PriceSlot rows delete sent:', { country, slot: slot.slot, rows: rowIds.length, sent }); return sent; } export async function enterRoom(country: string, catalog: string): Promise { return await sendCommandRequest('sheet', { country: country, catalog: catalog, param: 'enter' }); } export async function sendHeartbeat(country: string, catalog: string): Promise { return await sendCommandRequest('sheet', { country: country, catalog: catalog, param: 'heartbeat' }); } export async function exitRoom(country: string, catalog: string): Promise { return await sendCommandRequest('sheet', { country: country, catalog: catalog, param: 'exit' }); } export async function requestCatalogMenu(country: string, catalog: string): Promise { return await sendCommandRequest('sheet', { country: country, catalog: catalog, param: 'catalog/menu' }); } export async function updateMenu( country: string, catalog: string, content: any[] ): Promise { return await sendCommandRequest('sheet', { country: country, catalog: catalog, content: content, param: 'update/menu' }); } export async function addMenu(country: string, catalog: string, content: any[]): Promise { logger.info('[sheetService] Adding menu:', { country, catalog, content }); const sent = await sendCommandRequest('sheet', { country: country, catalog: catalog, content: content, param: 'add/menu' }); logger.info('[sheetService] Add menu sent:', sent); return sent; } export async function deleteMenu( country: string, catalog: string, targetIds: number[] ): Promise { const content = targetIds.map((id) => ({ target_id: id })); return await sendCommandRequest('sheet', { country: country, catalog: catalog, content: content, param: 'delete/menu' }); } export async function swapMenu( country: string, catalog: string, swaps: { source_id: number; target_id: number }[] ): Promise { return await sendCommandRequest('sheet', { country: country, catalog: catalog, content: swaps, param: 'swap/menu' }); } export async function requestListMenu(country: string, boxid?: 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 }; } productCodesLoading.set(true); setPendingProductCodesCountry(country); logger.info('[sheetService] Sending list_menu request for country:', country, 'boxid:', boxid); return await sendMessage({ type: 'list_menu', payload: { user_info, country, boxid: boxid || undefined } }); } export async function requestGenLayout(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(); logger.info('[sheetService] Sending gen-layout 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: 'new-inter-v3-multi-promotion-other_catalog-supra_app' } } }); } /** * Request price data from sheet for specific product codes * NOTE: Can only send once per type (price). Use hasSheetPriceBeenSent to check. */ export async function requestSheetPrice( country: string, productCodes: string[], force = false ): Promise { // Check if already sent if (!force && hasSheetPriceBeenSent('price')) { logger.warn('[sheetService] Price request already sent, skipping'); return false; } if (!productCodes || productCodes.length === 0) { logger.warn('[sheetService] No product codes to request price for'); return false; } // Generate request_id (UUID v4) const request_id = crypto.randomUUID(); // Store request_id and country in streamingRawData for tracking streamingRawData.update((data) => ({ ...data, price: { request_id, country, chunks: [], rawParts: [] } })); sheetPriceLoading.set(true); // Convert to array of objects (backend expects objects, not strings) const content = productCodes.map((code) => ({ product_code: code })); logger.info( '[sheetService] Sending sheet price request for country:', country, 'codes:', productCodes.length, 'request_id:', request_id ); const sent = await sendCommandRequest('sheet', { country: country, content: content, param: 'price', option: 'price', stream: true, request_id }); console.log('[sheetService] Sheet price request sent:', { country, request_id, sent }); if (sent) { markSheetPriceAsSent('price'); } else { sheetPriceLoading.set(false); } return sent; } export async function requestAllSheetPrice(country: string, force = false): Promise { if (!force && hasSheetPriceBeenSent('price')) { console.warn('[sheetService] Price request already sent, skipping'); return false; } const request_id = crypto.randomUUID(); streamingRawData.update((data) => ({ ...data, price: { request_id, country, chunks: [], rawParts: [] } })); sheetPriceLoading.set(true); console.log('[sheetService] Sending all sheet price request:', { country, request_id }); const sent = await sendCommandRequest('sheet', { country, content: [], param: 'price', option: 'price', stream: true, request_id }); if (sent) { markSheetPriceAsSent('price'); } else { sheetPriceLoading.set(false); } return sent; } /** * Update price data in sheet * content: [{ row_index: number, cells: [{ value: string, coord: { row: number, col: number } }] }] */ export async function updateSheetPrice( country: string, content: { row_index: number; cells: { value: string; coord: { row: number; col: number } }[] }[] ): Promise { if (!content || content.length === 0) { logger.warn('[sheetService] No content to update'); return false; } logger.info( '[sheetService] Updating sheet price for country:', country, 'items:', content.length ); return await sendCommandRequest('sheet', { country: country, content: content, param: 'update/price' }); } /** * Add new price rows to sheet (for product codes that don't exist in price sheet) * content: [{ cells: [product_code, name_en, name_th, ..., price, ...] }] */ export async function addSheetPrice( country: string, content: { cells: string[] }[] ): Promise { if (!content || content.length === 0) { logger.warn('[sheetService] No content to add'); return false; } logger.info( '[sheetService] Adding price rows for country:', country, 'items:', content.length, content ); return await sendCommandRequest('sheet', { country: country, content: content, param: 'add/price' }); }