diff --git a/dashboard/public/locales/en.json b/dashboard/public/locales/en.json index 17c5207..0dd14c7 100644 --- a/dashboard/public/locales/en.json +++ b/dashboard/public/locales/en.json @@ -133,7 +133,7 @@ "settings": { "subscription_link": { "copied": "Subscription Link Copied", - "copy": "Copy Subscription Link", + "copy": "Subscription Link", "error": "Unable to copy Subscription Link" } }, diff --git a/dashboard/src/components/copy-button/index.tsx b/dashboard/src/components/copy-button/index.tsx index 9b75626..a26fb2a 100644 --- a/dashboard/src/components/copy-button/index.tsx +++ b/dashboard/src/components/copy-button/index.tsx @@ -1,10 +1,15 @@ -import { Button, Tooltip, TooltipContent, TooltipTrigger } from '@marzneshin/components'; -import CopyToClipboard from 'react-copy-to-clipboard'; -import { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { CopyCheck, CopyX, ClipboardCopy, LucideIcon } from 'lucide-react'; -import { toast } from 'sonner'; -import { cn } from '@marzneshin/utils'; +import { + Button, + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@marzneshin/components"; +import CopyToClipboard from "react-copy-to-clipboard"; +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { CopyCheck, CopyX, ClipboardCopy, LucideIcon } from "lucide-react"; +import { toast } from "sonner"; +import { cn } from "@marzneshin/utils"; interface CopyToClipboardButtonProps { text: string; @@ -23,8 +28,9 @@ export const CopyToClipboardButton: React.FC = ({ successMessage, copyIcon, copyLabel, - errorLabel, className, - tooltipMsg + errorLabel, + className, + tooltipMsg, }) => { const { t } = useTranslation(); const [copied, setCopied] = useState(false); @@ -40,7 +46,7 @@ export const CopyToClipboardButton: React.FC = ({ }, [copied, successMessage, t]); useEffect(() => { - setHealthy(text !== ''); + setHealthy(text !== ""); }, [text]); const handleClick = () => { @@ -49,26 +55,35 @@ export const CopyToClipboardButton: React.FC = ({ } }; - const tooltip = tooltipMsg ? tooltipMsg : (copyLabel ? copyLabel : undefined) + const tooltip = tooltipMsg ? tooltipMsg : copyLabel ? copyLabel : undefined; - const Icon = copied ? CopyCheck : (healthy ? (copyIcon ? copyIcon : ClipboardCopy) : CopyX) - const isWithLabel = successMessage && copyLabel && errorLabel + const Icon = copied + ? CopyCheck + : healthy + ? copyIcon + ? copyIcon + : ClipboardCopy + : CopyX; + const isWithLabel = successMessage && copyLabel && errorLabel; return ( - + - - {tooltip && - - {tooltip} - - } - + {tooltip && {tooltip}} + ); }; diff --git a/dashboard/src/components/stacks/horizontal.tsx b/dashboard/src/components/stacks/horizontal.tsx index f571ee5..fa7f8bc 100644 --- a/dashboard/src/components/stacks/horizontal.tsx +++ b/dashboard/src/components/stacks/horizontal.tsx @@ -1,5 +1,9 @@ -import { FC, PropsWithChildren } from "react"; +import type { FC, PropsWithChildren, HTMLAttributes } from "react"; +import { cn } from "@marzneshin/utils"; -export const HStack: FC = ({ children }) => { - return
{children}
; +export const HStack: FC> = ({ + children, + className, +}) => { + return
{children}
; }; diff --git a/dashboard/src/features/users/dialogs/settings/index.tsx b/dashboard/src/features/users/dialogs/settings/index.tsx index 9603bda..d7d861b 100644 --- a/dashboard/src/features/users/dialogs/settings/index.tsx +++ b/dashboard/src/features/users/dialogs/settings/index.tsx @@ -8,39 +8,53 @@ import { TabsContent, TabsList, TabsTrigger, -} from "@marzneshin/components" -import { FC } from "react" -import { useTranslation } from "react-i18next" -import { UserServicesTable, UserType } from "@marzneshin/features/users" -import { UserInfoTable, QRCodeSection } from "./user-info" +} from "@marzneshin/components"; +import { UserServicesTable } from "@marzneshin/features/users"; +import type { UserType } from "@marzneshin/features/users"; +import type { FC } from "react"; +import { useTranslation } from "react-i18next"; +import { QRCodeSection, UserInfoTable } from "./user-info"; +import { SubscriptionActions } from "./user-info/subscription-actions"; interface UsersSettingsDialogProps { - onOpenChange: (state: boolean) => void - open: boolean - entity: UserType | null + onOpenChange: (state: boolean) => void; + open: boolean; + entity: UserType | null; } -export const UsersSettingsDialog: FC = ({ onOpenChange, open, entity }) => { +export const UsersSettingsDialog: FC = ({ + onOpenChange, + open, + entity, +}) => { const { t } = useTranslation(); if (entity) { return ( - - - - {t('settings')} - - + + + {t("settings")} + - {t('user_info')} - {t('services')} + + {" "} + {t("user_info")}{" "} + + + {" "} + {t("services")}{" "} + - + + @@ -49,6 +63,6 @@ export const UsersSettingsDialog: FC = ({ onOpenChange - ) + ); } -} +}; diff --git a/dashboard/src/features/users/dialogs/settings/user-info/qrcode.tsx b/dashboard/src/features/users/dialogs/settings/user-info/qrcode.tsx index f4e4e07..328fa46 100644 --- a/dashboard/src/features/users/dialogs/settings/user-info/qrcode.tsx +++ b/dashboard/src/features/users/dialogs/settings/user-info/qrcode.tsx @@ -1,53 +1,34 @@ - -import { - useUserSubscriptionRevokeCmd, - UserType -} from '@marzneshin/features/users'; +import type { UserType } from "@marzneshin/features/users"; import QRCode from "react-qr-code"; import { QrCodeIcon } from "lucide-react"; -import { FC } from 'react' +import type { FC } from "react"; import { - Button, Card, CardContent, CardHeader, - CardTitle -} from '@marzneshin/components'; -import { useTranslation } from 'react-i18next'; -import { SubscriptionLinkButton } from './subcription-link-button'; -import { getSubscriptionLink } from '@marzneshin/utils'; + CardTitle, +} from "@marzneshin/components"; +import { useTranslation } from "react-i18next"; +import { getSubscriptionLink } from "@marzneshin/utils"; -interface QRCodeProps { - entity: UserType +export interface QRCodeProps { + entity: UserType; } -export const QRCodeSection: FC = ( - { entity } -) => { +export const QRCodeSection: FC = ({ entity }) => { const subscribeQrLink = getSubscriptionLink(entity.subscription_url); - const { mutate: revokeSubscription } = useUserSubscriptionRevokeCmd() - const { t } = useTranslation() + const { t } = useTranslation(); return ( - {t('subscription')} + {t("subscription")} QRCode - - -
- - -
+ +
); -} +}; diff --git a/dashboard/src/features/users/dialogs/settings/user-info/subcription-link-button.tsx b/dashboard/src/features/users/dialogs/settings/user-info/subcription-link-button.tsx index 004369f..8b69cd8 100644 --- a/dashboard/src/features/users/dialogs/settings/user-info/subcription-link-button.tsx +++ b/dashboard/src/features/users/dialogs/settings/user-info/subcription-link-button.tsx @@ -1,24 +1,26 @@ -import { useTranslation } from 'react-i18next'; -import { CopyToClipboardButton } from '@marzneshin/components'; -import { FC } from 'react'; -import { SquareCode } from 'lucide-react'; -import { getSubscriptionLink } from '@marzneshin/utils'; +import { useTranslation } from "react-i18next"; +import { CopyToClipboardButton } from "@marzneshin/components"; +import type { FC } from "react"; +import { SquareCode } from "lucide-react"; +import { getSubscriptionLink } from "@marzneshin/utils"; interface SubscriptionLinkButtonProps { - subscriptionLink: string; + subscriptionLink: string; } -export const SubscriptionLinkButton: FC = ({ subscriptionLink }) => { - const { t } = useTranslation(); +export const SubscriptionLinkButton: FC = ({ + subscriptionLink, +}) => { + const { t } = useTranslation(); - return ( - - ); + return ( + + ); }; diff --git a/dashboard/src/features/users/dialogs/settings/user-info/subscription-actions.tsx b/dashboard/src/features/users/dialogs/settings/user-info/subscription-actions.tsx new file mode 100644 index 0000000..b79e723 --- /dev/null +++ b/dashboard/src/features/users/dialogs/settings/user-info/subscription-actions.tsx @@ -0,0 +1,21 @@ +import { Button, HStack } from "@marzneshin/components"; +import { useTranslation } from "react-i18next"; +import { getSubscriptionLink } from "@marzneshin/utils"; +import { useUserSubscriptionRevokeCmd } from "@marzneshin/features/users"; +import type { QRCodeProps } from "./qrcode"; +import type { FC } from 'react'; + +import { SubscriptionLinkButton } from "./subcription-link-button"; +export const SubscriptionActions: FC = ({ entity }) => { + const subscribeQrLink = getSubscriptionLink(entity.subscription_url); + const { mutate: revokeSubscription } = useUserSubscriptionRevokeCmd(); + const { t } = useTranslation(); + return ( + + + + + ); +}; diff --git a/dashboard/src/features/users/dialogs/settings/user-info/table.tsx b/dashboard/src/features/users/dialogs/settings/user-info/table.tsx index d4de22e..d3138d4 100644 --- a/dashboard/src/features/users/dialogs/settings/user-info/table.tsx +++ b/dashboard/src/features/users/dialogs/settings/user-info/table.tsx @@ -7,15 +7,15 @@ import { Table, TableBody, TableCell, - TableRow + TableRow, } from "@marzneshin/components"; -import { FC } from 'react'; +import { FC } from "react"; import { useUserUsageResetCmd, UserType, UsersStatus, UsersStatusBadge, -} from '@marzneshin/features/users'; +} from "@marzneshin/features/users"; import { CircularProgress } from "@nextui-org/progress"; import { useTranslation } from "react-i18next"; import { format, isDate, isValid } from "date-fns"; @@ -25,68 +25,101 @@ interface UserInfoTableProps { entity: UserType; } -const TableRowWithCell: FC<{ label: string; value: string | number | JSX.Element }> = ({ label, value }) => ( +const TableRowWithCell: FC<{ + label: string; + value: string | number | JSX.Element; +}> = ({ label, value }) => ( {label} {value} ); -const DateTableRow: FC<{ label: string; date: Date | string }> = ({ label, date }) => { - let formattedDate = ''; +const DateTableRow: FC<{ label: string; date: Date | string }> = ({ + label, + date, +}) => { + let formattedDate = ""; if (date && isValid(date)) { formattedDate = format(date, "PPP"); } - return ( - - ); + return ; }; -const CircularProgressBarRow: FC<{ label: string; value: number; limit: number }> = ({ label, value, limit }) => ( +const CircularProgressBarRow: FC<{ + label: string; + value: number; + limit: number; +}> = ({ label, value, limit }) => ( {label} - + + + ); export const UserInfoTable: FC = ({ entity }) => { const { t } = useTranslation(); - const { mutate: resetUsage } = useUserUsageResetCmd() - const expireDate = entity.expire ? (isDate(entity.expire) ? entity.expire : new Date(entity.expire)) : null; + const { mutate: resetUsage } = useUserUsageResetCmd(); + const expireDate = entity.expire + ? isDate(entity.expire) + ? entity.expire + : new Date(entity.expire) + : null; return ( - {t('user_info')} -
- - -
+ {t("user_info")}
- - } /> - {(expireDate && entity.on_hold_expire_duration === 0) ? ( - + + } + /> + {expireDate && entity.on_hold_expire_duration === 0 ? ( + ) : ( <> - - {entity.on_hold_timeout && - - } + + {entity.on_hold_timeout && ( + + )} )} - {entity.data_limit && - - } - - + {entity.data_limit && ( + + )} + +
+
+ + +
);