import fs from "node:fs"; import path from "node:path"; import vm from "node:vm"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const html = fs.readFileSync(path.join(root, "index.html"), "utf8"); function expression(name, pattern) { const match = html.match(pattern); if (!match) throw new Error(`Não foi possível extrair ${name}.`); return vm.runInNewContext(`(${match[1]})`); } const savedDefault = expression("savedDefault", /const savedDefault=(\[[^\n]+\]);/); const areaById = expression("areaById", /const areaById=({[^\n]+});/); const savedPinPositions = expression("savedPinPositions", /const savedPinPositions=({[^\n]+});/); const upperDefault = expression("upperDefault", /const upperDefault=(\(\(\)=>\{[\s\S]*?\}\)\(\));/); const accessoryLinks = expression("TPU_ACCESSORY_LINKS", /const TPU_ACCESSORY_LINKS=({[^\n]+});/); const baseLinks = expression("TPU_BASE_LINKS", /const TPU_BASE_LINKS=({[^\n]+});/); const requiredStores = expression("REQUIRED_ACCESSORY_STORES", /const REQUIRED_ACCESSORY_STORES=(\[[\s\S]*?\n\]);/); const requiredTpus = expression("REQUIRED_TPUS", /const REQUIRED_TPUS=(\[[^\n]+\]);/); const context = { window: {} }; vm.createContext(context); vm.runInContext(fs.readFileSync(path.join(root, "tpu_mpc_v4_data.js"), "utf8"), context); vm.runInContext(fs.readFileSync(path.join(root, "tpu_area_details.js"), "utf8"), context); const imported = context.window.MP_IMPORTED_TPUS; const details = context.window.MP_TPU_DETAILS.byId; const quadrantNames = { 74: "DEP 6 (I)", 103: "BOX 1 (IV)", 120: "DEP (II)", 135: "DEP 2 (III)", 136: "DEP 3 (III)", 137: "DEP 4 (III)", 138: "DEP 5 (III)", 139: "DEP 1 (III)", }; function spaceType(name) { const normalized = String(name).normalize("NFD").replace(/[\u0300-\u036f]/g, "").toUpperCase(); const number = normalized.match(/^(?:LOJ|LOJA|LJ)\s*0*(\d+)/); if (number && ["16", "18", "20"].includes(String(Number(number[1])))) return "SUBSTACAO"; if (normalized.startsWith("LOJ")) return "LOJ"; if (normalized.startsWith("BC")) return "BC"; if (normalized.startsWith("BP")) return "BP"; if (normalized.startsWith("AM") || normalized.startsWith("MI")) return "AM"; if (normalized.startsWith("PATIO")) return "PATIO"; if (normalized.startsWith("BOX")) return "BOX"; if (normalized.startsWith("DEP")) return "DEP"; if (normalized.startsWith("LED")) return "LED"; if (normalized.startsWith("REST")) return "REST"; if (normalized.startsWith("CON")) return "CON"; return "OUTRO"; } const types = [ ["LOJ", "Loja", "#F6D5C5", 1], ["BC", "Banca", "#CDE8D6", 1], ["BP", "Banca do peixe", "#DCD5F2", 1], ["AM", "Área de mesa", "#F7E7B8", .85], ["PATIO", "Pátio", "#CFE3F4", 1], ["BOX", "Box", "#F4C7C3", 1], ["DEP", "Depósito", "#D7E1EA", .75], ["LED", "Painel LED", "#FFE08A", 1], ["REST", "Restaurante", "#F3D3E0", 1], ["CON", "Concessão", "#CDEBE7", 1], ["SUBSTACAO", "Subestação", "#D9D2E9", 1], ["OUTRO", "Outro", "#FFFFFF", 1], ].map(([code, label, color, coefficient]) => ({ code, label, color, coefficient })); function normalizeSpace(raw, floorId) { const corrected = floorId === "ground" ? (quadrantNames[Number(raw.id)] || (Number(raw.id) === 145 && raw.name.trim().toUpperCase() === "AM PIII-1" ? "AM PIII-2" : raw.name)) : raw.name; const pin = floorId === "ground" ? savedPinPositions[raw.id] : null; const x = Number.isFinite(Number(raw.pinX)) ? Number(raw.pinX) : pin?.[0] ?? ((raw.col - 1 + raw.width / 2) / 42); const y = Number.isFinite(Number(raw.pinY)) ? Number(raw.pinY) : pin?.[1] ?? ((raw.row - 1 + raw.height / 2) / 43); return { id: `${floorId}:${raw.id}`, legacy_id: Number(raw.id), floor_id: floorId, name: corrected, type: spaceType(corrected), base_area_m2: Number((floorId === "ground" ? areaById[raw.id] : raw.area) || 0), position: { x: Number(x.toFixed(7)), y: Number(y.toFixed(7)) }, grid: { col: Number(raw.col), row: Number(raw.row), width: Number(raw.width), height: Number(raw.height) }, components: { mezzanine: 0, deposit: 0, deck: 0, table: 0 }, }; } const groundById = new Map(savedDefault.map(item => [Number(item.id), item])); for (const item of requiredStores) if (!groundById.has(Number(item.id))) groundById.set(Number(item.id), item); const spaces = [ ...[...groundById.values()].map(item => normalizeSpace(item, "ground")), ...upperDefault.map(item => normalizeSpace(item, "upper")), ]; function splitBoxes(items) { const combined = items.find(t => Number(t.id) === 400115); if (!combined) return items; const boxes = [103, 147, 148, 149, 150, 151, 152, 153]; const deposits = [154, 155, 156, 157, 158, 159, 160, 161]; const tables = [162, 163, 164, 165, 166, 167, 168, 169]; const ids = [400115, 400129, 400130, 400131, 400132, 400133, 400134, 400135]; const replacements = ids.map((id, index) => ({ ...combined, id, name: `TPU_BOX_${index + 1}_IV`, storeIds: [boxes[index], deposits[index], tables[index]], baseStoreIds: [boxes[index]], accessoryStoreIds: [deposits[index], tables[index]], linkedFloor: null, linkedStoreIds: [], allocationRefs: [{ floor: "ground", id: boxes[index] }], sourceOrder: String(112 + index), sourceGrossArea: 19.62, sourceUnitAreas: [9.52], sourceMezzanine: 0, sourceDeposit: 7.4, sourceDeck: 0, sourceTableArea: 2.7, sourceWeightedArea: 17.365, sourceArea: 17.365, })); return items.filter(t => Number(t.id) !== 400115).concat(replacements); } const rawByFloor = { ground: splitBoxes(imported.ground).concat(requiredTpus), upper: imported.upper, }; const tpuMap = new Map(); for (const [floorId, items] of Object.entries(rawByFloor)) { for (const raw of items) { const merged = { ...(details[String(raw.id)] || {}), ...raw }; let target = tpuMap.get(String(raw.id)); if (!target) { const source = {}; for (const [key, value] of Object.entries(merged)) { if (key.startsWith("source")) source[key.slice(6, 7).toLowerCase() + key.slice(7)] = value; } source.allocationRefs = (merged.allocationRefs || []).map(ref => `${ref.floor}:${ref.id}`); target = { id: String(raw.id), legacy_id: Number(raw.id), name: raw.name, color: raw.color || "#5B8DB8", members: [], source, visual_links: [], }; tpuMap.set(String(raw.id), target); } const accessories = new Set(merged.accessoryStoreIds || (floorId === "ground" ? accessoryLinks[raw.id] || [] : [])); const bases = new Set(merged.baseStoreIds || [ ...(raw.storeIds || []).filter(id => !accessories.has(id)), ...(floorId === "ground" ? baseLinks[raw.id] || [] : []), ]); const localIds = new Set([...bases, ...accessories, ...(raw.storeIds || [])]); for (const id of localIds) { const member = { space_id: `${floorId}:${id}`, role: accessories.has(id) ? "accessory" : "base" }; if (!target.members.some(item => item.space_id === member.space_id)) target.members.push(member); } if (raw.linkedFloor) { for (const id of raw.linkedStoreIds || []) { const spaceId = `${raw.linkedFloor}:${id}`; if (!target.members.some(item => item.space_id === spaceId)) target.members.push({ space_id: spaceId, role: "base" }); } } } } const document = { schema_version: 1, revision: 0, updated_at: new Date().toISOString(), floors: [ { id: "ground", name: "Térreo", image: "/plants/planta_terreo_mercado_publico.png", width: 1200, height: 1189, order: 1 }, { id: "upper", name: "2º pavimento", image: "/plants/planta_segundo_pavimento_mercado_publico.png", width: 1200, height: 1184, order: 2 }, ], space_types: types, component_types: [ { code: "mezzanine", label: "Mezanino", coefficient: .75 }, { code: "deposit", label: "Depósito", coefficient: .75 }, { code: "deck", label: "Deck", coefficient: .30 }, { code: "table", label: "Área de mesa", coefficient: .85 }, ], spaces, tpus: [...tpuMap.values()], settings: { active_floor: "ground", show_tpu_links: true }, }; const spacesById = new Map(document.spaces.map(space => [space.id, space])); for (const tpu of document.tpus) { const refs = tpu.source.allocationRefs || []; if (!refs.length) continue; const units = (tpu.source.unitAreas || []).map(Number).filter(value => value > 0); let bases; if (units.length === refs.length) { bases = units; } else if (refs.length === 1) { bases = [units.reduce((sum, value) => sum + value, 0) || spacesById.get(refs[0])?.base_area_m2 || 0]; } else { const mapped = refs.map(ref => spacesById.get(ref)?.base_area_m2 || 0); const mappedTotal = mapped.reduce((sum, value) => sum + value, 0); const unitTotal = units.reduce((sum, value) => sum + value, 0); bases = mapped.map(value => mappedTotal ? unitTotal * value / mappedTotal : unitTotal / refs.length); } const memberSpaces = tpu.members.map(member => ({ ...member, space: spacesById.get(member.space_id) })).filter(item => item.space); const depositAccessories = memberSpaces.filter(item => item.role === "accessory" && item.space.type === "DEP"); const tableAccessories = memberSpaces.filter(item => item.role === "accessory" && item.space.type === "AM"); const hasDepositAccessory = depositAccessories.length > 0; const hasTableAccessory = tableAccessories.length > 0; for (const item of depositAccessories) { item.space.base_area_m2 = Number((Number(tpu.source.deposit || 0) / depositAccessories.length).toFixed(4)); item.space.components = { mezzanine: 0, deposit: 0, deck: 0, table: 0 }; } for (const item of tableAccessories) { item.space.base_area_m2 = Number((Number(tpu.source.tableArea || 0) / tableAccessories.length).toFixed(4)); item.space.components = { mezzanine: 0, deposit: 0, deck: 0, table: 0 }; } const baseTotal = bases.reduce((sum, value) => sum + value, 0) || refs.length; refs.forEach((ref, index) => { const space = spacesById.get(ref); if (!space) return; const share = baseTotal ? bases[index] / baseTotal : 1 / refs.length; space.base_area_m2 = Number((bases[index] || 0).toFixed(4)); space.components = { mezzanine: Number((Number(tpu.source.mezzanine || 0) * share).toFixed(4)), deposit: hasDepositAccessory ? 0 : Number((Number(tpu.source.deposit || 0) * share).toFixed(4)), deck: Number((Number(tpu.source.deck || 0) * share).toFixed(4)), table: hasTableAccessory ? 0 : Number((Number(tpu.source.tableArea || 0) * share).toFixed(4)), }; }); } const output = path.join(root, "seed", "default.json"); fs.writeFileSync(output, `${JSON.stringify(document, null, 2)}\n`); console.log(`Seed criado: ${spaces.length} espaços, ${document.tpus.length} TPUs.`);