last update before handover
This commit is contained in:
@@ -2,19 +2,19 @@ import { ChevronUp, ChevronDown } from "lucide-react";
|
||||
import { Button } from "./ui/button";
|
||||
|
||||
export function NavigationButtons() {
|
||||
const scrollAmount = 200;
|
||||
const scrollAmount = 300;
|
||||
|
||||
const scrollUp = () => {
|
||||
const panel = document.getElementById("details-panel");
|
||||
if (panel) {
|
||||
panel.scrollBy({ top: -scrollAmount, behavior: "smooth" });
|
||||
panel.scrollBy({ top: -scrollAmount, behavior: "auto" });
|
||||
}
|
||||
};
|
||||
|
||||
const scrollDown = () => {
|
||||
const panel = document.getElementById("details-panel");
|
||||
if (panel) {
|
||||
panel.scrollBy({ top: scrollAmount, behavior: "smooth" });
|
||||
panel.scrollBy({ top: scrollAmount, behavior: "auto" });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
HardDrive,
|
||||
LogIn,
|
||||
ChevronRight,
|
||||
ChevronDown
|
||||
ChevronDown,
|
||||
HelpCircle,
|
||||
HeadphonesIcon
|
||||
} from "lucide-react";
|
||||
|
||||
interface SidebarProps {
|
||||
@@ -85,6 +87,18 @@ const menuItems: MenuItem[] = [
|
||||
icon: <LogIn className="w-5 h-5" />,
|
||||
path: "/login",
|
||||
},
|
||||
{
|
||||
id: "help",
|
||||
label: "HELP",
|
||||
icon: <HelpCircle className="w-5 h-5" />,
|
||||
path: "/help",
|
||||
},
|
||||
{
|
||||
id: "support",
|
||||
label: "SUPPORT",
|
||||
icon: <HeadphonesIcon className="w-5 h-5" />,
|
||||
path: "/support",
|
||||
},
|
||||
];
|
||||
|
||||
export function Sidebar({ collapsed, onToggle }: SidebarProps) {
|
||||
|
||||
@@ -43,9 +43,31 @@ export function DateTimeSettingView() {
|
||||
id="timezone"
|
||||
className="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded text-white"
|
||||
>
|
||||
<option>UTC+8 (Philippine Time)</option>
|
||||
<option>UTC+0 (GMT)</option>
|
||||
<option>UTC+1 (CET)</option>
|
||||
<option>UTC+2 (EET)</option>
|
||||
<option>UTC+3 (EAT)</option>
|
||||
<option>UTC+4 (GST)</option>
|
||||
<option>UTC+5 (PKT)</option>
|
||||
<option>UTC+5:30 (IST)</option>
|
||||
<option>UTC+6 (BTT)</option>
|
||||
<option>UTC+7 (ICT)</option>
|
||||
<option>UTC+8 (MYT - Malaysian Time)</option>
|
||||
<option>UTC+8 (PHT - Philippine Time)</option>
|
||||
<option>UTC+9 (JST)</option>
|
||||
<option>UTC+10 (AEST)</option>
|
||||
<option>UTC+11 (AEDT)</option>
|
||||
<option>UTC+12 (NZST)</option>
|
||||
<option>UTC-1 (AZOT)</option>
|
||||
<option>UTC-2 (BRT)</option>
|
||||
<option>UTC-3 (ART)</option>
|
||||
<option>UTC-4 (AST)</option>
|
||||
<option>UTC-5 (EST)</option>
|
||||
<option>UTC-6 (CST)</option>
|
||||
<option>UTC-7 (MST)</option>
|
||||
<option>UTC-8 (PST)</option>
|
||||
<option>UTC-9 (AKST)</option>
|
||||
<option>UTC-10 (HST)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,51 +1,229 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
|
||||
import { useEffect } from 'react';
|
||||
import { Card, CardContent, CardTitle } from "../ui/card";
|
||||
import { BarChart3 } from "lucide-react";
|
||||
import { useSensorStore } from "../../stores/sensorStore";
|
||||
import {
|
||||
BarChart,
|
||||
Bar,
|
||||
LineChart,
|
||||
Line,
|
||||
AreaChart,
|
||||
Area,
|
||||
ComposedChart,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer
|
||||
} from 'recharts';
|
||||
|
||||
const visualizations = [
|
||||
{ id: 'rainfall', title: 'Rainfall', type: 'bar' },
|
||||
{ id: 'voltage', title: 'Voltage', type: 'composed' },
|
||||
{ id: 'signal', title: 'Signal Strength', type: 'area' },
|
||||
{ id: 'waterlevel', title: 'Water Level', type: 'line' },
|
||||
{ id: 'adc', title: 'ADC Channels', type: 'multiline' },
|
||||
{ id: 'temperature', title: 'Temperature', type: 'area' },
|
||||
];
|
||||
|
||||
function generateMockData(id: string) {
|
||||
const hours = Array.from({ length: 24 }, (_, i) => i);
|
||||
switch (id) {
|
||||
case 'rainfall':
|
||||
return hours.map(h => ({ name: `${h}:00`, rainfall: Math.random() * 5 }));
|
||||
case 'voltage':
|
||||
return hours.map(h => ({ name: `${h}:00`, solar: 11 + Math.random() * 4, battery: 11.5 + Math.random() * 2 }));
|
||||
case 'signal':
|
||||
return hours.map(h => ({ name: `${h}:00`, asu: Math.floor(Math.random() * 31), dBm: -113 + Math.floor(Math.random() * 60) }));
|
||||
case 'waterlevel':
|
||||
return hours.map(h => ({ name: `${h}:00`, level: 1 + Math.random() * 3 }));
|
||||
case 'adc':
|
||||
return hours.map(h => ({ name: `${h}:00`, ch1: Math.random() * 20, ch2: Math.random() * 20, ch3: Math.random() * 20, ch4: Math.random() * 20 }));
|
||||
case 'temperature':
|
||||
return hours.map(h => ({ name: `${h}:00`, temp: 20 + Math.random() * 10 }));
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function renderChart(id: string, data: { name: string; [key: string]: number | string }[]) {
|
||||
switch (id) {
|
||||
case 'rainfall':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={190}>
|
||||
<BarChart data={data} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||
<XAxis dataKey="name" stroke="#9CA3AF" fontSize={8} tickLine={false} />
|
||||
<YAxis stroke="#9CA3AF" fontSize={8} tickLine={false} />
|
||||
<Tooltip contentStyle={{ backgroundColor: '#1F2937', border: 'none', fontSize: 10 }} />
|
||||
<Bar dataKey="rainfall" fill="#3B82F6" radius={[2, 2, 0, 0]} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
case 'voltage':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={190}>
|
||||
<ComposedChart data={data} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||
<XAxis dataKey="name" stroke="#9CA3AF" fontSize={8} tickLine={false} />
|
||||
<YAxis stroke="#9CA3AF" fontSize={8} tickLine={false} domain={[0, 20]} />
|
||||
<Tooltip contentStyle={{ backgroundColor: '#1F2937', border: 'none', fontSize: 10 }} />
|
||||
<Line type="monotone" dataKey="solar" stroke="#F59E0B" dot={false} strokeWidth={2} />
|
||||
<Line type="monotone" dataKey="battery" stroke="#10B981" dot={false} strokeWidth={2} />
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
case 'signal':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={190}>
|
||||
<AreaChart data={data} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||
<XAxis dataKey="name" stroke="#9CA3AF" fontSize={8} tickLine={false} />
|
||||
<YAxis stroke="#9CA3AF" fontSize={8} tickLine={false} domain={[-120, 0]} />
|
||||
<Tooltip contentStyle={{ backgroundColor: '#1F2937', border: 'none', fontSize: 10 }} />
|
||||
<Area type="monotone" dataKey="dBm" stroke="#3B82F6" fill="#3B82F6" fillOpacity={0.3} />
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
case 'waterlevel':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={190}>
|
||||
<LineChart data={data} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||
<XAxis dataKey="name" stroke="#9CA3AF" fontSize={8} tickLine={false} />
|
||||
<YAxis stroke="#9CA3AF" fontSize={8} tickLine={false} domain={[0, 5]} />
|
||||
<Tooltip contentStyle={{ backgroundColor: '#1F2937', border: 'none', fontSize: 10 }} />
|
||||
<Line type="monotone" dataKey="level" stroke="#3B82F6" strokeWidth={2} dot={false} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
case 'adc':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={190}>
|
||||
<LineChart data={data} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||
<XAxis dataKey="name" stroke="#9CA3AF" fontSize={8} tickLine={false} />
|
||||
<YAxis stroke="#9CA3AF" fontSize={8} tickLine={false} domain={[0, 25]} />
|
||||
<Tooltip contentStyle={{ backgroundColor: '#1F2937', border: 'none', fontSize: 10 }} />
|
||||
<Line type="monotone" dataKey="ch1" stroke="#3B82F6" dot={false} strokeWidth={1} />
|
||||
<Line type="monotone" dataKey="ch2" stroke="#10B981" dot={false} strokeWidth={1} />
|
||||
<Line type="monotone" dataKey="ch3" stroke="#8B5CF6" dot={false} strokeWidth={1} />
|
||||
<Line type="monotone" dataKey="ch4" stroke="#F59E0B" dot={false} strokeWidth={1} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
case 'temperature':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={190}>
|
||||
<AreaChart data={data} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||
<XAxis dataKey="name" stroke="#9CA3AF" fontSize={8} tickLine={false} />
|
||||
<YAxis stroke="#9CA3AF" fontSize={8} tickLine={false} domain={[15, 35]} />
|
||||
<Tooltip contentStyle={{ backgroundColor: '#1F2937', border: 'none', fontSize: 10 }} />
|
||||
<Area type="monotone" dataKey="temp" stroke="#EF4444" fill="#EF4444" fillOpacity={0.3} />
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function GraphView() {
|
||||
const { data } = useSensorStore();
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const panel = document.getElementById("details-panel");
|
||||
if (!panel) return;
|
||||
|
||||
if (e.key === 'PageUp' || e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
panel.scrollTop -= Math.max(300, panel.clientHeight);
|
||||
} else if (e.key === 'PageDown' || e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
panel.scrollTop += Math.max(300, panel.clientHeight);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="h-full p-1 flex flex-col">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<BarChart3 className="w-5 h-5 text-blue-400" />
|
||||
<h1 className="text-lg font-bold text-white">Graph View</h1>
|
||||
<div className="h-full flex flex-col relative">
|
||||
<div className="flex items-center justify-between p-1 bg-gray-800 border-b border-gray-700 relative z-10">
|
||||
<div className="flex items-center">
|
||||
<BarChart3 className="w-4 h-4 text-blue-400 mr-2" />
|
||||
<h1 className="text-base font-bold text-white">Graph View</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2 mb-2">
|
||||
<Card className="bg-gray-900 border-gray-700">
|
||||
<CardContent className="py-3">
|
||||
<div className="text-center">
|
||||
<div className="text-xl font-bold text-blue-400">24h</div>
|
||||
<div className="text-xs text-gray-400">Time Range</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="bg-gray-900 border-gray-700">
|
||||
<CardContent className="py-3">
|
||||
<div className="text-center">
|
||||
<div className="text-xl font-bold text-green-400">156</div>
|
||||
<div className="text-xs text-gray-400">Data Points</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="bg-gray-900 border-gray-700">
|
||||
<CardContent className="py-3">
|
||||
<div className="text-center">
|
||||
<div className="text-xl font-bold text-purple-400">12.5</div>
|
||||
<div className="text-xs text-gray-400">Avg Value</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
{visualizations.map((viz, idx) => {
|
||||
const chartData = generateMockData(viz.id);
|
||||
|
||||
let stats: { label: string; value: string; color: string }[] = [];
|
||||
switch (viz.id) {
|
||||
case 'rainfall':
|
||||
stats = [
|
||||
{ label: '24h', value: `${data.rainfall.today}mm`, color: 'text-blue-400' },
|
||||
{ label: 'Avg', value: `${(data.rainfall.today / 24).toFixed(1)}mm`, color: 'text-purple-400' },
|
||||
];
|
||||
break;
|
||||
case 'voltage':
|
||||
stats = [
|
||||
{ label: 'Solar', value: `${data.voltage.solar.toFixed(1)}V`, color: 'text-yellow-400' },
|
||||
{ label: 'Battery', value: `${data.voltage.battery.toFixed(1)}V`, color: 'text-green-400' },
|
||||
];
|
||||
break;
|
||||
case 'signal':
|
||||
stats = [
|
||||
{ label: 'ASU', value: `${data.communication.asu}`, color: 'text-blue-400' },
|
||||
{ label: 'dBm', value: `${data.communication.dBm}`, color: 'text-green-400' },
|
||||
];
|
||||
break;
|
||||
case 'waterlevel':
|
||||
stats = [
|
||||
{ label: 'Level', value: '2.5m', color: 'text-blue-400' },
|
||||
{ label: 'Status', value: 'Normal', color: 'text-green-400' },
|
||||
];
|
||||
break;
|
||||
case 'adc':
|
||||
stats = [
|
||||
{ label: 'CH1', value: '12.4mA', color: 'text-blue-400' },
|
||||
{ label: 'CH2', value: '8.2mA', color: 'text-green-400' },
|
||||
];
|
||||
break;
|
||||
case 'temperature':
|
||||
stats = [
|
||||
{ label: 'Current', value: '26.5°C', color: 'text-red-400' },
|
||||
{ label: 'Max', value: '28.1°C', color: 'text-orange-400' },
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
<Card className="bg-gray-900 border-gray-700 flex-1">
|
||||
<CardHeader className="py-2">
|
||||
<CardTitle className="text-white text-sm">Data Visualization</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="py-2 h-full">
|
||||
<div className="h-full bg-gray-800 rounded flex items-center justify-center">
|
||||
<span className="text-gray-500 text-sm">Chart visualization area</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
return (
|
||||
<div key={viz.id} data-graph-card className="mb-1">
|
||||
<Card className="bg-gray-900 border-gray-700">
|
||||
<CardContent className="py-1 px-2">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<CardTitle className="text-white text-sm m-0">{viz.title}</CardTitle>
|
||||
{stats.map((stat, sidx) => (
|
||||
<span key={sidx} className="text-xs">
|
||||
<span className={stat.color}>{stat.value}</span>
|
||||
<span className="text-gray-500 ml-0.5">{stat.label}</span>
|
||||
</span>
|
||||
))}
|
||||
<span className="text-xs text-gray-600 ml-auto">{idx + 1}/{visualizations.length}</span>
|
||||
</div>
|
||||
{renderChart(viz.id, chartData)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
74
sample_interface/src/app/components/views/HelpView.tsx
Normal file
74
sample_interface/src/app/components/views/HelpView.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { useRef, useEffect } from 'react';
|
||||
import Markdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { components } from './MarkdownComponents';
|
||||
import helpContent from '../../../../HELP.md?raw';
|
||||
|
||||
const tocItems = [
|
||||
{ id: 'overview', label: '1. Overview' },
|
||||
{ id: 'navigation', label: '2. Navigation' },
|
||||
{ id: 'main-sections', label: '3. Main Sections' },
|
||||
{ id: 'keyboard-shortcuts', label: '4. Keyboard Shortcuts' },
|
||||
{ id: 'display-modes', label: '5. Display Modes' },
|
||||
];
|
||||
|
||||
export function HelpView() {
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const scrollToSection = (id: string) => {
|
||||
const element = document.getElementById(id);
|
||||
const panel = document.getElementById("details-panel");
|
||||
if (element && panel) {
|
||||
const panelRect = panel.getBoundingClientRect();
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
const offset = elementRect.top - panelRect.top + panel.scrollTop;
|
||||
panel.scrollTop = offset;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const panel = document.getElementById("details-panel");
|
||||
if (!panel) return;
|
||||
|
||||
if (e.key === 'PageUp' || e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
panel.scrollTop -= Math.max(200, panel.clientHeight);
|
||||
} else if (e.key === 'PageDown' || e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
panel.scrollTop += Math.max(200, panel.clientHeight);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, []);
|
||||
|
||||
const contentWithoutToc = helpContent.replace(/^# Table of Contents[\s\S]*?^---$/m, '');
|
||||
|
||||
return (
|
||||
<div className="h-full overflow-y-auto">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div className="bg-gray-900 border border-gray-700 rounded-lg p-4 sticky top-0">
|
||||
<h2 className="text-xl font-bold text-white mb-4">Table of Contents</h2>
|
||||
<nav className="space-y-2">
|
||||
{tocItems.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => scrollToSection(item.id)}
|
||||
className="block text-blue-400 hover:underline text-left w-full"
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
<div id="details-panel" ref={contentRef} className="bg-gray-900 border border-gray-700 rounded-lg p-4 overflow-y-auto max-h-[calc(100vh-200px)]">
|
||||
<div className="prose prose-invert max-w-none prose-sm prose-table:border-collapse prose-th:border prose-td:border prose-th:border-gray-600 prose-td:border-gray-600 prose-th:p-2 prose-td:p-2 prose-thead:bg-gray-800 prose-tr:hover:bg-gray-800">
|
||||
<Markdown remarkPlugins={[remarkGfm]} components={components}>{contentWithoutToc}</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Components } from 'react-markdown';
|
||||
|
||||
export const components: Components = {
|
||||
h1: ({ node, children, ...props }) => {
|
||||
const text = Array.isArray(children) ? children.join('') : String(children);
|
||||
const id = text.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
||||
return <h1 id={id} {...props}>{children}</h1>;
|
||||
},
|
||||
h2: ({ node, children, ...props }) => {
|
||||
const text = Array.isArray(children) ? children.join('') : String(children);
|
||||
const id = text.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
||||
return <h2 id={id} {...props}>{children}</h2>;
|
||||
},
|
||||
h3: ({ node, children, ...props }) => {
|
||||
const text = Array.isArray(children) ? children.join('') : String(children);
|
||||
const id = text.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
||||
return <h3 id={id} {...props}>{children}</h3>;
|
||||
},
|
||||
};
|
||||
55
sample_interface/src/app/components/views/SupportView.tsx
Normal file
55
sample_interface/src/app/components/views/SupportView.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Mail, Phone, User } from "lucide-react";
|
||||
|
||||
export function SupportView() {
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center">
|
||||
<div className="bg-gray-900 border border-gray-700 rounded-lg p-8 max-w-md w-full">
|
||||
<div className="text-center mb-6">
|
||||
<div className="h-16 mb-4 flex items-center justify-center">
|
||||
<span className="text-3xl font-bold text-blue-400">TCK</span>
|
||||
<span className="text-3xl font-bold text-red-500 ml-1">e-Solutions</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-white">Support</h1>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3 p-3 bg-gray-800 rounded-lg">
|
||||
<User className="w-5 h-5 text-blue-400" />
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Company</p>
|
||||
<p className="text-white">TCK e-Solutions Sdn. Bhd.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 p-3 bg-gray-800 rounded-lg">
|
||||
<User className="w-5 h-5 text-blue-400" />
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Contact Person</p>
|
||||
<p className="text-white">C-Fu</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 p-3 bg-gray-800 rounded-lg">
|
||||
<Mail className="w-5 h-5 text-blue-400" />
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Email</p>
|
||||
<a href="mailto:cloud@tck.com.my" className="text-blue-400 hover:underline">
|
||||
cloud@tck.com.my
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 p-3 bg-gray-800 rounded-lg">
|
||||
<Phone className="w-5 h-5 text-blue-400" />
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Phone</p>
|
||||
<a href="tel:+601229292929" className="text-blue-400 hover:underline">
|
||||
+601229292929
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -16,6 +16,8 @@ import { CalibrationView } from "./components/views/CalibrationView";
|
||||
import { FlashMemoryView } from "./components/views/FlashMemoryView";
|
||||
import { SettingView } from "./components/views/SettingView";
|
||||
import { LoginView } from "./components/views/LoginView";
|
||||
import { HelpView } from "./components/views/HelpView";
|
||||
import { SupportView } from "./components/views/SupportView";
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
@@ -39,6 +41,8 @@ export const router = createBrowserRouter([
|
||||
{ path: "flash-memory", Component: FlashMemoryView },
|
||||
{ path: "setting", Component: SettingView },
|
||||
{ path: "login", Component: LoginView },
|
||||
{ path: "help", Component: HelpView },
|
||||
{ path: "support", Component: SupportView },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
@custom-variant light (&:is(.light *));
|
||||
|
||||
:root {
|
||||
--font-size: 16px;
|
||||
@@ -188,4 +189,67 @@
|
||||
#details-panel::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera */
|
||||
}
|
||||
}
|
||||
|
||||
/* Light mode overrides for hardcoded dark backgrounds */
|
||||
.light {
|
||||
--bg-gray-900: #f9fafb;
|
||||
--bg-gray-800: #f3f4f6;
|
||||
--bg-gray-700: #e5e7eb;
|
||||
--bg-gray-950: #ffffff;
|
||||
}
|
||||
|
||||
.light .bg-gray-900 {
|
||||
background-color: #ffffff !important;
|
||||
border-color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.light .bg-gray-900 \* {
|
||||
color: #111827 !important;
|
||||
}
|
||||
|
||||
.light .bg-gray-800 {
|
||||
background-color: #f3f4f6 !important;
|
||||
border-color: #d1d5db !important;
|
||||
}
|
||||
|
||||
.light .bg-gray-800 \* {
|
||||
color: #111827 !important;
|
||||
}
|
||||
|
||||
.light .bg-gray-700 {
|
||||
background-color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.light .bg-gray-700 \* {
|
||||
color: #111827 !important;
|
||||
}
|
||||
|
||||
.light .text-white {
|
||||
color: #111827 !important;
|
||||
}
|
||||
|
||||
.light .text-gray-300,
|
||||
.light .text-gray-400,
|
||||
.light .text-gray-500,
|
||||
.light .text-gray-600 {
|
||||
color: #4b5563 !important;
|
||||
}
|
||||
|
||||
.light .border-gray-700,
|
||||
.light .border-gray-600,
|
||||
.light .border-gray-500 {
|
||||
border-color: #d1d5db !important;
|
||||
}
|
||||
|
||||
.light .bg-gray-950 {
|
||||
background-color: #f9fafb !important;
|
||||
}
|
||||
|
||||
.light .bg-gray-950 \* {
|
||||
color: #111827 !important;
|
||||
}
|
||||
|
||||
.light .hover\:bg-gray-800:hover {
|
||||
background-color: #e5e7eb !important;
|
||||
}
|
||||
Reference in New Issue
Block a user