🔍 About This Code Showcase
This curated showcase walks through the core logic behind Wee Auto Car Care: how the seed is generated with density and skill constraints, how the 4-state bookability classifier works, and how the interactive New Booking modal validates against both bay and mechanic availability.
Full HTML markup and styling are omitted for clarity. The source focuses on the decision logic that differentiates this dashboard from a naive free/busy grid.
📁 Project Structure
projects/wee-auto-car-care/
├── generate_seed.py # Reproducible 12-day booking seed generator
├── build_dashboard.py # HTML template builder (embeds JSON)
├── workshop_bookings.json # 142 seed bookings — source of truth
└── demo.html # Self-contained interactive dashboard (~96KB)
🌱 Seed Generator — Density + Skill Constraints
generate_seed.py builds 142 bookings across 12 days (Mar 2–14, 2026, Sundays closed) while enforcing: no day 100% booked, each mechanic keeps ≥2 free slots/day, multi-slot jobs (timing belt 3h, AC compressor 3h) book consecutive slots on the same bay + mechanic.
SERVICES = [
("Tyre Replacement (4 wheels)", 45, 60, "tyre", "BAY1"),
("Hunter Alignment", 60, 80, "alignment", "BAY3"),
("Timing Belt Replacement", 180, 450, "timing_belt", "BAY2"),
("AC Service", 75, 180, "ac_service", "BAY2"),
]
MECHANICS = {
"Shamsul": ["oil", "spark_plug", "diagnostic", "timing_belt",
"ac_topup", "ac_service", "ac_compressor", "battery",
"tyre", "rotation", "balancing"],
"Vishnu": ["tyre", "rotation", "balancing", "alignment",
"oil", "spark_plug", "ac_topup", "battery"],
"Albert": ["tyre", "rotation", "oil", "battery"],
}
while placed < target and attempts < 800:
attempts += 1
svc = random.choices(SERVICES, weights=SERVICE_WEIGHTS)[0]
name, dur, price, skill, bay = svc
n = slots_needed(dur)
start_idx = random.randint(0, 8 - n)
if any(schedule[bay][start_idx + i] is not None
for i in range(n)):
continue
eligibles = [m for m, skills in MECHANICS.items()
if skill in skills]
chosen = next(
(m for m in eligibles
if all(not mech_busy[m][start_idx + i]
for i in range(n))),
None)
if not chosen:
continue
if sum(mech_busy[chosen]) + n > 6:
continue
place_booking(booking, schedule, mech_busy, bay, chosen, start_idx, n)
placed += n
🧠 4-State Bookability Classifier
The heart of the dashboard. For every empty cell in the bay grid, compute whether it is truly bookable (qualified mechanic free) or merely idle (bay free, but no one can work it).
function freeSlotAnalysis(bay, slotIdx, mechBusy) {
const freeMechs = MECHANICS.filter(m => !mechBusy[m][slotIdx]);
const baySvcs = SERVICE_CATALOG.filter(s => s.bay === bay);
const bookableServices = [];
const bookableMechs = new Set();
baySvcs.forEach(svc => {
const qualified = freeMechs.filter(
m => MECHANIC_SKILLS[m].includes(svc.skill)
);
if (qualified.length > 0) {
bookableServices.push(svc);
qualified.forEach(m => bookableMechs.add(m));
}
});
if (bookableServices.length === 0) {
return { state: "idle", reason: idleReason(bay, freeMechs) };
}
return {
state: "bookable",
mechanics: MECHANICS.filter(m => bookableMechs.has(m)),
services: bookableServices,
};
}
Example: Mar 14, 1pm. Bay 3 is empty. Vishnu (only alignment-skilled mechanic) is busy doing tyre rotation in Bay 1. A naive grid shows Bay 3 as "free." This function flags it as IDLE — "No alignment-skilled mechanic free", which prevents the taukey from wrongly telling a customer to come for alignment.
➕ Interactive New Booking — Validation
The New Booking modal dynamically filters dropdowns so the taukey can never submit an impossible booking. The slot dropdown only shows slots where the service's required bay is free. The mechanic dropdown only shows skill-matched mechanics who are free across all occupied slots.
function refreshSlotsAndMechanics() {
const date = document.getElementById("f-date").value;
const svcName = document.getElementById("f-service").value;
const svc = SERVICE_CATALOG.find(s => s.name === svcName);
const dur = SERVICE_DURATIONS()[svcName];
const n = slotsNeeded(dur);
const sched = computeSchedule(date);
const slotSel = document.getElementById("f-slot");
slotSel.innerHTML = "";
for (let i = 0; i <= 8 - n; i++) {
const free = Array.from({length: n}, (_, k) => !sched.bayBusy[svc.bay][i+k])
.every(Boolean);
if (free) {
const opt = new Option(
`${SLOT_LABELS[i]} (${n > 1 ? n + " slots" : "1 slot"}, ready ${readyTimeStr(SLOTS[i], dur)})`,
SLOTS[i]
);
slotSel.add(opt);
}
}
refreshMechanicOnly();
}
function refreshMechanicOnly() {
const qualified = MECHANICS.filter(
m => MECHANIC_SKILLS[m].includes(svc.skill)
);
const avail = qualified.filter(m =>
Array.from({length: n}, (_, k) => !sched.mechBusy[m][startIdx+k]).every(Boolean)
);
avail.forEach(m => mechSel.add(new Option(m, m)));
}
💾 localStorage Sandbox — Each Visitor Isolated
The dashboard is demo-ready without a backend because each visitor's edits persist in their own browser under a versioned storage key. Reset restores the canonical seed.
const STORAGE_KEY = "wee-auto-bookings-v1";
function cloneInitial() {
return JSON.parse(JSON.stringify(WORKSHOP_DATA));
}
function loadState() {
try {
const raw = localStorage.getItem(STORAGE_KEY);
return raw ? JSON.parse(raw) : null;
} catch (e) { return null; }
}
function saveState() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}
function resetState() {
localStorage.removeItem(STORAGE_KEY);
state = cloneInitial();
renderAll();
}
let state = loadState() || cloneInitial();
Production upgrade path: replace loadState / saveState with fetch("/api/bookings") calls to a FastAPI backend. The render functions and validation logic stay identical — only the state layer swaps.