BruceBanners's picture
Male my code better, enhance it and make the UI easier to use and better.
39fe13a verified
<!DOCTYPE html>
<html lang="no">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Split-flap Admin – Canvas</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<style>
:root {
--primary: #3b82f6;
--secondary: #1f2937;
--accent: #10b981;
--danger: #ef4444;
--warning: #f59e0b;
--dark: #0f1115;
--darker: #0b0d12;
--light: #e5e7eb;
--border: #1f2330;
}
body {
scrollbar-width: thin;
scrollbar-color: var(--secondary) var(--darker);
}
body::-webkit-scrollbar {
width: 8px;
}
body::-webkit-scrollbar-track {
background: var(--darker);
}
body::-webkit-scrollbar-thumb {
background: var(--secondary);
border-radius: 4px;
}
.tile-handle {
position: absolute;
right: -4px;
bottom: -4px;
width: 12px;
height: 12px;
background: var(--primary);
border-radius: 2px;
cursor: nwse-resize;
opacity: 0;
transition: opacity 0.2s;
}
.tile:hover .tile-handle {
opacity: 1;
}
.grid-bg {
background-size: calc(100%/var(--cols)) calc(100%/var(--rows));
background-image:
linear-gradient(#1a2333 1px, transparent 1px),
linear-gradient(90deg, #1a2333 1px, transparent 1px);
}
.property-section {
transition: all 0.3s ease;
}
.log-entry {
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-5px); }
to { opacity: 1; transform: translateY(0); }
}
.status-indicator {
transition: all 0.3s ease;
}
input:focus, select:focus, textarea:focus {
outline: 2px solid var(--primary);
outline-offset: -2px;
}
.btn {
transition: all 0.2s ease;
}
.btn:hover {
transform: translateY(-1px);
}
.btn:active {
transform: translateY(1px);
}
</style>
</head>
<body class="bg-gray-900 text-gray-100 font-sans min-h-screen">
<div class="flex flex-col md:flex-row h-screen overflow-hidden">
<!-- Sidebar -->
<div class="w-full md:w-80 lg:w-96 bg-gray-800 border-r border-gray-700 overflow-y-auto p-4 space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<h1 class="text-xl font-bold text-white flex items-center">
<i data-feather="layout" class="mr-2"></i>
SplitFlap Commander
</h1>
<span id="status" class="px-2 py-1 bg-green-500/20 text-green-400 text-xs rounded-full border border-green-500/30">Ready</span>
</div>
<!-- API Configuration -->
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700">
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center">
<i data-feather="settings" class="mr-2 w-4 h-4"></i>
API Configuration
</h2>
<div class="space-y-3">
<div>
<label class="block text-xs text-gray-400 mb-1">API Endpoint</label>
<input id="apiUrl" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm" value="/cb/api.php">
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Room</label>
<input id="room" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm" value="hallA">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Token</label>
<input id="token" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm" value="BYTT-DETTE">
</div>
</div>
<div class="grid grid-cols-3 gap-2">
<button id="ping" class="btn bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="wifi" class="w-4 h-4"></i>
</button>
<button id="clear" class="btn bg-gray-600 hover:bg-gray-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
<button id="pushScene" class="btn bg-green-600 hover:bg-green-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="send" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
<!-- Grid Configuration -->
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700">
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center">
<i data-feather="grid" class="mr-2 w-4 h-4"></i>
Grid Setup
</h2>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Columns</label>
<input id="cols" type="number" min="4" max="80" value="20" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Rows</label>
<input id="rows" type="number" min="4" max="40" value="10" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
</div>
</div>
<!-- Icons Configuration -->
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700">
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center">
<i data-feather="star" class="mr-2 w-4 h-4"></i>
Icons
</h2>
<div class="grid grid-cols-3 gap-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Rail</label>
<input id="icoRail" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🚆">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Bus</label>
<input id="icoBus" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🚌">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Tram</label>
<input id="icoTram" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🚊">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Metro</label>
<input id="icoMetro" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🚇">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Water</label>
<input id="icoWater" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🛥️">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Air</label>
<input id="icoAir" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="✈️">
</div>
<div class="col-span-3">
<label class="block text-xs text-gray-400 mb-1">Other</label>
<input id="icoOther" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="•">
</div>
</div>
</div>
<!-- Tile Management -->
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700">
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center">
<i data-feather="plus-square" class="mr-2 w-4 h-4"></i>
Add Tiles
</h2>
<div class="grid grid-cols-2 gap-2">
<button id="addClock" class="btn bg-purple-600 hover:bg-purple-700 text-white py-2 px-3 rounded text-sm flex items-center justify-center">
<i data-feather="clock" class="w-4 h-4 mr-1"></i> Clock
</button>
<button id="addEntur" class="btn bg-indigo-600 hover:bg-indigo-700 text-white py-2 px-3 rounded text-sm flex items-center justify-center">
<i data-feather="list" class="w-4 h-4 mr-1"></i> Entur
</button>
<button id="addText" class="btn bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 rounded text-sm flex items-center justify-center">
<i data-feather="type" class="w-4 h-4 mr-1"></i> Text
</button>
<div class="grid grid-cols-2 gap-2">
<button id="dupTile" class="btn bg-gray-600 hover:bg-gray-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="copy" class="w-4 h-4"></i>
</button>
<button id="delTile" class="btn bg-red-600 hover:bg-red-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="trash" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
<!-- Properties Panel -->
<div id="propsPanel" class="bg-gray-750 rounded-lg p-4 border border-gray-700">
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center">
<i data-feather="sliders" class="mr-2 w-4 h-4"></i>
Properties
</h2>
<div id="props" class="space-y-4">
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs text-gray-400 mb-1">ID</label>
<input id="p_id" readonly class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm font-mono">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Type</label>
<input id="p_type" readonly class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm font-mono">
</div>
</div>
<div class="grid grid-cols-4 gap-2">
<div>
<label class="block text-xs text-gray-400 mb-1">X</label>
<input id="p_x" type="number" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Y</label>
<input id="p_y" type="number" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">W</label>
<input id="p_w" type="number" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">H</label>
<input id="p_h" type="number" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
</div>
<!-- Clock Properties -->
<div id="typeClock" class="property-section hidden space-y-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Time Format</label>
<select id="clk_fmt" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
<option value="HH:mm">HH:mm</option>
<option value="HH:mm:ss">HH:mm:ss</option>
<option value="dd.MM HH:mm">dd.MM HH:mm</option>
<option value="dd.MM">dd.MM</option>
</select>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Color</label>
<input id="clk_color" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#ffffff">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Update (ms)</label>
<input id="clk_ms" type="number" min="200" value="1000" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
</div>
</div>
<!-- Entur Properties -->
<div id="typeEntur" class="property-section hidden space-y-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Stop Place ID</label>
<input id="en_stop" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm font-mono" value="NSR:StopPlace:58287">
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Mode</label>
<select id="en_mode" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
<option value="departures">Departures</option>
<option value="arrivals">Arrivals</option>
</select>
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Limit</label>
<input id="en_limit" type="number" min="1" max="20" value="6" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Fetch (s)</label>
<input id="en_fetch" type="number" min="5" value="30" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Lines</label>
<input id="en_lines" type="number" min="1" max="20" value="6" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Template</label>
<input id="en_tpl" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm font-mono" value="[{icon}] {time} {line|cut=6} {dest|cutEnd=12} Sp{quay|pad=2} {status} {delay}">
</div>
<div class="grid grid-cols-3 gap-2">
<div>
<label class="block text-xs text-gray-400 mb-1">Normal</label>
<input id="en_c_norm" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#ffffff">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Delayed</label>
<input id="en_c_del" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#ffe66d">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Cancelled</label>
<input id="en_c_can" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#ff5c5c">
</div>
</div>
<div class="grid grid-cols-5 gap-2 pt-2">
<label class="flex items-center space-x-1">
<input id="en_vy" type="checkbox" checked class="rounded border-gray-600 text-blue-600">
<span class="text-xs">Vy</span>
</label>
<label class="flex items-center space-x-1">
<input id="en_sj" type="checkbox" checked class="rounded border-gray-600 text-blue-600">
<span class="text-xs">SJ</span>
</label>
<label class="flex items-center space-x-1">
<input id="en_fly" type="checkbox" checked class="rounded border-gray-600 text-blue-600">
<span class="text-xs">Fly</span>
</label>
<label class="flex items-center space-x-1">
<input id="en_onlydel" type="checkbox" class="rounded border-gray-600 text-blue-600">
<span class="text-xs">Delayed</span>
</label>
<label class="flex items-center space-x-1">
<input id="en_onlycan" type="checkbox" class="rounded border-gray-600 text-blue-600">
<span class="text-xs">Canceled</span>
</label>
</div>
<div class="grid grid-cols-2 gap-2 pt-2">
<button id="en_fetch_now" class="btn bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="refresh-cw" class="w-4 h-4 mr-1"></i> Fetch Now
</button>
<button id="en_preview" class="btn bg-gray-600 hover:bg-gray-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="eye" class="w-4 h-4 mr-1"></i> Preview
</button>
</div>
</div>
<!-- Text Properties -->
<div id="typeText" class="property-section hidden space-y-3">
<div>
<label class="block text-xs text-gray-400 mb-1">Text Content</label>
<input id="tx_text" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm" value="Velkommen">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Text Color</label>
<input id="tx_color" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#6daaff">
</div>
</div>
</div>
</div>
<!-- Auto Push -->
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700">
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center">
<i data-feather="repeat" class="mr-2 w-4 h-4"></i>
Auto Push
</h2>
<div class="grid grid-cols-3 gap-3 items-end">
<div class="col-span-2">
<label class="block text-xs text-gray-400 mb-1">Interval (seconds)</label>
<input id="scene_push" type="number" value="10" min="3" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
</div>
<div class="grid grid-cols-2 gap-2">
<button id="startAuto" class="btn bg-green-600 hover:bg-green-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="play" class="w-4 h-4"></i>
</button>
<button id="stopAuto" class="btn bg-red-600 hover:bg-red-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="square" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
<!-- Storage -->
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700">
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center">
<i data-feather="save" class="mr-2 w-4 h-4"></i>
Storage
</h2>
<div class="grid grid-cols-3 gap-2">
<button id="save" class="btn bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="save" class="w-4 h-4"></i>
</button>
<button id="load" class="btn bg-gray-600 hover:bg-gray-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="folder" class="w-4 h-4"></i>
</button>
<button id="export" class="btn bg-green-600 hover:bg-green-700 text-white py-2 px-3 rounded text-sm">
<i data-feather="download" class="w-4 h-4"></i>
</button>
</div>
<div class="mt-3">
<label class="block text-xs text-gray-400 mb-1">Import Configuration</label>
<input id="importFile" type="file" accept="application/json" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm file:mr-3 file:py-1 file:px-3 file:rounded file:border-0 file:text-xs file:font-medium file:bg-gray-600 file:text-white">
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Canvas Header -->
<div class="bg-gray-800 border-b border-gray-700 p-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<h2 class="text-lg font-semibold text-white">Canvas</h2>
<span id="dim" class="px-2 py-1 bg-gray-700 text-gray-300 text-xs rounded">20 × 10</span>
</div>
<div class="flex items-center space-x-2">
<button class="btn bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded" title="Zoom In">
<i data-feather="zoom-in" class="w-4 h-4"></i>
</button>
<button class="btn bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded" title="Zoom Out">
<i data-feather="zoom-out" class="w-4 h-4"></i>
</button>
<button class="btn bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded" title="Reset View">
<i data-feather="refresh-ccw" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
<!-- Canvas Area -->
<div class="flex-1 relative overflow-auto bg-gray-900 p-4">
<div id="canvas" class="relative w-full h-full min-h-[400px] bg-gray-850 border border-gray-700 rounded-lg overflow-hidden">
<div id="grid" class="absolute inset-0 grid-bg" style="--cols:20;--rows:10"></div>
<!-- Tiles will be rendered here -->
</div>
</div>
<!-- Log Area -->
<div class="bg-gray-800 border-t border-gray-700">
<div class="flex items-center justify-between p-3 border-b border-gray-700">
<h3 class="text-sm font-semibold text-gray-300 flex items-center">
<i data-feather="activity" class="mr-2 w-4 h-4"></i>
Log
</h3>
<button class="text-gray-400 hover:text-gray-300 p-1 rounded" title="Clear Log">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</div>
<div id="log" class="h-32 overflow-y-auto bg-gray-900 text-xs font-mono p-3 space-y-1"></div>
</div>
</div>
</div>
<script>
// Your original JavaScript code remains mostly unchanged, but integrated with the new UI
// Only minor adjustments for class names and selectors
const S=id=>document.getElementById(id);
const statusEl=S('status'); const logEl=S('log');
function setStatus(s, type = 'success') {
statusEl.textContent = s;
const colors = {
success: 'bg-green-500/20 text-green-400 border-green-500/30',
error: 'bg-red-500/20 text-red-400 border-red-500/30',
warning: 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30',
info: 'bg-blue-500/20 text-blue-400 border-blue-500/30'
};
statusEl.className = `px-2 py-1 text-xs rounded-full border ${colors[type] || colors.success}`;
}
function log(m,d){
const t=new Date().toLocaleTimeString();
const line=`[${t}] ${m}${d? " "+JSON.stringify(d):""}`;
const entry = document.createElement('div');
entry.className = 'log-entry text-gray-300';
entry.textContent = line;
logEl.prepend(entry);
console.log(m,d??"");
}
// The rest of your JavaScript code remains the same...
// [Your original JavaScript code continues here with minimal adjustments for the new class names]
// Initialize feather icons
feather.replace();
</script>
</body>
</html>