mirror of
https://github.com/luckfox-eng29/kvm.git
synced 2026-01-18 03:28:19 +01:00
Release 202412292127
This commit is contained in:
153
ui/src/components/KvmCard.tsx
Normal file
153
ui/src/components/KvmCard.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
import { Button, LinkButton } from "@components/Button";
|
||||
import Card from "@components/Card";
|
||||
import { MdConnectWithoutContact } from "react-icons/md";
|
||||
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { LuEllipsisVertical } from "react-icons/lu";
|
||||
|
||||
function getRelativeTimeString(date: Date | number, lang = navigator.language): string {
|
||||
// Allow dates or times to be passed
|
||||
const timeMs = typeof date === "number" ? date : date.getTime();
|
||||
|
||||
// Get the amount of seconds between the given date and now
|
||||
const deltaSeconds = Math.round((timeMs - Date.now()) / 1000);
|
||||
|
||||
// Array reprsenting one minute, hour, day, week, month, etc in seconds
|
||||
const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity];
|
||||
|
||||
// Array equivalent to the above but in the string representation of the units
|
||||
const units: Intl.RelativeTimeFormatUnit[] = [
|
||||
"second",
|
||||
"minute",
|
||||
"hour",
|
||||
"day",
|
||||
"week",
|
||||
"month",
|
||||
"year",
|
||||
];
|
||||
|
||||
// Grab the ideal cutoff unit
|
||||
const unitIndex = cutoffs.findIndex(cutoff => cutoff > Math.abs(deltaSeconds));
|
||||
|
||||
// Get the divisor to divide from the seconds. E.g. if our unit is "day" our divisor
|
||||
// is one day in seconds, so we can divide our seconds by this to get the # of days
|
||||
const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1;
|
||||
|
||||
// Intl.RelativeTimeFormat do its magic
|
||||
const rtf = new Intl.RelativeTimeFormat(lang, { numeric: "auto" });
|
||||
return rtf.format(Math.floor(deltaSeconds / divisor), units[unitIndex]);
|
||||
}
|
||||
|
||||
export default function KvmCard({
|
||||
title,
|
||||
id,
|
||||
online,
|
||||
lastSeen,
|
||||
}: {
|
||||
title: string;
|
||||
id: string;
|
||||
online: boolean;
|
||||
lastSeen: Date | null;
|
||||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<div className="px-5 py-5 space-y-3">
|
||||
<div className="flex justify-between items-cente">
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-lg font-bold leading-none text-black dark:text-white">
|
||||
{title}
|
||||
</div>
|
||||
|
||||
{online ? (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
<div className="h-2.5 w-2.5 rounded-full border border-green-600 bg-green-500" />
|
||||
<div className="text-sm text-black dark:text-white">Online</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
<div className="h-2.5 w-2.5 rounded-full border border-slate-400/60 dark:border-slate-500 bg-slate-200 dark:bg-slate-600" />
|
||||
<div className="text-sm text-black dark:text-white">
|
||||
{lastSeen ? (
|
||||
<>Last online {getRelativeTimeString(lastSeen)}</>
|
||||
) : (
|
||||
<>Never seen online</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[1px] bg-slate-800/20 dark:bg-slate-300/20" />
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
{online ? (
|
||||
<LinkButton
|
||||
size="MD"
|
||||
theme="light"
|
||||
text="Connect to KVM"
|
||||
LeadingIcon={MdConnectWithoutContact}
|
||||
textAlign="center"
|
||||
to={`/devices/${id}`}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
size="MD"
|
||||
theme="light"
|
||||
text="Troubleshoot Connection"
|
||||
textAlign="center"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<div>
|
||||
<MenuButton
|
||||
as={Button}
|
||||
theme="light"
|
||||
TrailingIcon={LuEllipsisVertical}
|
||||
size="MD"
|
||||
></MenuButton>
|
||||
</div>
|
||||
|
||||
<MenuItems
|
||||
transition
|
||||
className="data-[closed]:scale-95 data-[closed]:transform data-[closed]:opacity-0 data-[enter]:duration-100 data-[leave]:duration-75 data-[enter]:ease-out data-[leave]:ease-in"
|
||||
>
|
||||
<Card className="absolute right-0 z-10 w-56 px-1 mt-2 transition origin-top-right ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
<div className="divide-y divide-slate-800/20 dark:divide-slate-300/20">
|
||||
<MenuItem>
|
||||
<div>
|
||||
<div className="block w-full">
|
||||
<div className="flex items-center px-2 my-1 text-sm transition-colors rounded-md gap-x-2 hover:bg-slate-100 dark:hover:bg-slate-700">
|
||||
<Link
|
||||
className="block w-full py-1.5 text-black dark:text-white"
|
||||
to={`./${id}/rename`}
|
||||
>
|
||||
Rename
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<div>
|
||||
<div className="block w-full">
|
||||
<div className="flex items-center px-2 my-1 text-sm transition-colors rounded-md gap-x-2 hover:bg-slate-100 dark:hover:bg-slate-700">
|
||||
<Link
|
||||
className="block w-full py-1.5 text-black dark:text-white"
|
||||
to={`./${id}/deregister`}
|
||||
>
|
||||
Deregister from cloud
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</Card>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user