diff --git a/dashboard/public/locales/en.json b/dashboard/public/locales/en.json
@@ -19,6 +19,9 @@
"config": "Config",
"tag": "Tag",
"protocol": "Protocol",
+ "online": "Online",
+ "offline": "Offline",
+ "not_connected": "Not Connected",
"apply": "Apply",
"open": "Open",
"delete": "Delete",
diff --git a/dashboard/src/features/users/components/index.ts b/dashboard/src/features/users/components/index.ts
@@ -1,3 +1,4 @@
export * from './users-status'
export * from './services-card'
export * from './date-field'
+export * from './online-status'
diff --git a/dashboard/src/features/users/components/online-status.tsx b/dashboard/src/features/users/components/online-status.tsx
@@ -0,0 +1,37 @@
+import { FC } from "react"
+import { useTranslation } from "react-i18next"
+import { cn, isUserOnline } from "@marzneshin/utils"
+import { UserType } from ".."
+import { Tooltip, TooltipContent, TooltipTrigger } from "@marzneshin/components"
+import { Circle } from "lucide-react"
+
+interface OnlineStatusProps {
+ user: UserType
+}
+
+export const OnlineStatus: FC<OnlineStatusProps> = ({ user }) => {
+ let statusColor
+ let tooltip
+ const status = isUserOnline(user)
+ const { t } = useTranslation()
+ if (user.online_at === null) {
+ statusColor = "text-yellow-500"
+ tooltip = t('not_connected')
+ } else if (status) {
+ statusColor = "text-green-500"
+ tooltip = t('online')
+ } else {
+ statusColor = "text-red-500"
+ tooltip = t('offline')
+ }
+ return (
+ <Tooltip>
+ <TooltipTrigger>
+ <Circle className={cn("w-5 h-5", statusColor)} />
+ </TooltipTrigger>
+ <TooltipContent>
+ {tooltip}
+ </TooltipContent>
+ </Tooltip>
+ )
+}
diff --git a/dashboard/src/features/users/tables/users/columns.tsx b/dashboard/src/features/users/tables/users/columns.tsx
@@ -1,24 +1,31 @@
import { ColumnDef } from "@tanstack/react-table"
-import { UsersStatusBadge, UserType, UsersStatus } from "@marzneshin/features/users"
+import { UsersStatusBadge, UserType, UsersStatus, OnlineStatus } from "@marzneshin/features/users"
import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header"
import i18n from "@marzneshin/features/i18n"
-import { CopyToClipboardButton, DataTableActionsCell, buttonVariants } from "@marzneshin/components"
+import {
+ CopyToClipboardButton,
+ DataTableActionsCell,
+ buttonVariants
+} from "@marzneshin/components"
import { CircularProgress } from "@nextui-org/progress"
import { LinkIcon } from "lucide-react"
import { getSubscriptionLink } from "@marzneshin/utils"
+
interface ColumnAction {
onDelete: (user: UserType) => void;
onOpen: (user: UserType) => void;
onEdit: (user: UserType) => void;
}
-
export const columns = (actions: ColumnAction): ColumnDef<UserType>[] => ([
{
accessorKey: "username",
header: ({ column }) => <DataTableColumnHeader title={i18n.t('username')} column={column} />,
+ cell: ({ row }) => (<div className="flex flex-row gap-2 items-center">
+ <OnlineStatus user={row.original} /> {row.original.username}
+ </div>)
},
{
accessorKey: "status",
diff --git a/dashboard/src/utils/index.ts b/dashboard/src/utils/index.ts
@@ -3,3 +3,4 @@ export * from './fetch'
export * from './cn'
export * from './query-client'
export * from './subscription-link'
+export * from './is-online'
diff --git a/dashboard/src/utils/is-online.ts b/dashboard/src/utils/is-online.ts
@@ -0,0 +1,17 @@
+import { UserType } from "@marzneshin/features/users";
+
+/**
+ * Checks whether the user has been online recently.
+ *
+ * This function compares the provided user's last online time with the current time
+ * to determine if the user has been online within a certain threshold.
+ *
+ * @param user - The user entity containing information about the user.
+ * @returns A boolean value indicating whether the user has been online recently.
+ */
+export const isUserOnline = (user: UserType): boolean => {
+ const utcUserOnlineTime = new Date(user.online_at + 'Z');
+ const currentTime = new Date();
+ const timeDifference = Math.abs(currentTime.getTime() - utcUserOnlineTime.getTime());
+ return timeDifference < 30000;
+};