mirror of https://github.com/jetkvm/kvm.git
155 lines
5.6 KiB
TypeScript
155 lines
5.6 KiB
TypeScript
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";
|
|
|
|
import Card from "@components/Card";
|
|
import { Button, LinkButton } from "@components/Button";
|
|
|
|
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 representing 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-center">
|
|
<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>
|
|
);
|
|
}
|