507 lines
12 KiB
TypeScript
507 lines
12 KiB
TypeScript
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<boolean> {
|
|
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<boolean> {
|
|
return await sendCommandRequest('sheet', {
|
|
country: country,
|
|
catalog: catalog,
|
|
catalog_name: catalogName,
|
|
param: 'add/catalog'
|
|
});
|
|
}
|
|
|
|
export async function requestPriceSlots(country: string): Promise<boolean> {
|
|
setPendingPriceSlotsCountry(country);
|
|
resetPriceSlotsCountry(country);
|
|
return requestPriceSlotOption(country, 'PriceSlot');
|
|
}
|
|
|
|
export async function requestPriceSlot(country: string, slotNumber: number): Promise<boolean> {
|
|
setPendingPriceSlotsCountry(country);
|
|
return requestPriceSlotOption(country, `PriceSlot${slotNumber}`);
|
|
}
|
|
|
|
async function requestPriceSlotOption(country: string, option: string): Promise<boolean> {
|
|
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<boolean> {
|
|
return requestPriceSlotOption(country, 'PriceSlot');
|
|
}
|
|
|
|
export async function updatePriceSlot(
|
|
country: string,
|
|
slot: PriceSlot,
|
|
content: SheetRowUpdate[]
|
|
): Promise<boolean> {
|
|
// 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<boolean> {
|
|
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<boolean> {
|
|
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<boolean> {
|
|
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<boolean> {
|
|
return await sendCommandRequest('sheet', {
|
|
country: country,
|
|
catalog: catalog,
|
|
param: 'enter'
|
|
});
|
|
}
|
|
|
|
export async function sendHeartbeat(country: string, catalog: string): Promise<boolean> {
|
|
return await sendCommandRequest('sheet', {
|
|
country: country,
|
|
catalog: catalog,
|
|
param: 'heartbeat'
|
|
});
|
|
}
|
|
|
|
export async function exitRoom(country: string, catalog: string): Promise<boolean> {
|
|
return await sendCommandRequest('sheet', {
|
|
country: country,
|
|
catalog: catalog,
|
|
param: 'exit'
|
|
});
|
|
}
|
|
|
|
export async function requestCatalogMenu(country: string, catalog: string): Promise<boolean> {
|
|
return await sendCommandRequest('sheet', {
|
|
country: country,
|
|
catalog: catalog,
|
|
param: 'catalog/menu'
|
|
});
|
|
}
|
|
|
|
export async function updateMenu(
|
|
country: string,
|
|
catalog: string,
|
|
content: any[]
|
|
): Promise<boolean> {
|
|
return await sendCommandRequest('sheet', {
|
|
country: country,
|
|
catalog: catalog,
|
|
content: content,
|
|
param: 'update/menu'
|
|
});
|
|
}
|
|
|
|
export async function addMenu(country: string, catalog: string, content: any[]): Promise<boolean> {
|
|
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<boolean> {
|
|
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<boolean> {
|
|
return await sendCommandRequest('sheet', {
|
|
country: country,
|
|
catalog: catalog,
|
|
content: swaps,
|
|
param: 'swap/menu'
|
|
});
|
|
}
|
|
|
|
export async function requestListMenu(country: string, boxid?: string): Promise<boolean> {
|
|
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<boolean> {
|
|
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<boolean> {
|
|
// 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<boolean> {
|
|
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<boolean> {
|
|
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<boolean> {
|
|
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'
|
|
});
|
|
}
|