import { aql } from 'arangojs' import { ensureGraph, getDb } from '../dist/db.js' type GeoNode = { _key: string node_type: string name: string latitude: number longitude: number country: string country_code: string transport_types?: string[] product_uuid?: string product_name?: string product_image_url?: string supplier_uuid?: string supplier_name?: string supplier_logo_url?: string price_per_unit?: string currency?: string quantity?: string unit?: string synced_at: string } type DemoHub = GeoNode & { _key: string latitude: number longitude: number } type DemoProduct = { uuid: string name: string unit: string imageUrl: string } type DemoSupplier = { uuid: string name: string latitude: number longitude: number country: string country_code: string logoUrl: string hubKey: string } async function upsertNode(doc: GeoNode) { const { _key, ...patch } = doc await getDb().query(aql` UPSERT { _key: ${_key} } INSERT ${doc} UPDATE ${patch} IN ${getDb().collection('nodes')} `) } async function upsertEdge(doc: Record) { const key = String(doc._key) const { _key, ...patch } = doc await getDb().query(aql` UPSERT { _key: ${key} } INSERT ${doc} UPDATE ${patch} IN ${getDb().collection('edges')} `) } await ensureGraph() const now = new Date().toISOString() const products: DemoProduct[] = [ { uuid: '55555555-1111-4111-8111-111111111111', name: 'Solar panels', unit: 'pallet', imageUrl: 'https://images.unsplash.com/photo-1509391366360-2e959784a276?auto=format&fit=crop&w=900&q=80', }, { uuid: '55555555-1111-4111-8111-222222222222', name: 'Industrial pumps', unit: 'unit', imageUrl: 'https://images.unsplash.com/photo-1581092160607-ee22621dd758?auto=format&fit=crop&w=900&q=80', }, { uuid: '55555555-1111-4111-8111-333333333333', name: 'Soybean meal', unit: 'ton', imageUrl: 'https://images.unsplash.com/photo-1625246333195-78d9c38ad449?auto=format&fit=crop&w=900&q=80', }, ] const hubs: DemoHub[] = [ { _key: 'logistics-demo-hub-1', node_type: 'logistics', name: 'Guangzhou Rail Terminal', latitude: 23.1291, longitude: 113.2644, country: 'China', country_code: 'CN', transport_types: ['auto', 'rail'], synced_at: now, }, { _key: 'logistics-demo-hub-2', node_type: 'logistics', name: 'Shenzhen Yantian Port', latitude: 22.5942, longitude: 114.2743, country: 'China', country_code: 'CN', transport_types: ['auto', 'sea'], synced_at: now, }, { _key: 'logistics-demo-hub-3', node_type: 'logistics', name: 'Harbin Rail Hub', latitude: 45.8038, longitude: 126.5349, country: 'China', country_code: 'CN', transport_types: ['auto', 'rail'], synced_at: now, }, { _key: 'logistics-demo-hub-4', node_type: 'logistics', name: 'Vladivostok Commercial Port', latitude: 43.1155, longitude: 131.8855, country: 'Russia', country_code: 'RU', transport_types: ['sea', 'rail'], synced_at: now, }, { _key: 'logistics-demo-hub-5', node_type: 'logistics', name: 'Moscow Rail Hub', latitude: 55.7558, longitude: 37.6173, country: 'Russia', country_code: 'RU', transport_types: ['auto', 'rail'], synced_at: now, }, ] const suppliers: DemoSupplier[] = [ { uuid: '66666666-2222-4111-8111-111111111111', name: 'Guangzhou Solar Factory', latitude: 23.0462, longitude: 113.3831, country: 'China', country_code: 'CN', logoUrl: 'https://ui-avatars.com/api/?name=Guangzhou+Solar+Factory&background=0B6BFF&color=fff&bold=true', hubKey: 'logistics-demo-hub-1', }, { uuid: '66666666-2222-4111-8111-222222222222', name: 'Shenzhen Pump Works', latitude: 22.5431, longitude: 114.0579, country: 'China', country_code: 'CN', logoUrl: 'https://ui-avatars.com/api/?name=Shenzhen+Pump+Works&background=9F1239&color=fff&bold=true', hubKey: 'logistics-demo-hub-2', }, { uuid: '66666666-2222-4111-8111-333333333333', name: 'Harbin Agro Export', latitude: 45.7424, longitude: 126.6617, country: 'China', country_code: 'CN', logoUrl: 'https://ui-avatars.com/api/?name=Harbin+Agro+Export&background=0F8A6A&color=fff&bold=true', hubKey: 'logistics-demo-hub-3', }, { uuid: '66666666-2222-4111-8111-444444444444', name: 'Vladivostok Timber Terminal', latitude: 43.1056, longitude: 131.8735, country: 'Russia', country_code: 'RU', logoUrl: 'https://ui-avatars.com/api/?name=Vladivostok+Timber+Terminal&background=C45125&color=fff&bold=true', hubKey: 'logistics-demo-hub-4', }, ] function quoteUuid(index: number) { return `77777777-3333-4${String(index).padStart(3, '0')}-8${String(index).padStart(3, '0')}-777777${String(index).padStart(6, '0')}` } function hubByKey(key: string) { const hub = hubs.find(item => item._key === key) if (!hub) throw new Error(`Unknown demo hub: ${key}`) return hub } const supplierNodes: GeoNode[] = suppliers.map(supplier => ({ _key: supplier.uuid, node_type: 'supplier', name: supplier.name, latitude: supplier.latitude, longitude: supplier.longitude, country: supplier.country, country_code: supplier.country_code, supplier_logo_url: supplier.logoUrl, synced_at: now, })) const offers: GeoNode[] = Array.from({ length: 24 }, (_, index) => { const product = products[index % products.length] const supplier = suppliers[Math.floor(index / products.length) % suppliers.length] const localIndex = Math.floor(index / suppliers.length) const latitudeOffset = ((localIndex % 3) - 1) * 0.08 const longitudeOffset = (Math.floor(localIndex / 3) - 1) * 0.1 return { _key: quoteUuid(index), node_type: 'offer', name: `${supplier.name} ${product.name}`, latitude: supplier.latitude + latitudeOffset, longitude: supplier.longitude + longitudeOffset, country: supplier.country, country_code: supplier.country_code, product_uuid: product.uuid, product_name: product.name, product_image_url: product.imageUrl, supplier_uuid: supplier.uuid, supplier_name: supplier.name, supplier_logo_url: supplier.logoUrl, price_per_unit: String(900 + (index % 8) * 55), currency: 'USD', quantity: String(500 + index * 25), unit: product.unit, synced_at: now, } }) for (const node of [...hubs, ...offers, ...supplierNodes]) { await upsertNode(node) } const hubLinks = [ { from: 'logistics-demo-hub-1', to: 'logistics-demo-hub-3', distanceKm: 3280, travelTimeSeconds: 691200, transportType: 'rail', }, { from: 'logistics-demo-hub-3', to: 'logistics-demo-hub-1', distanceKm: 3280, travelTimeSeconds: 691200, transportType: 'rail', }, { from: 'logistics-demo-hub-2', to: 'logistics-demo-hub-4', distanceKm: 2850, travelTimeSeconds: 432000, transportType: 'sea', }, { from: 'logistics-demo-hub-4', to: 'logistics-demo-hub-2', distanceKm: 2850, travelTimeSeconds: 432000, transportType: 'sea', }, { from: 'logistics-demo-hub-3', to: 'logistics-demo-hub-4', distanceKm: 650, travelTimeSeconds: 129600, transportType: 'rail', }, { from: 'logistics-demo-hub-4', to: 'logistics-demo-hub-3', distanceKm: 650, travelTimeSeconds: 129600, transportType: 'rail', }, { from: 'logistics-demo-hub-4', to: 'logistics-demo-hub-5', distanceKm: 9250, travelTimeSeconds: 1036800, transportType: 'rail', }, { from: 'logistics-demo-hub-5', to: 'logistics-demo-hub-4', distanceKm: 9250, travelTimeSeconds: 1036800, transportType: 'rail', }, ] for (const [index, link] of hubLinks.entries()) { const toHub = hubByKey(link.to) await upsertEdge({ _key: `geo-demo-hub-edge-${index + 1}`, _from: `nodes/${link.from}`, _to: `nodes/${link.to}`, to_uuid: link.to, to_name: toHub.name, to_latitude: toHub.latitude, to_longitude: toHub.longitude, distance_km: link.distanceKm, travel_time_seconds: link.travelTimeSeconds, transport_type: link.transportType, }) } for (const [index, offer] of offers.entries()) { const supplier = suppliers[Math.floor(index / products.length) % suppliers.length] await upsertEdge({ _key: `geo-demo-offer-edge-${index + 1}`, _from: `nodes/${supplier.hubKey}`, _to: `nodes/${offer._key}`, to_uuid: offer._key, to_name: offer.name, to_latitude: offer.latitude, to_longitude: offer.longitude, distance_km: Math.max(1, Math.round(Math.hypot((offer.latitude - supplier.latitude) * 111, (offer.longitude - supplier.longitude) * 75))), travel_time_seconds: 3600 + index * 120, transport_type: 'offer', }) } console.log(`Seeded geo demo data: hubs ${hubs.length}, offers ${offers.length}, suppliers ${suppliers.length}, edges ${hubLinks.length + offers.length}`)