Improve delivery workflows and add versioned guided tours
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
// Local, in-memory review environment. It serves the production build and
|
||||
// fictional API data on its own loopback port, never contacts the real backend.
|
||||
import { createServer } from 'node:http';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { resolve, sep } from 'node:path';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
const root = resolve(fileURLToPath(new URL('../dist/', import.meta.url)));
|
||||
const dateKey = date => [date.getFullYear(), String(date.getMonth() + 1).padStart(2, '0'), String(date.getDate()).padStart(2, '0')].join('-');
|
||||
const today = dateKey(new Date());
|
||||
const tomorrowDate = new Date(); tomorrowDate.setDate(tomorrowDate.getDate() + 1);
|
||||
const tomorrow = dateKey(tomorrowDate);
|
||||
const base = { date: today, pickup_location: 'symbat', pickup_location_2: null, warehouse_request_source: null, warehouse_request_source_2: null, product_name_2: null, address: 'Кокшетау, Тестовая улица, 1', street: 'Тестовая', house: '1', apartment: '12', entrance: '2', floor: '4', phone: '+7 (000) 000-00-00', additional_phone: null, has_elevator: true, payment_status: 'prepaid', service_info: null, comment: '', status: 'new', created_at: new Date().toISOString(), updated_at: new Date().toISOString() };
|
||||
let deliveries = [
|
||||
{ ...base, id: 'demo-1', customer_name: 'Демо-клиент Анна', product_name: 'Диван «Комфорт»', service_info: 'Сборка после доставки', comment: 'Позвонить за 30 минут до приезда.' },
|
||||
{ ...base, id: 'demo-2', customer_name: 'Демо-клиент Иван', product_name: 'Обеденный стол и четыре стула', street: 'Примерная', house: '24А', apartment: null, entrance: '1', floor: '2', pickup_location: 'warehouse', warehouse_request_source: 'nursaya', has_elevator: false, payment_status: 'paid' },
|
||||
{ ...base, id: 'demo-3', customer_name: 'Демо-клиент Мария', product_name: 'Кровать и матрас', street: 'Учебная', house: '7', status: 'delivered', payment_status: 'paid' },
|
||||
{ ...base, id: 'demo-4', date: tomorrow, customer_name: 'Демо-клиент Алексей', product_name: 'Шкаф', pickup_location: 'galaktika' },
|
||||
];
|
||||
deliveries = deliveries.map(d => ({ ...d, address: `Кокшетау, ул. ${d.street}, д. ${d.house}` }));
|
||||
let failNext = false;
|
||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
const json = (response, status, data) => { response.writeHead(status, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' }); response.end(JSON.stringify(data)); };
|
||||
const toDate = value => { const [day, month, year] = value.split('-'); return `${year}-${month}-${day}`; };
|
||||
const server = createServer(async (request, response) => {
|
||||
try {
|
||||
const url = new URL(request.url, 'http://localhost');
|
||||
if (url.pathname.startsWith('/api/') || url.pathname === '/__demo__/fail-next') {
|
||||
let body = ''; for await (const chunk of request) body += chunk;
|
||||
const data = body ? JSON.parse(body) : {};
|
||||
if (url.pathname === '/__demo__/fail-next') { failNext = true; return json(response, 200, { ok: true }); }
|
||||
if (url.pathname === '/api/auth/login') {
|
||||
if (data.username !== 'demo' || data.password !== 'demo123') return json(response, 401, { error: 'Демо-вход: demo / demo123' });
|
||||
return json(response, 200, { token: `demo.${Buffer.from(JSON.stringify({ sub: 'preview-demo-user' })).toString('base64')}.demo` });
|
||||
}
|
||||
if (url.pathname === '/api/ws') return json(response, 501, { error: 'No websocket in demo' });
|
||||
if (request.method === 'GET' && url.pathname === '/api/deliveries/count') {
|
||||
const dates = [...new Set(deliveries.map(d => d.date))];
|
||||
return json(response, 200, { counts: dates.map(date => ({ date, count: deliveries.filter(d => d.date === date).length })) });
|
||||
}
|
||||
if (request.method === 'GET' && url.pathname === '/api/deliveries') return json(response, 200, { deliveries: deliveries.filter(d => d.date === toDate(url.searchParams.get('date'))) });
|
||||
await sleep(500);
|
||||
if (failNext) { failNext = false; return json(response, 500, { error: 'Simulated failure' }); }
|
||||
if (request.method === 'POST' && url.pathname === '/api/deliveries') {
|
||||
const id = randomUUID(); deliveries.push({ ...base, ...data, id, date: toDate(data.date) }); return json(response, 200, { id, message: 'created' });
|
||||
}
|
||||
const id = url.pathname.split('/')[3];
|
||||
const existing = deliveries.find(d => d.id === id);
|
||||
if (!existing) return json(response, 404, { error: 'Not found' });
|
||||
if (request.method === 'DELETE') deliveries = deliveries.filter(d => d.id !== id);
|
||||
else if (url.pathname.endsWith('/status')) existing.status = existing.status === 'new' ? 'delivered' : 'new';
|
||||
else Object.assign(existing, data, { date: toDate(data.date), updated_at: new Date().toISOString() });
|
||||
return json(response, 200, { message: 'ok' });
|
||||
}
|
||||
if (url.pathname === '/sw.js') { response.writeHead(404); return response.end(); }
|
||||
const path = resolve(root, `.${decodeURIComponent(url.pathname === '/' ? '/index.html' : url.pathname)}`);
|
||||
if (!path.startsWith(root + sep)) { response.writeHead(403); return response.end(); }
|
||||
let content = await readFile(path);
|
||||
const type = path.endsWith('.html') ? 'text/html' : path.endsWith('.js') ? 'text/javascript' : path.endsWith('.css') ? 'text/css' : path.endsWith('.svg') ? 'image/svg+xml' : path.endsWith('.json') ? 'application/json' : 'application/octet-stream';
|
||||
if (type === 'text/html') content = Buffer.from(content.toString().replace('<body>', '<body><div style="background:#fff1e5;color:#6e3900;padding:8px 16px;text-align:center;font:13px system-ui">Демо-просмотр · Вымышленные заявки · Изменения не затрагивают рабочую базу</div>'));
|
||||
response.writeHead(200, { 'Content-Type': type, 'Cache-Control': 'no-store' }); response.end(content);
|
||||
} catch { response.writeHead(404); response.end('Not found'); }
|
||||
});
|
||||
server.listen(4174, '127.0.0.1', () => console.log('Demo UI: http://127.0.0.1:4174 — login demo / demo123'));
|
||||
process.on('SIGINT', () => server.close(() => process.exit(0)));
|
||||
Reference in New Issue
Block a user