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>
 | |
|   );
 | |
| }
 |