update get data priceslot
This commit is contained in:
parent
cd88d5aed9
commit
6a2f4e5945
5 changed files with 912 additions and 337 deletions
|
|
@ -362,6 +362,16 @@ export async function executeCmd(command: string) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export async function goToMachineHome() {
|
||||
if (!getAdbInstance()) return;
|
||||
try {
|
||||
await executeCmd('input keyevent KEYCODE_HOME');
|
||||
} catch (e) {
|
||||
console.error('[goToMachineHome] error', e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function disconnect() {
|
||||
let instance = getAdbInstance();
|
||||
if (instance) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import {
|
|||
handleSheetStreamEnd,
|
||||
handleSheetStreamError,
|
||||
handleCatalogsResponse,
|
||||
handlePriceSlotsResponse,
|
||||
isPriceSlotsPayload,
|
||||
handleListMenuResponse,
|
||||
sheetCatalogsLoading,
|
||||
handleRawStreamHeader,
|
||||
|
|
@ -283,22 +285,55 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
|
||||
if (from === 'sheet-service' && level === 'content') {
|
||||
const currentUid = auth.currentUser?.uid;
|
||||
const content = p.content ?? p.value ?? p.payload;
|
||||
|
||||
if (target && currentUid && target === currentUid) {
|
||||
if (!msg && p.content?.catalogs) {
|
||||
handleCatalogsResponse(p.content);
|
||||
addNotification(`INFO:Loaded ${p.content.catalogs?.length || 0} catalogs`);
|
||||
console.log('[Sheet] Notify content received:', {
|
||||
msg,
|
||||
target,
|
||||
currentUid,
|
||||
contentKeys: content && typeof content === 'object' ? Object.keys(content) : [],
|
||||
content
|
||||
});
|
||||
|
||||
if (!target || (currentUid && target === currentUid)) {
|
||||
if (!msg && content?.catalogs) {
|
||||
handleCatalogsResponse(content);
|
||||
addNotification(`INFO:Loaded ${content.catalogs?.length || 0} catalogs`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!msg &&
|
||||
(content?.priceSlots ||
|
||||
content?.priceslots ||
|
||||
content?.price_slots ||
|
||||
content?.slots ||
|
||||
content?.param === 'priceslot' ||
|
||||
content?.option === 'PriceSlot' ||
|
||||
isPriceSlotsPayload(content))
|
||||
) {
|
||||
handlePriceSlotsResponse(content);
|
||||
addNotification('INFO:Loaded PriceSlot data');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle streaming messages (with msg field)
|
||||
switch (msg) {
|
||||
case 'priceslot':
|
||||
case 'price_slot':
|
||||
handlePriceSlotsResponse(content);
|
||||
addNotification('INFO:Loaded PriceSlot data');
|
||||
break;
|
||||
case 'start':
|
||||
handleSheetStreamStart(p);
|
||||
addNotification('INFO:Sheet data streaming started');
|
||||
break;
|
||||
case 'chunk':
|
||||
handleSheetStreamChunk(p);
|
||||
if (isPriceSlotsPayload(content)) {
|
||||
handlePriceSlotsResponse(content);
|
||||
} else {
|
||||
handleSheetStreamChunk(p);
|
||||
}
|
||||
break;
|
||||
case 'end':
|
||||
handleSheetStreamEnd(p);
|
||||
|
|
@ -310,8 +345,15 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
break;
|
||||
default:
|
||||
// Handle other content notifications from sheet-service
|
||||
console.log('[Sheet] Received content:', p.content);
|
||||
console.log('[Sheet] Received content:', content);
|
||||
}
|
||||
} else {
|
||||
console.warn('[Sheet] Ignored content because target does not match current user:', {
|
||||
target,
|
||||
currentUid,
|
||||
msg,
|
||||
content
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -466,19 +508,30 @@ const handlers: Record<string, (payload: any) => void> = {
|
|||
// Header for price stream
|
||||
handleRawStreamHeader('price', p);
|
||||
},
|
||||
raw_stream_priceslot: (p) => {
|
||||
handleRawStreamHeader('priceslot', p);
|
||||
},
|
||||
raw_stream_chunk_price: (p) => {
|
||||
// Chunk for price stream
|
||||
handleRawStreamChunk('price', p);
|
||||
},
|
||||
raw_stream_chunk_priceslot: (p) => {
|
||||
handleRawStreamChunk('priceslot', p);
|
||||
},
|
||||
raw_stream_end_price: (p) => {
|
||||
// End for price stream
|
||||
handleRawStreamEnd('price', p);
|
||||
},
|
||||
raw_stream_end_priceslot: (p) => {
|
||||
handleRawStreamEnd('priceslot', p);
|
||||
}
|
||||
};
|
||||
|
||||
export function handleIncomingMessages(raw: string) {
|
||||
const msg: WSMessage = JSON.parse(raw);
|
||||
// console.log(`[WS MSG] type=${msg.type}`, msg.payload);
|
||||
if (msg.type !== 'heartbeat') {
|
||||
console.log(`[WS MSG] type=${msg.type}`, msg.payload);
|
||||
}
|
||||
if (msg == null) {
|
||||
// error response
|
||||
addNotification('ERR:No response from server');
|
||||
|
|
|
|||
|
|
@ -7,8 +7,12 @@ import {
|
|||
markSheetPriceAsSent,
|
||||
sheetPriceLoading,
|
||||
streamingRawData,
|
||||
setPendingProductCodesCountry
|
||||
setPendingProductCodesCountry,
|
||||
setPendingPriceSlotsCountry,
|
||||
priceSlotsLoading,
|
||||
resetPriceSlotsCountry
|
||||
} from '../stores/sheetStore';
|
||||
import type { PriceSlot } from '../stores/sheetStore';
|
||||
import { setGenLayoutGenerating } from '../stores/genLayoutStore';
|
||||
|
||||
export function requestCatalogs(country: string): boolean {
|
||||
|
|
@ -19,21 +23,38 @@ export function requestCatalogs(country: string): boolean {
|
|||
}
|
||||
|
||||
export function requestPriceSlots(country: string): boolean {
|
||||
return sendCommandRequest('sheet', {
|
||||
setPendingPriceSlotsCountry(country);
|
||||
resetPriceSlotsCountry(country);
|
||||
const request_id = crypto.randomUUID();
|
||||
|
||||
streamingRawData.update((data) => ({
|
||||
...data,
|
||||
priceslot: {
|
||||
request_id,
|
||||
country,
|
||||
chunks: [],
|
||||
rawParts: []
|
||||
}
|
||||
}));
|
||||
priceSlotsLoading.set(true);
|
||||
|
||||
const values = {
|
||||
country: country,
|
||||
param: 'priceslot'
|
||||
});
|
||||
param: 'price',
|
||||
option: 'PriceSlot',
|
||||
stream: true,
|
||||
request_id
|
||||
};
|
||||
console.log('[sheetService] Sending PriceSlot request:', values);
|
||||
const sent = sendCommandRequest('sheet', values);
|
||||
console.log('[sheetService] PriceSlot request sent:', sent);
|
||||
if (!sent) {
|
||||
priceSlotsLoading.set(false);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
export function updatePriceSlot(
|
||||
country: string,
|
||||
content: {
|
||||
slot: number;
|
||||
name: string;
|
||||
description: string;
|
||||
products: { product_code: string; price: number | null; row_index?: number }[];
|
||||
}
|
||||
): boolean {
|
||||
export function updatePriceSlot(country: string, content: PriceSlot): boolean {
|
||||
return sendCommandRequest('sheet', {
|
||||
country: country,
|
||||
content: content,
|
||||
|
|
@ -210,7 +231,14 @@ export function requestSheetPrice(country: string, productCodes: string[]): bool
|
|||
// Convert to array of objects (backend expects objects, not strings)
|
||||
const content = productCodes.map((code) => ({ product_code: code }));
|
||||
|
||||
console.log('[sheetService] Sending sheet price request for country:', country, 'codes:', productCodes.length, 'request_id:', request_id);
|
||||
console.log(
|
||||
'[sheetService] Sending sheet price request for country:',
|
||||
country,
|
||||
'codes:',
|
||||
productCodes.length,
|
||||
'request_id:',
|
||||
request_id
|
||||
);
|
||||
|
||||
const sent = sendCommandRequest('sheet', {
|
||||
country: country,
|
||||
|
|
@ -242,7 +270,12 @@ export function updateSheetPrice(
|
|||
return false;
|
||||
}
|
||||
|
||||
console.log('[sheetService] Updating sheet price for country:', country, 'items:', content.length);
|
||||
console.log(
|
||||
'[sheetService] Updating sheet price for country:',
|
||||
country,
|
||||
'items:',
|
||||
content.length
|
||||
);
|
||||
|
||||
return sendCommandRequest('sheet', {
|
||||
country: country,
|
||||
|
|
@ -255,16 +288,19 @@ export function updateSheetPrice(
|
|||
* 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 function addSheetPrice(
|
||||
country: string,
|
||||
content: { cells: string[] }[]
|
||||
): boolean {
|
||||
export function addSheetPrice(country: string, content: { cells: string[] }[]): boolean {
|
||||
if (!content || content.length === 0) {
|
||||
console.warn('[sheetService] No content to add');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('[sheetService] Adding price rows for country:', country, 'items:', content.length, content);
|
||||
console.log(
|
||||
'[sheetService] Adding price rows for country:',
|
||||
country,
|
||||
'items:',
|
||||
content.length,
|
||||
content
|
||||
);
|
||||
|
||||
return sendCommandRequest('sheet', {
|
||||
country: country,
|
||||
|
|
|
|||
|
|
@ -24,16 +24,212 @@ export interface PriceSlotProduct {
|
|||
row_index?: number;
|
||||
}
|
||||
|
||||
export interface PriceSlotServiceRow {
|
||||
row_index?: number;
|
||||
cells: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface PriceSlot {
|
||||
slot: number;
|
||||
name: string;
|
||||
description: string;
|
||||
kind?: 'price' | 'service';
|
||||
header?: string[];
|
||||
products: PriceSlotProduct[];
|
||||
serviceRows?: PriceSlotServiceRow[];
|
||||
}
|
||||
|
||||
export const priceSlots = writable<Record<string, PriceSlot[]>>({});
|
||||
export const priceSlotsLoading = writable<boolean>(false);
|
||||
export const priceSlotsError = writable<string | null>(null);
|
||||
let pendingPriceSlotsCountry = '';
|
||||
|
||||
export function setPendingPriceSlotsCountry(country: string) {
|
||||
pendingPriceSlotsCountry = country.toLowerCase();
|
||||
}
|
||||
|
||||
export function resetPriceSlotsCountry(country: string) {
|
||||
const key = country.toLowerCase();
|
||||
priceSlots.update((data) => ({
|
||||
...data,
|
||||
[key]: []
|
||||
}));
|
||||
priceSlotsError.set(null);
|
||||
}
|
||||
|
||||
function normalizePriceSlotProduct(product: any): PriceSlotProduct | null {
|
||||
const cells = Array.isArray(product?.cells) ? product.cells : [];
|
||||
const cellValue = (col: number) => cells.find((cell: any) => cell?.coord?.col === col)?.value;
|
||||
const productCode =
|
||||
product?.product_code ?? product?.ProductCode ?? product?.code ?? cellValue(1);
|
||||
|
||||
if (!productCode) return null;
|
||||
|
||||
const priceValue =
|
||||
product?.price ??
|
||||
product?.Price ??
|
||||
product?.value ??
|
||||
product?.cash_price ??
|
||||
product?.CashPrice ??
|
||||
cellValue(5);
|
||||
const price =
|
||||
priceValue === '' || priceValue === undefined || priceValue === null
|
||||
? null
|
||||
: Number(priceValue);
|
||||
|
||||
return {
|
||||
product_code: String(productCode),
|
||||
name: String(
|
||||
product?.name ?? product?.ProductName ?? product?.product_name ?? cellValue(2) ?? ''
|
||||
),
|
||||
price: Number.isNaN(price) ? null : price,
|
||||
row_index: product?.row_index ?? product?.row
|
||||
};
|
||||
}
|
||||
|
||||
function getPriceSlotHeader(slot: any): string[] {
|
||||
const header = Array.isArray(slot?.header) ? slot.header : [];
|
||||
return header.map((value: any) => String(value ?? '').trim());
|
||||
}
|
||||
|
||||
function isServicePriceSlotHeader(header: string[]): boolean {
|
||||
return header.some((value) => value.toLowerCase() === 'servicetype');
|
||||
}
|
||||
|
||||
function normalizePriceSlotServiceRow(row: any, header: string[]): PriceSlotServiceRow | null {
|
||||
const cells = Array.isArray(row?.cells) ? row.cells : [];
|
||||
const mappedCells = header.reduce<Record<string, string>>((result, columnName, index) => {
|
||||
if (!columnName) return result;
|
||||
const value =
|
||||
row?.[columnName] ??
|
||||
row?.[columnName.replace(/\s+/g, '')] ??
|
||||
cells.find((cell: any) => cell?.coord?.col === index + 1)?.value ??
|
||||
'';
|
||||
result[columnName] = String(value ?? '');
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
if (Object.values(mappedCells).every((value) => value === '')) return null;
|
||||
|
||||
return {
|
||||
row_index: row?.row_index ?? row?.row,
|
||||
cells: mappedCells
|
||||
};
|
||||
}
|
||||
|
||||
function normalizePriceSlot(slot: any, index: number): PriceSlot {
|
||||
const sheetName = slot?.sheet ?? slot?.Sheet;
|
||||
const displayName = slot?.name ?? slot?.title ?? sheetName;
|
||||
const slotNumber = Number(
|
||||
slot?.slot ?? slot?.price_slot ?? slot?.id ?? displayName?.match?.(/\d+/)?.[0] ?? index + 1
|
||||
);
|
||||
const productsSource = slot?.products ?? slot?.items ?? slot?.rows ?? slot?.payload ?? [];
|
||||
const header = getPriceSlotHeader(slot);
|
||||
const isServiceSlot = isServicePriceSlotHeader(header);
|
||||
const headerName = isServiceSlot ? header[12] : header[10];
|
||||
const headerDescription = isServiceSlot ? header[13] : header[11];
|
||||
const products = (Array.isArray(productsSource) ? productsSource : [])
|
||||
.map(normalizePriceSlotProduct)
|
||||
.filter((product): product is PriceSlotProduct => product !== null);
|
||||
const serviceRows = isServiceSlot
|
||||
? (Array.isArray(productsSource) ? productsSource : [])
|
||||
.map((row) => normalizePriceSlotServiceRow(row, header))
|
||||
.filter((row): row is PriceSlotServiceRow => row !== null)
|
||||
: [];
|
||||
|
||||
return {
|
||||
slot: Number.isNaN(slotNumber) ? index + 1 : slotNumber,
|
||||
name: String(
|
||||
headerName ?? displayName ?? `PriceSlot${Number.isNaN(slotNumber) ? index + 1 : slotNumber}`
|
||||
),
|
||||
description: String(headerDescription ?? ''),
|
||||
kind: isServiceSlot ? 'service' : 'price',
|
||||
header,
|
||||
products: isServiceSlot ? [] : products,
|
||||
serviceRows
|
||||
};
|
||||
}
|
||||
|
||||
export function handlePriceSlotsResponse(content: any) {
|
||||
console.log('[PriceSlot] Raw backend response:', content);
|
||||
const country = String(
|
||||
content?.country ?? content?.Country ?? pendingPriceSlotsCountry
|
||||
).toLowerCase();
|
||||
const source =
|
||||
content?.priceSlots ??
|
||||
content?.priceslots ??
|
||||
content?.price_slots ??
|
||||
content?.slots ??
|
||||
content?.data ??
|
||||
content?.value ??
|
||||
content?.content ??
|
||||
content;
|
||||
const slotList = Array.isArray(source)
|
||||
? source
|
||||
: typeof source === 'object' && source
|
||||
? Object.entries(source).map(([key, value]) => ({
|
||||
...(typeof value === 'object' && value ? value : {}),
|
||||
name: (value as any)?.name ?? key
|
||||
}))
|
||||
: [];
|
||||
|
||||
if (!country || slotList.length === 0) {
|
||||
console.warn('[PriceSlot] No slot list found:', { country, source, content });
|
||||
priceSlotsError.set('No PriceSlot data found in backend response');
|
||||
priceSlotsLoading.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedSlots = slotList
|
||||
.map(normalizePriceSlot)
|
||||
.filter((slot) =>
|
||||
slot.kind === 'service' ? (slot.serviceRows?.length ?? 0) > 0 : slot.products.length > 0
|
||||
);
|
||||
|
||||
if (normalizedSlots.length === 0) {
|
||||
console.warn('[PriceSlot] Response did not include usable rows:', { country, slotList });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[PriceSlot] Normalized slots:', {
|
||||
country,
|
||||
slots: normalizedSlots.length,
|
||||
firstSlot: normalizedSlots[0]
|
||||
});
|
||||
|
||||
priceSlots.update((data) => {
|
||||
const merged = new Map<string, PriceSlot>();
|
||||
for (const slot of data[country] ?? []) {
|
||||
merged.set(`${slot.slot}:${slot.name}`, slot);
|
||||
}
|
||||
for (const slot of normalizedSlots) {
|
||||
merged.set(`${slot.slot}:${slot.name}`, slot);
|
||||
}
|
||||
|
||||
return {
|
||||
...data,
|
||||
[country]: Array.from(merged.values()).sort((a, b) => a.slot - b.slot)
|
||||
};
|
||||
});
|
||||
priceSlotsError.set(null);
|
||||
priceSlotsLoading.set(false);
|
||||
}
|
||||
|
||||
export function isPriceSlotsPayload(content: any): boolean {
|
||||
const source =
|
||||
content?.priceSlots ??
|
||||
content?.priceslots ??
|
||||
content?.price_slots ??
|
||||
content?.slots ??
|
||||
content?.data ??
|
||||
content?.value ??
|
||||
content?.content ??
|
||||
content;
|
||||
|
||||
if (content?.param === 'priceslot' || content?.option === 'PriceSlot') return true;
|
||||
if (!Array.isArray(source)) return false;
|
||||
return source.some((item) => String(item?.sheet ?? item?.Sheet ?? '').startsWith('PriceSlot'));
|
||||
}
|
||||
|
||||
export const countryPrimaryLanguageMap: Record<string, string> = {
|
||||
THAI: 'Thai',
|
||||
|
|
@ -78,11 +274,14 @@ export function getCountryPrimaryLanguage(countryCode: string): string {
|
|||
|
||||
// Sheet column configuration by country for new_layout_v2
|
||||
// Maps language keys to column indices and product code columns
|
||||
export const SHEET_COLUMN_CONFIG_BY_COUNTRY: Record<string, {
|
||||
language: Record<string, number>;
|
||||
productCode: { hot: number; cold: number; blend: number };
|
||||
primaryLanguage: string;
|
||||
}> = {
|
||||
export const SHEET_COLUMN_CONFIG_BY_COUNTRY: Record<
|
||||
string,
|
||||
{
|
||||
language: Record<string, number>;
|
||||
productCode: { hot: number; cold: number; blend: number };
|
||||
primaryLanguage: string;
|
||||
}
|
||||
> = {
|
||||
tha: {
|
||||
language: { en: 3, th: 4, zh: 5, my: 8 },
|
||||
productCode: { hot: 9, cold: 10, blend: 11 },
|
||||
|
|
@ -151,8 +350,10 @@ export const SHEET_COLUMN_CONFIG_BY_COUNTRY: Record<string, {
|
|||
};
|
||||
|
||||
export function getSheetColumnConfig(countryCode: string) {
|
||||
return SHEET_COLUMN_CONFIG_BY_COUNTRY[countryCode.toLowerCase()]
|
||||
|| SHEET_COLUMN_CONFIG_BY_COUNTRY.default;
|
||||
return (
|
||||
SHEET_COLUMN_CONFIG_BY_COUNTRY[countryCode.toLowerCase()] ||
|
||||
SHEET_COLUMN_CONFIG_BY_COUNTRY.default
|
||||
);
|
||||
}
|
||||
|
||||
export function handleCatalogsResponse(content: CatalogsResponse) {
|
||||
|
|
@ -304,10 +505,13 @@ export interface SheetPriceItem {
|
|||
|
||||
// Price sheet header name mappings by country
|
||||
// Maps our field names to the actual header names in the sheet
|
||||
export const PRICE_HEADER_NAMES_BY_COUNTRY: Record<string, {
|
||||
cash_price: string[]; // Possible header names for cash price
|
||||
non_cash_price: string[]; // Possible header names for non-cash price
|
||||
}> = {
|
||||
export const PRICE_HEADER_NAMES_BY_COUNTRY: Record<
|
||||
string,
|
||||
{
|
||||
cash_price: string[]; // Possible header names for cash price
|
||||
non_cash_price: string[]; // Possible header names for non-cash price
|
||||
}
|
||||
> = {
|
||||
tha: {
|
||||
cash_price: ['Price'],
|
||||
non_cash_price: ['MainPrice']
|
||||
|
|
@ -366,7 +570,7 @@ export const PRICE_HEADER_NAMES_BY_COUNTRY: Record<string, {
|
|||
// Find column index from header array by matching header names
|
||||
export function findHeaderIndex(headerArray: string[], possibleNames: string[]): number {
|
||||
for (const name of possibleNames) {
|
||||
const idx = headerArray.findIndex(h => h.toLowerCase() === name.toLowerCase());
|
||||
const idx = headerArray.findIndex((h) => h.toLowerCase() === name.toLowerCase());
|
||||
if (idx !== -1) {
|
||||
// Return col index (header index + 1 because cells start from col 1)
|
||||
return idx + 1;
|
||||
|
|
@ -382,7 +586,9 @@ export const lastRequestSheetPrice = writable<Record<string, Record<string, Gris
|
|||
export const sheetPriceHeader = writable<Record<string, string[]>>({});
|
||||
|
||||
// Store: sheetPriceAllRows[country][product_code] = array of {row, cells} (ALL rows for duplicates)
|
||||
export const sheetPriceAllRows = writable<Record<string, Record<string, { row: number; cells: GristCell[] }[]>>>({});
|
||||
export const sheetPriceAllRows = writable<
|
||||
Record<string, Record<string, { row: number; cells: GristCell[] }[]>>
|
||||
>({});
|
||||
|
||||
// Helper function to get price value from cells using dynamic header lookup
|
||||
export function getPriceFromCells(
|
||||
|
|
@ -397,15 +603,20 @@ export function getPriceFromCells(
|
|||
}
|
||||
|
||||
// Get possible header names for this country
|
||||
const headerNames = PRICE_HEADER_NAMES_BY_COUNTRY[country] || PRICE_HEADER_NAMES_BY_COUNTRY.default;
|
||||
const possibleNames = priceType === 'cash_price' ? headerNames.cash_price : headerNames.non_cash_price;
|
||||
const headerNames =
|
||||
PRICE_HEADER_NAMES_BY_COUNTRY[country] || PRICE_HEADER_NAMES_BY_COUNTRY.default;
|
||||
const possibleNames =
|
||||
priceType === 'cash_price' ? headerNames.cash_price : headerNames.non_cash_price;
|
||||
|
||||
// Find the column index for this price type
|
||||
const colIdx = findHeaderIndex(headers, possibleNames);
|
||||
//console.log(`[getPriceFromCells] ${country} ${priceType}: colIdx=${colIdx}, headers=`, headers, 'possibleNames=', possibleNames);
|
||||
|
||||
if (colIdx < 0) {
|
||||
console.warn(`[getPriceFromCells] No ${priceType} column found for ${country}, tried:`, possibleNames);
|
||||
console.warn(
|
||||
`[getPriceFromCells] No ${priceType} column found for ${country}, tried:`,
|
||||
possibleNames
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -444,15 +655,20 @@ export const streamingRawData = writable<
|
|||
|
||||
// Handler: raw_stream header (e.g., raw_stream_price)
|
||||
export function handleRawStreamHeader(subtype: string, payload: any) {
|
||||
console.log(`[RawStream] Header for ${subtype}:`, payload);
|
||||
let targetSubtype = subtype;
|
||||
const currentData = get(streamingRawData);
|
||||
if (subtype === 'price' && currentData.priceslot?.request_id === payload.request_id) {
|
||||
targetSubtype = 'priceslot';
|
||||
}
|
||||
|
||||
console.log(`[RawStream] Header for ${targetSubtype}:`, payload);
|
||||
|
||||
// Get existing stream data to preserve country from request
|
||||
const currentData = get(streamingRawData);
|
||||
const existingData = currentData[subtype];
|
||||
const existingData = currentData[targetSubtype];
|
||||
|
||||
streamingRawData.update((data) => ({
|
||||
...data,
|
||||
[subtype]: {
|
||||
[targetSubtype]: {
|
||||
request_id: payload.request_id,
|
||||
header: payload.header || payload.headers,
|
||||
country: payload.country || existingData?.country || '',
|
||||
|
|
@ -461,7 +677,7 @@ export function handleRawStreamHeader(subtype: string, payload: any) {
|
|||
}
|
||||
}));
|
||||
|
||||
if (subtype === 'price') {
|
||||
if (targetSubtype === 'price') {
|
||||
sheetPriceStreamMeta.set({
|
||||
request_id: payload.request_id,
|
||||
country: payload.country || existingData?.country || '',
|
||||
|
|
@ -473,13 +689,21 @@ export function handleRawStreamHeader(subtype: string, payload: any) {
|
|||
|
||||
// Handler: raw_stream chunk (e.g., raw_stream_chunk_price)
|
||||
export function handleRawStreamChunk(subtype: string, payload: any) {
|
||||
console.log(`[RawStream] Chunk ${payload.idx} for ${subtype}, raw length:`, payload.raw?.length);
|
||||
|
||||
const currentData = get(streamingRawData);
|
||||
const streamData = currentData[subtype];
|
||||
let targetSubtype = subtype;
|
||||
if (subtype === 'price' && currentData.priceslot?.request_id === payload.request_id) {
|
||||
targetSubtype = 'priceslot';
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[RawStream] Chunk ${payload.idx} for ${targetSubtype}, raw length:`,
|
||||
payload.raw?.length
|
||||
);
|
||||
|
||||
const streamData = currentData[targetSubtype];
|
||||
|
||||
if (!streamData || streamData.request_id !== payload.request_id) {
|
||||
console.warn(`[RawStream] Chunk received for unknown stream: ${subtype}`);
|
||||
console.warn(`[RawStream] Chunk received for unknown stream: ${targetSubtype}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -488,13 +712,13 @@ export function handleRawStreamChunk(subtype: string, payload: any) {
|
|||
// Accumulate raw parts - will be joined and parsed in handleRawStreamEnd
|
||||
streamingRawData.update((data) => ({
|
||||
...data,
|
||||
[subtype]: {
|
||||
[targetSubtype]: {
|
||||
...streamData,
|
||||
country: payload.country || streamData.country,
|
||||
rawParts: [...(streamData.rawParts || []), payload.raw]
|
||||
}
|
||||
}));
|
||||
console.log(`[RawStream] Accumulated chunk ${payload.idx} for ${subtype}`);
|
||||
console.log(`[RawStream] Accumulated chunk ${payload.idx} for ${targetSubtype}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -504,25 +728,30 @@ export function handleRawStreamChunk(subtype: string, payload: any) {
|
|||
|
||||
streamingRawData.update((data) => ({
|
||||
...data,
|
||||
[subtype]: {
|
||||
[targetSubtype]: {
|
||||
...streamData,
|
||||
country: payload.country || streamData.country,
|
||||
chunks: [...streamData.chunks, ...contentArray]
|
||||
}
|
||||
}));
|
||||
|
||||
console.log(`[RawStream] Chunk for ${subtype}: +${contentArray.length} items`);
|
||||
console.log(`[RawStream] Chunk for ${targetSubtype}: +${contentArray.length} items`);
|
||||
}
|
||||
|
||||
// Handler: raw_stream end (e.g., raw_stream_end_price)
|
||||
export function handleRawStreamEnd(subtype: string, payload: any) {
|
||||
console.log(`[RawStream] End payload for ${subtype}:`, payload);
|
||||
|
||||
const currentData = get(streamingRawData);
|
||||
const streamData = currentData[subtype];
|
||||
let targetSubtype = subtype;
|
||||
if (subtype === 'price' && currentData.priceslot?.request_id === payload.request_id) {
|
||||
targetSubtype = 'priceslot';
|
||||
}
|
||||
|
||||
console.log(`[RawStream] End payload for ${targetSubtype}:`, payload);
|
||||
|
||||
const streamData = currentData[targetSubtype];
|
||||
|
||||
if (!streamData || streamData.request_id !== payload.request_id) {
|
||||
console.warn(`[RawStream] End received for unknown stream: ${subtype}`);
|
||||
console.warn(`[RawStream] End received for unknown stream: ${targetSubtype}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -554,18 +783,36 @@ export function handleRawStreamEnd(subtype: string, payload: any) {
|
|||
}
|
||||
}
|
||||
|
||||
console.log(`[RawStream] End for ${subtype}: total ${chunks.length} items, country: ${country}`);
|
||||
console.log(
|
||||
`[RawStream] End for ${targetSubtype}: total ${chunks.length} items, country: ${country}`
|
||||
);
|
||||
|
||||
if (subtype === 'price') {
|
||||
processSheetPriceData(country, streamData.header || [], chunks);
|
||||
sheetPriceStreamMeta.update((meta) => (meta ? { ...meta, status: 'complete' } : null));
|
||||
sheetPriceLoading.set(false);
|
||||
if (targetSubtype === 'priceslot' && isPriceSlotsPayload({ slots: chunks })) {
|
||||
handlePriceSlotsResponse({ country, slots: chunks });
|
||||
}
|
||||
|
||||
if (targetSubtype === 'price') {
|
||||
const looksLikePriceSlot = chunks.some((item) => {
|
||||
return (
|
||||
String(item?.sheet ?? item?.Sheet ?? '').startsWith('PriceSlot') ||
|
||||
item?.option === 'PriceSlot' ||
|
||||
item?.param === 'priceslot'
|
||||
);
|
||||
});
|
||||
|
||||
if (looksLikePriceSlot) {
|
||||
handlePriceSlotsResponse({ country, slots: chunks });
|
||||
} else {
|
||||
processSheetPriceData(country, streamData.header || [], chunks);
|
||||
sheetPriceStreamMeta.update((meta) => (meta ? { ...meta, status: 'complete' } : null));
|
||||
sheetPriceLoading.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the streaming data
|
||||
streamingRawData.update((data) => {
|
||||
const newData = { ...data };
|
||||
delete newData[subtype];
|
||||
delete newData[targetSubtype];
|
||||
return newData;
|
||||
});
|
||||
}
|
||||
|
|
@ -600,8 +847,18 @@ function processSheetPriceData(country: string, header: string[], chunks: any[])
|
|||
|
||||
// Find column indices dynamically from header
|
||||
// product_code header is typically "ProductCode" or similar
|
||||
const productCodeIdx = findHeaderIndex(effectiveHeader, ['ProductCode', 'Product_Code', 'product_code', 'Code']);
|
||||
console.log(`[SheetPrice] productCodeIdx from header:`, productCodeIdx, 'header:', effectiveHeader);
|
||||
const productCodeIdx = findHeaderIndex(effectiveHeader, [
|
||||
'ProductCode',
|
||||
'Product_Code',
|
||||
'product_code',
|
||||
'Code'
|
||||
]);
|
||||
console.log(
|
||||
`[SheetPrice] productCodeIdx from header:`,
|
||||
productCodeIdx,
|
||||
'header:',
|
||||
effectiveHeader
|
||||
);
|
||||
|
||||
const priceByProductCode: Record<string, GristCell[]> = {};
|
||||
// Track ALL rows per product code (for duplicates)
|
||||
|
|
@ -702,7 +959,10 @@ function processSheetPriceData(country: string, header: string[], chunks: any[])
|
|||
// Log duplicates info
|
||||
const duplicates = Object.entries(allRowsByProductCode).filter(([_, rows]) => rows.length > 1);
|
||||
if (duplicates.length > 0) {
|
||||
console.log(`[SheetPrice] Found ${duplicates.length} product codes with duplicate rows:`, duplicates.slice(0, 3));
|
||||
console.log(
|
||||
`[SheetPrice] Found ${duplicates.length} product codes with duplicate rows:`,
|
||||
duplicates.slice(0, 3)
|
||||
);
|
||||
}
|
||||
if (chunks.length > 0 && Object.keys(priceByProductCode).length > 0) {
|
||||
const sampleKey = Object.keys(priceByProductCode)[0];
|
||||
|
|
@ -769,14 +1029,24 @@ export function loadProductCodesFromCache(country?: string): boolean {
|
|||
// Only load if country matches (or no country filter specified)
|
||||
if (data.codes && Array.isArray(data.codes)) {
|
||||
if (country && data.country && data.country !== country) {
|
||||
console.log('[sheetStore] Cache is for different country:', data.country, '!= requested:', country);
|
||||
console.log(
|
||||
'[sheetStore] Cache is for different country:',
|
||||
data.country,
|
||||
'!= requested:',
|
||||
country
|
||||
);
|
||||
// Clear the store for different country
|
||||
existingProductCodes.set(new Set());
|
||||
return false;
|
||||
}
|
||||
existingProductCodes.set(new Set(data.codes));
|
||||
currentProductCodesCountry = data.country || '';
|
||||
console.log('[sheetStore] Loaded', data.codes.length, 'product codes from cache for', data.country || 'unknown');
|
||||
console.log(
|
||||
'[sheetStore] Loaded',
|
||||
data.codes.length,
|
||||
'product codes from cache for',
|
||||
data.country || 'unknown'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -798,7 +1068,13 @@ export function clearProductCodes() {
|
|||
export function handleListMenuResponse(payload: { codes: string[]; country?: string }) {
|
||||
// Use pending country if not in payload
|
||||
const country = payload.country || pendingProductCodesCountry;
|
||||
console.log('[sheetStore] Received list_menu_response for', country, ':', payload.codes?.length, 'codes');
|
||||
console.log(
|
||||
'[sheetStore] Received list_menu_response for',
|
||||
country,
|
||||
':',
|
||||
payload.codes?.length,
|
||||
'codes'
|
||||
);
|
||||
|
||||
if (payload && payload.codes) {
|
||||
existingProductCodes.set(new Set(payload.codes));
|
||||
|
|
@ -814,7 +1090,12 @@ export function handleListMenuResponse(payload: { codes: string[]; country?: str
|
|||
timestamp: Date.now()
|
||||
})
|
||||
);
|
||||
console.log('[sheetStore] Saved', payload.codes.length, 'product codes to cache for', country);
|
||||
console.log(
|
||||
'[sheetStore] Saved',
|
||||
payload.codes.length,
|
||||
'product codes to cache for',
|
||||
country
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn('[sheetStore] Failed to save to cache:', e);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue