templates/chicorytv-admin/Settings.jsx wired into app.html; design_handoff_settings/ carries the implementation spec + API mapping. Synced to the Claude Design project via DesignSync. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
145 lines
7.6 KiB
HTML
145 lines
7.6 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>ChicoryTV Admin</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..700&family=Geist+Mono:wght@400..600&display=swap" rel="stylesheet">
|
|
<script src="./ds-base.js"></script>
|
|
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/lucide@0.544.0/dist/umd/lucide.min.js"></script>
|
|
<script>
|
|
// Apply saved preview theme before paint. Primary (warm) = no attribute.
|
|
try {
|
|
var t = localStorage.getItem("ctv-theme");
|
|
if (t === "chicory") t = "cool"; // migrate old default name
|
|
if (t === "cool" || t === "dual") document.documentElement.dataset.theme = t;
|
|
} catch (e) {}
|
|
</script>
|
|
<style>
|
|
html, body { margin: 0; height: 100%; background: var(--surface-app); color: var(--text-primary); font-family: var(--font-sans); }
|
|
#root { height: 100vh; }
|
|
* { box-sizing: border-box; }
|
|
::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
::-webkit-scrollbar-thumb { background: var(--ctv-surface-3); border-radius: 6px; border: 2px solid var(--surface-app); }
|
|
::-webkit-scrollbar-track { background: transparent; }
|
|
/* Overlay scroll regions (custom fading indicator) hide the native bar so
|
|
content never reflows when a scrollbar appears/disappears. */
|
|
.ctv-hide-sb::-webkit-scrollbar { width: 0; height: 0; }
|
|
/* Motion primitives — subtle, iOS/macOS-style. Quick, ease-out, small. */
|
|
@keyframes ctv-pop { from { transform: scale(0.4); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
|
@keyframes ctv-fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
|
|
.ctv-lift { transition: transform 240ms cubic-bezier(0.16,1,0.3,1), box-shadow 240ms cubic-bezier(0.16,1,0.3,1); }
|
|
.ctv-lift:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
|
|
.ctv-press { transition: transform 150ms cubic-bezier(0.16,1,0.3,1), background 220ms cubic-bezier(0.16,1,0.3,1), color 220ms cubic-bezier(0.16,1,0.3,1), border-color 220ms cubic-bezier(0.16,1,0.3,1); }
|
|
.ctv-press:active { transform: scale(0.96); }
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.ctv-lift, .ctv-press { transition: none; }
|
|
.ctv-lift:hover, .ctv-press:active { transform: none; }
|
|
* { scroll-behavior: auto !important; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
|
|
<!-- Theme preview switcher — a design-system demo affordance, not product chrome -->
|
|
<div id="theme-switch" style="position:fixed;left:16px;bottom:14px;z-index:999;display:flex;align-items:center;gap:8px;padding:7px 9px;background:var(--surface-raised);border:1px solid var(--border-control);border-radius:var(--radius-pill);box-shadow:var(--shadow-pop);font-family:var(--font-sans)">
|
|
<span style="font:600 10px/1 var(--font-sans);letter-spacing:.06em;text-transform:uppercase;color:var(--text-disabled);padding-left:4px">Theme</span>
|
|
<button data-theme-btn="warm" title="Warm chicory (primary)" style="width:22px;height:22px;border-radius:50%;border:2px solid transparent;background:#E08A3C;cursor:pointer;padding:0"></button>
|
|
<button data-theme-btn="cool" title="Cool periwinkle" style="width:22px;height:22px;border-radius:50%;border:2px solid transparent;background:#5B7CFA;cursor:pointer;padding:0"></button>
|
|
<button data-theme-btn="dual" title="Dual accent" style="width:22px;height:22px;border-radius:50%;border:2px solid transparent;background:linear-gradient(135deg,#5B7CFA 0 50%,#F0663F 50% 100%);cursor:pointer;padding:0"></button>
|
|
</div>
|
|
<script>
|
|
(function () {
|
|
var current = document.documentElement.dataset.theme || "warm";
|
|
var btns = document.querySelectorAll("#theme-switch [data-theme-btn]");
|
|
function paint() {
|
|
current = document.documentElement.dataset.theme || "warm";
|
|
btns.forEach(function (b) {
|
|
b.style.borderColor = b.getAttribute("data-theme-btn") === current ? "var(--text-primary)" : "transparent";
|
|
});
|
|
}
|
|
btns.forEach(function (b) {
|
|
b.addEventListener("click", function () {
|
|
var t = b.getAttribute("data-theme-btn");
|
|
if (t === "warm") delete document.documentElement.dataset.theme;
|
|
else document.documentElement.dataset.theme = t;
|
|
try { localStorage.setItem("ctv-theme", t); } catch (e) {}
|
|
paint();
|
|
});
|
|
});
|
|
paint();
|
|
})();
|
|
</script>
|
|
<script src="data.js"></script>
|
|
<script type="text/babel" src="icons.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="Shell.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="Dashboard.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="Channels.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="ChannelBuilder.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="ScheduleEditor.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="Epg.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="Playouts.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="ScheduleLibrary.jsx" data-presets="react"></script>
|
|
<script type="text/babel" src="Settings.jsx" data-presets="react"></script>
|
|
<script type="text/babel" data-presets="react">
|
|
const { Sidebar, TopBar } = window.CTVShell;
|
|
|
|
const TITLES = {
|
|
dashboard: "Dashboard", channels: "Channels", builder: "New Channel", guide: "Guide", schedules: "Schedules",
|
|
playouts: "Playouts", collections: "Collections", libraries: "Libraries", settings: "Settings",
|
|
};
|
|
|
|
function Placeholder({ label }) {
|
|
const Ico = window.Ico;
|
|
return (
|
|
<div style={{ height: "100%", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, color: "var(--text-disabled)" }}>
|
|
<Ico n="Wrench" s={26} />
|
|
<span style={{ font: "var(--text-sm)/1 var(--font-sans)" }}>{label} — not part of this UI kit</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function App() {
|
|
const [view, setView] = React.useState("dashboard");
|
|
const screens = {
|
|
dashboard: window.CTVDashboard,
|
|
channels: window.CTVChannels,
|
|
builder: window.CTVChannelBuilder,
|
|
guide: window.CTVEpg,
|
|
schedules: window.CTVScheduleEditor,
|
|
playouts: window.CTVPlayouts,
|
|
libraries: window.CTVLibraries,
|
|
settings: window.CTVSettings,
|
|
};
|
|
const Screen = screens[view];
|
|
return (
|
|
<div style={{ display: "flex", height: "100%" }}>
|
|
<Sidebar view={view} setView={setView} />
|
|
<div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", height: "100%" }}>
|
|
<TopBar title={TITLES[view]} />
|
|
<main style={{ flex: 1, minHeight: 0, overflow: "auto" }}>
|
|
{Screen ? <Screen /> : <Placeholder label={TITLES[view]} />}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function mount() {
|
|
if (!window.ChicoryTVDesignSystem_eb3b61 || !window.CTVShell || !window.CTVDashboard ||
|
|
!window.CTVChannels || !window.CTVChannelBuilder || !window.CTVScheduleEditor || !window.CTVEpg ||
|
|
!window.CTVPlayouts || !window.CTVLibraries || !window.CTVSettings) {
|
|
return setTimeout(mount, 40);
|
|
}
|
|
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
|
|
}
|
|
mount();
|
|
</script>
|
|
</body>
|
|
</html>
|