gitscope
← marzneshin / Diff · 56b916e → 56b916e
Changes 56b916e..56b916e
Patch commit
diff --git a/dashboard/public/locales/en.json b/dashboard/public/locales/en.json
index d347a2d..9f560be 100644
--- a/dashboard/public/locales/en.json
+++ b/dashboard/public/locales/en.json
@@ -121,6 +121,10 @@
},
"nodes": {
"settings": {
+ "no-backend-alert": {
+ "title": "No backend is configured for this node",
+ "desc": "Please take required measures and configure a backend for this node."
+ },
"title": "Nodes Settings",
"detail": "Details",
"log-container": {
diff --git a/dashboard/public/locales/fa.json b/dashboard/public/locales/fa.json
index 22b2ef6..c63b5b1 100644
--- a/dashboard/public/locales/fa.json
+++ b/dashboard/public/locales/fa.json
@@ -117,6 +117,10 @@
},
"nodes": {
"settings": {
+ "no-backend-alert": {
+ "title": "هیچ بکندی برای این نود تنظیم نشده است.",
+ "desc": "لطفا اقدامات لازم جهت افزودن بکندی به این نود را انجام دهید."
+ },
"title": "تنظیمات نود",
"detail": "جزئیات",
"log-container": {
diff --git a/dashboard/src/components/alert-card/index.tsx b/dashboard/src/components/alert-card/index.tsx
new file mode 100644
index 0000000..838a604
--- /dev/null
+++ b/dashboard/src/components/alert-card/index.tsx
@@ -0,0 +1,20 @@
+import { Info, TriangleAlert, OctagonAlert } from 'lucide-react';
+import {
+ Alert,
+ AlertDescription,
+ AlertTitle,
+} from '@marzneshin/components';
+
+export const AlertCard = ({ title, desc, variant = "default" }: { variant?: "warning" | "default" | "destructive", title: string | JSX.Element, desc: string | JSX.Element }) => {
+ const Icon = variant === "warning" ? TriangleAlert : (variant === "destructive" ? OctagonAlert : Info)
+ return (
+ <Alert variant={variant}>
+ <Icon className="mr-2" />
+ <AlertTitle className="font-semibold">{title}</AlertTitle>
+ <AlertDescription>
+ {desc}
+ </AlertDescription>
+ </Alert>
+ )
+}
+
diff --git a/dashboard/src/components/index.ts b/dashboard/src/components/index.ts
index 147cac2..168a40b 100644
--- a/dashboard/src/components/index.ts
+++ b/dashboard/src/components/index.ts
@@ -20,3 +20,4 @@ export * from "./info-table";
export * from "./skeletons";
export * from "./no-propogation-button";
export * from "./form-fields";
+export * from "./alert-card";
diff --git a/dashboard/src/components/ui/alert.tsx b/dashboard/src/components/ui/alert.tsx
index 1cda26e..9f22b3a 100644
--- a/dashboard/src/components/ui/alert.tsx
+++ b/dashboard/src/components/ui/alert.tsx
@@ -4,55 +4,57 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@marzneshin/utils"
const alertVariants = cva(
- "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
- {
- variants: {
- variant: {
- default: "bg-background text-foreground",
- destructive:
- "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- }
+ "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
+ {
+ variants: {
+ variant: {
+ default: "bg-background text-foreground",
+ warning:
+ "border-yellow/50 text-yellow dark:border-yellow [&>svg]:text-yellow",
+ destructive:
+ "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
)
const Alert = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
+ HTMLDivElement,
+ React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
- <div
- ref={ref}
- role="alert"
- className={cn(alertVariants({ variant }), className)}
- {...props}
- />
+ <div
+ ref={ref}
+ role="alert"
+ className={cn(alertVariants({ variant }), className)}
+ {...props}
+ />
))
Alert.displayName = "Alert"
const AlertTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLHeadingElement>
+ HTMLParagraphElement,
+ React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
- <h5
- ref={ref}
- className={cn("mb-1 font-medium leading-none tracking-tight", className)}
- {...props}
- />
+ <h5
+ ref={ref}
+ className={cn("mb-1 font-medium leading-none tracking-tight", className)}
+ {...props}
+ />
))
AlertTitle.displayName = "AlertTitle"
const AlertDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLParagraphElement>
+ HTMLParagraphElement,
+ React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn("text-sm [&_p]:leading-relaxed", className)}
- {...props}
- />
+ <div
+ ref={ref}
+ className={cn("text-sm [&_p]:leading-relaxed", className)}
+ {...props}
+ />
))
AlertDescription.displayName = "AlertDescription"
diff --git a/dashboard/src/features/nodes/dialogs/settings/index.tsx b/dashboard/src/features/nodes/dialogs/settings/index.tsx
index 5ceaefd..16424b6 100644
--- a/dashboard/src/features/nodes/dialogs/settings/index.tsx
+++ b/dashboard/src/features/nodes/dialogs/settings/index.tsx
@@ -1,10 +1,11 @@
import {
- type SettingsDialogProps,
- SettingsDialog,
- Tabs,
- TabsContent,
- TabsList,
- TabsTrigger,
+ type SettingsDialogProps,
+ SettingsDialog,
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+ AlertCard,
} from "@marzneshin/components";
import type { FC } from "react";
import { useTranslation } from "react-i18next";
@@ -13,51 +14,59 @@ import { NodesDetailTable } from "../../tables/detail-table";
import { NodeBackendSetting } from "./node-backend-setting";
interface NodesSettingsDialogProps extends SettingsDialogProps {
- entity: NodeType;
- onClose: () => void;
+ entity: NodeType;
+ onClose: () => void;
}
export const NodesSettingsDialog: FC<NodesSettingsDialogProps> = ({
- onOpenChange,
- open,
- entity,
- onClose,
+ onOpenChange,
+ open,
+ entity,
+ onClose,
}) => {
- const { t } = useTranslation();
+ const { t } = useTranslation();
- return (
- <SettingsDialog open={open} onClose={onClose} onOpenChange={onOpenChange}>
- <div className="my-4">
- <h1 className="font-medium font-header">
- {t("page.nodes.settings.detail")}
- </h1>
- <NodesDetailTable node={entity} />
- </div>
- <Tabs
- className="my-3 w-full h-full"
- defaultValue={entity.backends[0].name}
- >
- <TabsList className="w-full">
- {...entity.backends.map((backend: NodeBackendType) => (
- <TabsTrigger
- className="capitalize w-full"
- value={backend.name}
- key={backend.name}
- >
- {backend.name}
- </TabsTrigger>
- ))}
- </TabsList>
- {...entity.backends.map((backend: NodeBackendType) => (
- <TabsContent
- className="w-full"
- value={backend.name}
- key={backend.name}
- >
- <NodeBackendSetting node={entity} backend={backend.name} />
- </TabsContent>
- ))}
- </Tabs>
- </SettingsDialog>
- );
+ return (
+ <SettingsDialog open={open} onClose={onClose} onOpenChange={onOpenChange}>
+ <div className="my-4">
+ <h1 className="font-medium font-header">
+ {t("page.nodes.settings.detail")}
+ </h1>
+ <NodesDetailTable node={entity} />
+ </div>
+ {entity.backends.length === 0 ? (
+ <AlertCard
+ variant="warning"
+ desc={t('page.nodes.settings.no-backend-alert.desc')}
+ title={t('page.nodes.settings.no-backend-alert.title')}
+ />
+ ) : (
+ <Tabs
+ className="my-3 w-full h-full"
+ defaultValue={entity.backends[0].name}
+ >
+ <TabsList className="w-full">
+ {...entity.backends.map((backend: NodeBackendType) => (
+ <TabsTrigger
+ className="capitalize w-full"
+ value={backend.name}
+ key={backend.name}
+ >
+ {backend.name}
+ </TabsTrigger>
+ ))}
+ </TabsList>
+ {...entity.backends.map((backend: NodeBackendType) => (
+ <TabsContent
+ className="w-full"
+ value={backend.name}
+ key={backend.name}
+ >
+ <NodeBackendSetting node={entity} backend={backend.name} />
+ </TabsContent>
+ ))}
+ </Tabs>
+ )}
+ </SettingsDialog>
+ );
};
diff --git a/dashboard/src/features/users/components/no-service-alert/index.tsx b/dashboard/src/features/users/components/no-service-alert/index.tsx
index 0f9f43e..2e00f91 100644
--- a/dashboard/src/features/users/components/no-service-alert/index.tsx
+++ b/dashboard/src/features/users/components/no-service-alert/index.tsx
@@ -1,25 +1,23 @@
import { useServicesQuery } from '@marzneshin/features/services';
-import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import { Link } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next';
import {
- Alert,
- AlertDescription,
- AlertTitle,
+ AlertCard,
} from '@marzneshin/components';
export const UsersNoServiceAlert = () => {
const { t } = useTranslation();
const { data } = useServicesQuery({ page: 1, size: 10 });
return (data && data.pageCount === 0) && (
- <Alert>
- <ExclamationTriangleIcon className="mr-2" />
- <AlertTitle className="font-semibold text-primary">{t('page.users.services-alert.title')}</AlertTitle>
- <AlertDescription>
- {t('page.users.services-alert.desc')}
- <Link className="m-1 font-semibold text-secondary-foreground" to="/services">{t('page.nodes.certificate-alert.click')}</Link>
- </AlertDescription>
- </Alert>
+ <AlertCard
+ title={t('page.users.services-alert.title')}
+ desc={
+ <>
+ {t('page.users.services-alert.desc')}
+ <Link className="m-1 font-semibold text-secondary-foreground" to="/services">{t('page.nodes.certificate-alert.click')}</Link>
+ </>
+ }
+ />
)
}
diff --git a/dashboard/src/routes/_dashboard/nodes.lazy.tsx b/dashboard/src/routes/_dashboard/nodes.lazy.tsx
index f0e8d88..12ede6a 100644
--- a/dashboard/src/routes/_dashboard/nodes.lazy.tsx
+++ b/dashboard/src/routes/_dashboard/nodes.lazy.tsx
@@ -1,7 +1,5 @@
import {
- Alert,
- AlertDescription,
- AlertTitle,
+ AlertCard,
Card,
CardContent,
CardFooter,
@@ -12,7 +10,6 @@ import {
} from '@marzneshin/components';
import { NodesTable } from '@marzneshin/features/nodes';
import { Link, createFileRoute, Outlet } from '@tanstack/react-router'
-import { Info } from 'lucide-react';
import { type FC, Suspense } from 'react';
import { useTranslation } from 'react-i18next';
import { SudoRoute } from "@marzneshin/features/sudo-routes";
@@ -34,14 +31,15 @@ export const NodesPage: FC = () => {
</Suspense>
</CardContent>
<CardFooter>
- <Alert>
- <Info className="mr-2" />
- <AlertTitle className="font-semibold text-primary">{t('page.nodes.certificate-alert.title')}</AlertTitle>
- <AlertDescription>
- {t('page.nodes.certificate-alert.desc')}
- <Link className="m-1 font-semibold text-secondary-foreground" to="/settings">{t('page.nodes.certificate-alert.click')}</Link>
- </AlertDescription>
- </Alert>
+ <AlertCard
+ title={t('page.nodes.certificate-alert.title')}
+ desc={
+ <>
+ {t('page.nodes.certificate-alert.desc')}
+ <Link className="m-1 font-semibold text-secondary-foreground" to="/settings">{t('page.nodes.certificate-alert.click')}</Link>
+ </>
+ }
+ />
</CardFooter>
</Card >
</Page>