gitscope
← marzneshin / Diff · fb9bc33 → fb9bc33
Changes fb9bc33..fb9bc33
Patch commit
diff --git a/.github/workflows/pr_validation.yml b/.github/workflows/pr_validation.yml
index f478d86..2d32c84 100644
--- a/.github/workflows/pr_validation.yml
+++ b/.github/workflows/pr_validation.yml
@@ -22,6 +22,7 @@ jobs:
ci
docs
feat
+ improve
fix
merge
perf
diff --git a/dashboard/src/components/copy-button/index.tsx b/dashboard/src/components/copy-button/index.tsx
index 460a5b9..72db482 100644
--- a/dashboard/src/components/copy-button/index.tsx
+++ b/dashboard/src/components/copy-button/index.tsx
@@ -1,8 +1,8 @@
import {
- Button,
- Tooltip,
- TooltipContent,
- TooltipTrigger,
+ Button,
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
} from "@marzneshin/components";
import CopyToClipboard from "react-copy-to-clipboard";
import { useEffect, useState } from "react";
@@ -12,70 +12,70 @@ import { toast } from "sonner";
import { cn } from "@marzneshin/utils";
interface CopyToClipboardButtonProps {
- text: string;
- disabled?: boolean;
- successMessage: string;
- copyIcon?: LucideIcon;
- copyLabel?: string;
- errorLabel?: string;
- className?: string;
- tooltipMsg?: string;
+ text: string;
+ disabled?: boolean;
+ successMessage: string;
+ copyIcon?: LucideIcon;
+ copyLabel?: string;
+ errorLabel?: string;
+ className?: string;
+ tooltipMsg?: string;
}
export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({
- text,
- disabled = false,
- successMessage,
- copyIcon,
- copyLabel,
- errorLabel,
- className,
- tooltipMsg,
+ text,
+ disabled = false,
+ successMessage,
+ copyIcon,
+ copyLabel,
+ errorLabel,
+ className,
+ tooltipMsg,
}) => {
- const { t } = useTranslation();
- const [copied, setCopied] = useState(false);
- const [healthy, setHealthy] = useState(false);
+ const { t } = useTranslation();
+ const [copied, setCopied] = useState(false);
+ const [healthy, setHealthy] = useState(false);
- useEffect(() => {
- if (copied) {
- toast.success(t(successMessage));
- setTimeout(() => {
- setCopied(false);
- }, 1000);
- }
- }, [copied, successMessage, t]);
+ useEffect(() => {
+ if (copied) {
+ toast.success(t(successMessage));
+ setTimeout(() => {
+ setCopied(false);
+ }, 1000);
+ }
+ }, [copied, successMessage, t]);
- useEffect(() => {
- setHealthy(text !== "");
- }, [text]);
+ useEffect(() => {
+ setHealthy(text !== "");
+ }, [text]);
- const handleClick = () => {
- if (!disabled) {
- setCopied(true);
- }
- };
+ const handleClick = () => {
+ if (!disabled) {
+ setCopied(true);
+ }
+ };
- const tooltip = tooltipMsg ? tooltipMsg : copyLabel ? copyLabel : undefined;
+ const tooltip = tooltipMsg ? tooltipMsg : copyLabel ? copyLabel : undefined;
- const Icon = copied ? CopyCheck : healthy ? copyIcon || ClipboardCopy : CopyX;
- const isWithLabel = successMessage && copyLabel && errorLabel;
+ const Icon = copied ? CopyCheck : healthy ? copyIcon || ClipboardCopy : CopyX;
+ const isWithLabel = successMessage && copyLabel && errorLabel;
- return (
- <Tooltip>
- <TooltipTrigger>
- <CopyToClipboard text={text}>
- <Button
- className={cn(className, "p-2 flex flex-row items-center gap-2")}
- disabled={disabled || !healthy}
- onClick={handleClick}
- >
- <Icon />
- {isWithLabel &&
- t(copied ? successMessage : healthy ? copyLabel : errorLabel)}
- </Button>
- </CopyToClipboard>
- </TooltipTrigger>
- {tooltip && <TooltipContent>{tooltip}</TooltipContent>}
- </Tooltip>
- );
+ return (
+ <Tooltip>
+ <CopyToClipboard text={text}>
+ <TooltipTrigger asChild>
+ <Button
+ className={cn(className, "p-2 flex flex-row items-center gap-2")}
+ disabled={disabled || !healthy}
+ onClick={handleClick}
+ >
+ <Icon />
+ {isWithLabel &&
+ t(copied ? successMessage : healthy ? copyLabel : errorLabel)}
+ </Button>
+ </TooltipTrigger>
+ </CopyToClipboard>
+ {tooltip && <TooltipContent>{tooltip}</TooltipContent>}
+ </Tooltip>
+ );
};
diff --git a/dashboard/src/features/users/components/dialogs/settings/index.tsx b/dashboard/src/features/users/components/dialogs/settings/index.tsx
index b3c0b6d..86bbe51 100644
--- a/dashboard/src/features/users/components/dialogs/settings/index.tsx
+++ b/dashboard/src/features/users/components/dialogs/settings/index.tsx
@@ -1,4 +1,5 @@
import {
+ Button,
ScrollArea,
Sheet,
SheetContent,
@@ -10,11 +11,15 @@ import {
TabsTrigger,
} from "@marzneshin/components";
import { UserServicesTable } from "@marzneshin/features/users";
-import type { UserType } from "@marzneshin/features/users";
+import {
+ useUserUsageResetCmd,
+ type UserType
+} from "@marzneshin/features/users";
import { type FC, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { QRCodeSection, UserInfoTable } from "./user-info";
import { SubscriptionActions } from "./user-info/subscription-actions";
+import { UserStatusEnableButton } from "./user-info/user-status-enable-button";
interface UsersSettingsDialogProps {
onOpenChange: (state: boolean) => void;
@@ -30,6 +35,7 @@ export const UsersSettingsDialog: FC<UsersSettingsDialogProps> = ({
onClose,
}) => {
const { t } = useTranslation();
+ const { mutate: resetUsage } = useUserUsageResetCmd();
useEffect(() => {
if (!open) onClose();
@@ -45,12 +51,13 @@ export const UsersSettingsDialog: FC<UsersSettingsDialogProps> = ({
<Tabs defaultValue="info" className="w-full h-full">
<TabsList className="w-full bg-accent">
<TabsTrigger className="w-full" value="info">
- {" "}
- {t("user_info")}{" "}
+ {t("user_info")}
</TabsTrigger>
<TabsTrigger className="w-full" value="services">
- {" "}
- {t("services")}{" "}
+ {t("services")}
+ </TabsTrigger>
+ <TabsTrigger className="w-full" value="subscription">
+ {t("subscription")}
</TabsTrigger>
</TabsList>
<TabsContent
@@ -58,12 +65,20 @@ export const UsersSettingsDialog: FC<UsersSettingsDialogProps> = ({
className="flex flex-col gap-2 w-full h-full"
>
<UserInfoTable entity={entity} />
- <QRCodeSection entity={entity} />
- <SubscriptionActions entity={entity} />
+ <div className="flex flex-row justify-center items-center gap-2">
+ <Button className="w-1/2" onClick={() => resetUsage(entity)}>
+ {t("page.users.reset_usage")}
+ </Button>
+ <UserStatusEnableButton user={entity} />
+ </div>
</TabsContent>
<TabsContent value="services">
<UserServicesTable user={entity} />
</TabsContent>
+ <TabsContent value="subscription">
+ <QRCodeSection entity={entity} />
+ <SubscriptionActions entity={entity} />
+ </TabsContent>
</Tabs>
</ScrollArea>
</SheetContent>
diff --git a/dashboard/src/features/users/components/dialogs/settings/user-info/subscription-actions.tsx b/dashboard/src/features/users/components/dialogs/settings/user-info/subscription-actions.tsx
index 351a34a..797c09f 100644
--- a/dashboard/src/features/users/components/dialogs/settings/user-info/subscription-actions.tsx
+++ b/dashboard/src/features/users/components/dialogs/settings/user-info/subscription-actions.tsx
@@ -2,20 +2,28 @@ import { Button, HStack } from "@marzneshin/components";
import { useTranslation } from "react-i18next";
import { getSubscriptionLink } from "@marzneshin/utils";
import { useUserSubscriptionRevokeCmd } from "@marzneshin/features/users";
+import { CopyToClipboardButton } from "@marzneshin/components";
+import { SquareCode } from "lucide-react";
import type { QRCodeProps } from "./qrcode";
import type { FC } from 'react';
-import { SubscriptionLinkButton } from "./subscription-link-button";
export const SubscriptionActions: FC<QRCodeProps> = ({ entity }) => {
- const subscribeQrLink = getSubscriptionLink(entity.subscription_url);
- const { mutate: revokeSubscription } = useUserSubscriptionRevokeCmd();
- const { t } = useTranslation();
- return (
- <HStack className="w-full items-center">
- <SubscriptionLinkButton subscriptionLink={subscribeQrLink} />
- <Button className="w-full" onClick={() => revokeSubscription(entity)}>
- {t("page.users.revoke_subscription")}
- </Button>
- </HStack>
- );
+ const subscribeQrLink = getSubscriptionLink(entity.subscription_url);
+ const { mutate: revokeSubscription } = useUserSubscriptionRevokeCmd();
+ const { t } = useTranslation();
+ return (
+ <HStack className="w-full items-center my-2">
+ <CopyToClipboardButton
+ text={subscribeQrLink}
+ successMessage={t("page.users.settings.subscription_link.copied")}
+ copyLabel={t("page.users.settings.subscription_link.copy")}
+ errorLabel={t("page.users.settings.subscription_link.error")}
+ copyIcon={SquareCode}
+ className="w-1/2"
+ />
+ <Button variant="destructive" className="w-1/2" onClick={() => revokeSubscription(entity)}>
+ {t("page.users.revoke_subscription")}
+ </Button>
+ </HStack>
+ );
};
diff --git a/dashboard/src/features/users/components/dialogs/settings/user-info/subscription-link-button.tsx b/dashboard/src/features/users/components/dialogs/settings/user-info/subscription-link-button.tsx
deleted file mode 100644
index 8b69cd8..0000000
--- a/dashboard/src/features/users/components/dialogs/settings/user-info/subscription-link-button.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-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;
-}
-
-export const SubscriptionLinkButton: FC<SubscriptionLinkButtonProps> = ({
- subscriptionLink,
-}) => {
- const { t } = useTranslation();
-
- return (
- <CopyToClipboardButton
- text={getSubscriptionLink(subscriptionLink)}
- successMessage={t("page.users.settings.subscription_link.copied")}
- copyLabel={t("page.users.settings.subscription_link.copy")}
- errorLabel={t("page.users.settings.subscription_link.error")}
- copyIcon={SquareCode}
- className="w-full"
- />
- );
-};
diff --git a/dashboard/src/features/users/components/dialogs/settings/user-info/table.tsx b/dashboard/src/features/users/components/dialogs/settings/user-info/table.tsx
index d3138d4..6da46a3 100644
--- a/dashboard/src/features/users/components/dialogs/settings/user-info/table.tsx
+++ b/dashboard/src/features/users/components/dialogs/settings/user-info/table.tsx
@@ -1,5 +1,4 @@
import {
- Button,
Card,
CardContent,
CardHeader,
@@ -11,7 +10,6 @@ import {
} from "@marzneshin/components";
import { FC } from "react";
import {
- useUserUsageResetCmd,
UserType,
UsersStatus,
UsersStatusBadge,
@@ -19,7 +17,6 @@ import {
import { CircularProgress } from "@nextui-org/progress";
import { useTranslation } from "react-i18next";
import { format, isDate, isValid } from "date-fns";
-import { UserStatusEnableButton } from "./user-status-enable-button";
interface UserInfoTableProps {
entity: UserType;
@@ -61,7 +58,6 @@ const CircularProgressBarRow: FC<{
export const UserInfoTable: FC<UserInfoTableProps> = ({ entity }) => {
const { t } = useTranslation();
- const { mutate: resetUsage } = useUserUsageResetCmd();
const expireDate = entity.expire
? isDate(entity.expire)
? entity.expire
@@ -114,12 +110,6 @@ export const UserInfoTable: FC<UserInfoTableProps> = ({ entity }) => {
<TableRowWithCell label={t("note")} value={entity.note} />
</TableBody>
</Table>
- <div className="flex flex-row justify-center items-center gap-2">
- <Button className="w-full" onClick={() => resetUsage(entity)}>
- {t("page.users.reset_usage")}
- </Button>
- <UserStatusEnableButton user={entity} />
- </div>
</CardContent>
</Card>
);
diff --git a/dashboard/src/features/users/components/dialogs/settings/user-info/user-status-enable-button.tsx b/dashboard/src/features/users/components/dialogs/settings/user-info/user-status-enable-button.tsx
index 0476a3e..189f043 100644
--- a/dashboard/src/features/users/components/dialogs/settings/user-info/user-status-enable-button.tsx
+++ b/dashboard/src/features/users/components/dialogs/settings/user-info/user-status-enable-button.tsx
@@ -1,4 +1,5 @@
import { Button } from "@marzneshin/components";
+import { cn } from "@marzneshin/utils";
import { UserType, useUserStatusEnable } from "@marzneshin/features/users";
import { LoaderIcon } from "lucide-react";
import { FC, useCallback, useState } from "react";
@@ -23,7 +24,7 @@ export const UserStatusEnableButton: FC<UserStatusEnableButtonProps> = ({ user }
return (
<Button
- className={bgColor}
+ className={cn(bgColor, "w-1/2")}
onClick={handleUserStatusEnabledToggle}
>
{isPending ? <LoaderIcon className="animate-spin" /> : t(!userStatus ? 'enable' : 'disable')}