finished meteorologia and main dashboard
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
import { ReactNode, useState } from "react";
|
||||
import { type ReactNode, useEffect, useState } from "react";
|
||||
import { Sidebar } from "../navigation/Sidebar";
|
||||
import { TopBar } from "./TopBar";
|
||||
import { BottomStatusBar } from "./BottomStatusBar";
|
||||
import { TopBar } from "../layout/TopBar";
|
||||
import { useTelemetryStream } from "../../features/telemetry/hooks/useTelemetryStream";
|
||||
import { useNotifications } from "../../features/notifications/hooks/useNotifications";
|
||||
import { useCurrentUser } from "../../features/auth/hooks/useCurrentUser";
|
||||
import { useRuntimeConfig } from "../../features/system/hooks/useRuntimeConfig";
|
||||
import type { TelemetrySnapshot } from "../../types/telemetry";
|
||||
import type { AppPage } from "../../app/App";
|
||||
|
||||
type Theme = "dark" | "light";
|
||||
|
||||
type AppShellRenderProps = {
|
||||
theme: "dark" | "light";
|
||||
theme: Theme;
|
||||
snapshots: TelemetrySnapshot[];
|
||||
};
|
||||
|
||||
@@ -19,31 +19,71 @@ type AppShellProps = {
|
||||
onNavigate: (page: AppPage) => void;
|
||||
children: (props: AppShellRenderProps) => ReactNode;
|
||||
};
|
||||
|
||||
const THEME_STORAGE_KEY = "app-theme";
|
||||
|
||||
export function AppShell({ activePage, onNavigate, children }: AppShellProps) {
|
||||
const telemetry = useTelemetryStream();
|
||||
const notifications = useNotifications();
|
||||
const currentUser = useCurrentUser();
|
||||
const runtime = useRuntimeConfig();
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
|
||||
const [theme, setTheme] = useState<"dark" | "light">("dark");
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
const [theme, setTheme] = useState<Theme>(() => {
|
||||
const stored = localStorage.getItem(THEME_STORAGE_KEY);
|
||||
return stored === "light" || stored === "dark" ? stored : "dark";
|
||||
});
|
||||
|
||||
const isDark = theme === "dark";
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
||||
}, [theme]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme((current) => (current === "dark" ? "light" : "dark"));
|
||||
};
|
||||
|
||||
const isDark = theme === "dark";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
isDark
|
||||
? "h-screen overflow-hidden bg-[#0E1A24] text-[#EAF2FA]"
|
||||
: "h-screen overflow-hidden bg-[#F4F7FA] text-[#102030]"
|
||||
? "fixed inset-0 overflow-hidden bg-[#0b1220] text-slate-100"
|
||||
: "fixed inset-0 overflow-hidden bg-slate-100 text-slate-950"
|
||||
}
|
||||
>
|
||||
<style>{`
|
||||
.app-scrollbar {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: ${isDark ? "#334155 #0b1220" : "#94a3b8 #f1f5f9"};
|
||||
}
|
||||
|
||||
.app-scrollbar::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.app-scrollbar::-webkit-scrollbar-track {
|
||||
background: ${isDark ? "#0b1220" : "#f1f5f9"};
|
||||
border-left: 1px solid ${isDark ? "rgba(255,255,255,0.06)" : "#e2e8f0"};
|
||||
}
|
||||
|
||||
.app-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: ${isDark ? "#334155" : "#94a3b8"};
|
||||
border-radius: 999px;
|
||||
border: 2px solid ${isDark ? "#0b1220" : "#f1f5f9"};
|
||||
}
|
||||
|
||||
.app-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: ${isDark ? "#475569" : "#64748b"};
|
||||
}
|
||||
|
||||
.app-scrollbar::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<div className="flex h-full overflow-hidden">
|
||||
<div className="relative h-full shrink-0 overflow-hidden">
|
||||
<aside className="relative h-full shrink-0 overflow-hidden">
|
||||
<Sidebar
|
||||
theme={theme}
|
||||
activePage={activePage}
|
||||
@@ -51,8 +91,15 @@ export function AppShell({ activePage, onNavigate, children }: AppShellProps) {
|
||||
onNavigate={onNavigate}
|
||||
onToggleCollapsed={() => setSidebarCollapsed((current) => !current)}
|
||||
/>
|
||||
<div className="pointer-events-none absolute right-0 top-0 h-full w-2 bg-gradient-to-r from-[#3A5064]/6 to-transparent blur-sm" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={
|
||||
isDark
|
||||
? "pointer-events-none absolute right-0 top-0 h-full w-px bg-white/10"
|
||||
: "pointer-events-none absolute right-0 top-0 h-full w-px bg-slate-200"
|
||||
}
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<div className="flex min-w-0 flex-1 flex-col overflow-hidden">
|
||||
<TopBar
|
||||
@@ -68,23 +115,23 @@ export function AppShell({ activePage, onNavigate, children }: AppShellProps) {
|
||||
<main
|
||||
className={
|
||||
isDark
|
||||
? "custom-scrollbar min-h-0 flex-1 overflow-y-auto bg-[#0E1A24] px-6 py-5"
|
||||
: "custom-scrollbar min-h-0 flex-1 overflow-y-auto bg-[#F4F7FA] px-6 py-5"
|
||||
? `app-scrollbar min-h-0 flex-1 border-t border-white/10 bg-[#0b1220] ${activePage === "dashboard"
|
||||
? "overflow-hidden p-0"
|
||||
: "overflow-y-auto p-4"
|
||||
}`
|
||||
: `app-scrollbar min-h-0 flex-1 border-t border-slate-200 bg-slate-100 ${activePage === "dashboard"
|
||||
? "overflow-hidden p-0"
|
||||
: "overflow-y-auto p-4"
|
||||
}`
|
||||
}
|
||||
>
|
||||
{children({
|
||||
theme,
|
||||
snapshots: telemetry.snapshots,
|
||||
})}
|
||||
<div className="h-full w-full">
|
||||
{children({
|
||||
theme,
|
||||
snapshots: telemetry.snapshots,
|
||||
})}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<BottomStatusBar
|
||||
theme={theme}
|
||||
backendPort={runtime.runtimeConfig?.backendPort.toString()}
|
||||
mode={runtime.runtimeConfig?.mode}
|
||||
controllerName={runtime.runtimeConfig?.controllerName}
|
||||
controllerIp={runtime.runtimeConfig?.controllerIp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user