Add dark mod

This commit is contained in:
yoan 2024-08-31 13:16:03 +08:00
parent 7479c25e7a
commit 7d3e94247e
19 changed files with 260 additions and 75 deletions

1
ui/dist/assets/index-DK_EegWM.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

26
ui/dist/imgs/providers/ssh.svg vendored Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 73 73" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" fill="#2562eb">
<g id="SVGRepo_bgCarrier" stroke-width="0" />
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
<g id="SVGRepo_iconCarrier">
<title>databases-and-servers/servers/ssh</title>
<desc>Created with Sketch.</desc>
<defs> </defs>
<g id="databases-and-servers/servers/ssh" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="container" transform="translate(2.000000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero"
stroke="#2562eb" stroke-width="2">
<rect id="mask" x="-1" y="-1" width="71" height="71" rx="14"> </rect>
</g>
<g id="terminal" transform="translate(11.000000, 17.000000)" fill="#2562eb" fill-rule="nonzero">
<path
d="M49.2407485,0.0215730994 L1.65983626,0.0215730994 C0.743128655,0.0215730994 0,0.76480117 0,1.68140936 L0,9.94480117 L50.9005848,9.94480117 L50.9005848,1.68140936 C50.9005848,0.76480117 50.1574561,0.0215730994 49.2407485,0.0215730994 Z M32.0399298,7.19626901 C30.8196082,7.19626901 29.826848,6.20350877 29.826848,4.98318713 C29.826848,3.7628655 30.8196082,2.77010526 32.0399298,2.77010526 C33.2602515,2.77010526 34.2530117,3.7628655 34.2530117,4.98318713 C34.2530117,6.20350877 33.2602515,7.19626901 32.0399298,7.19626901 Z M38.0686667,7.19626901 C36.848345,7.19626901 35.8555848,6.20350877 35.8555848,4.98318713 C35.8555848,3.7628655 36.848345,2.77010526 38.0686667,2.77010526 C39.2889883,2.77010526 40.2817485,3.7628655 40.2817485,4.98318713 C40.2817485,6.20350877 39.2889883,7.19626901 38.0686667,7.19626901 Z M44.0974035,7.19626901 C42.8770819,7.19626901 41.8843216,6.20350877 41.8843216,4.98318713 C41.8843216,3.7628655 42.8770819,2.77010526 44.0974035,2.77010526 C45.3177251,2.77010526 46.3104854,3.7628655 46.3104854,4.98318713 C46.3104854,6.20350877 45.3177251,7.19626901 44.0974035,7.19626901 Z"
id="Shape"> </path>
<path
d="M0,13.2643743 L0,37.0905205 C0,38.0071287 0.743128655,38.7503567 1.65983626,38.7503567 L49.240848,38.7503567 C50.1574561,38.7503567 50.9006842,38.0072281 50.9006842,37.0905205 L50.9006842,13.2643743 L0,13.2643743 Z M23.4073099,27.2714795 L17.9739708,31.8946842 C17.2758772,32.4887895 16.2283392,32.404386 15.6342339,31.706193 C15.0401287,31.0080994 15.1245322,29.9605614 15.8227251,29.3664561 L19.7704035,26.0073158 L15.8227251,22.6481754 C15.1245322,22.0540702 15.0402281,21.0066316 15.6342339,20.3084386 C16.2282398,19.6101462 17.2758772,19.5259415 17.9739708,20.1199474 L23.4073099,24.743152 C24.185731,25.4055556 24.185731,26.6091754 23.4073099,27.2714795 Z M34.0021871,32.2903567 L27.4453567,32.2903567 C26.5287485,32.2903567 25.7855205,31.5472281 25.7855205,30.6305205 C25.7855205,29.7138129 26.5286491,28.9706842 27.4453567,28.9706842 L34.0021871,28.9706842 C34.9187953,28.9706842 35.6620234,29.7138129 35.6620234,30.6305205 C35.6620234,31.5472281 34.9187953,32.2903567 34.0021871,32.2903567 Z"
id="Shape"> </path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

4
ui/dist/index.html vendored
View File

@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-CJ5MTZe-.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-VYJgHfoP.css">
<script type="module" crossorigin src="/assets/index-zmVKcms4.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DK_EegWM.css">
</head>
<body class="bg-background">
<div id="root"></div>

View File

@ -0,0 +1,26 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 73 73" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" fill="#2562eb">
<g id="SVGRepo_bgCarrier" stroke-width="0" />
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
<g id="SVGRepo_iconCarrier">
<title>databases-and-servers/servers/ssh</title>
<desc>Created with Sketch.</desc>
<defs> </defs>
<g id="databases-and-servers/servers/ssh" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="container" transform="translate(2.000000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero"
stroke="#2562eb" stroke-width="2">
<rect id="mask" x="-1" y="-1" width="71" height="71" rx="14"> </rect>
</g>
<g id="terminal" transform="translate(11.000000, 17.000000)" fill="#2562eb" fill-rule="nonzero">
<path
d="M49.2407485,0.0215730994 L1.65983626,0.0215730994 C0.743128655,0.0215730994 0,0.76480117 0,1.68140936 L0,9.94480117 L50.9005848,9.94480117 L50.9005848,1.68140936 C50.9005848,0.76480117 50.1574561,0.0215730994 49.2407485,0.0215730994 Z M32.0399298,7.19626901 C30.8196082,7.19626901 29.826848,6.20350877 29.826848,4.98318713 C29.826848,3.7628655 30.8196082,2.77010526 32.0399298,2.77010526 C33.2602515,2.77010526 34.2530117,3.7628655 34.2530117,4.98318713 C34.2530117,6.20350877 33.2602515,7.19626901 32.0399298,7.19626901 Z M38.0686667,7.19626901 C36.848345,7.19626901 35.8555848,6.20350877 35.8555848,4.98318713 C35.8555848,3.7628655 36.848345,2.77010526 38.0686667,2.77010526 C39.2889883,2.77010526 40.2817485,3.7628655 40.2817485,4.98318713 C40.2817485,6.20350877 39.2889883,7.19626901 38.0686667,7.19626901 Z M44.0974035,7.19626901 C42.8770819,7.19626901 41.8843216,6.20350877 41.8843216,4.98318713 C41.8843216,3.7628655 42.8770819,2.77010526 44.0974035,2.77010526 C45.3177251,2.77010526 46.3104854,3.7628655 46.3104854,4.98318713 C46.3104854,6.20350877 45.3177251,7.19626901 44.0974035,7.19626901 Z"
id="Shape"> </path>
<path
d="M0,13.2643743 L0,37.0905205 C0,38.0071287 0.743128655,38.7503567 1.65983626,38.7503567 L49.240848,38.7503567 C50.1574561,38.7503567 50.9006842,38.0072281 50.9006842,37.0905205 L50.9006842,13.2643743 L0,13.2643743 Z M23.4073099,27.2714795 L17.9739708,31.8946842 C17.2758772,32.4887895 16.2283392,32.404386 15.6342339,31.706193 C15.0401287,31.0080994 15.1245322,29.9605614 15.8227251,29.3664561 L19.7704035,26.0073158 L15.8227251,22.6481754 C15.1245322,22.0540702 15.0402281,21.0066316 15.6342339,20.3084386 C16.2282398,19.6101462 17.2758772,19.5259415 17.9739708,20.1199474 L23.4073099,24.743152 C24.185731,25.4055556 24.185731,26.6091754 23.4073099,27.2714795 Z M34.0021871,32.2903567 L27.4453567,32.2903567 C26.5287485,32.2903567 25.7855205,31.5472281 25.7855205,30.6305205 C25.7855205,29.7138129 26.5286491,28.9706842 27.4453567,28.9706842 L34.0021871,28.9706842 C34.9187953,28.9706842 35.6620234,29.7138129 35.6620234,30.6305205 C35.6620234,31.5472281 34.9187953,32.2903567 34.0021871,32.2903567 Z"
id="Shape"> </path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,73 @@
import { createContext, useContext, useEffect, useState } from "react";
type Theme = "dark" | "light" | "system";
type ThemeProviderProps = {
children: React.ReactNode;
defaultTheme?: Theme;
storageKey?: string;
};
type ThemeProviderState = {
theme: Theme;
setTheme: (theme: Theme) => void;
};
const initialState: ThemeProviderState = {
theme: "system",
setTheme: () => null,
};
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
export function ThemeProvider({
children,
defaultTheme = "system",
storageKey = "vite-ui-theme",
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
);
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
root.classList.add(systemTheme);
return;
}
root.classList.add(theme);
}, [theme]);
const value = {
theme,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};
return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
}
export const useTheme = () => {
const context = useContext(ThemeProviderContext);
if (context === undefined)
throw new Error("useTheme must be used within a ThemeProvider");
return context;
};

View File

@ -0,0 +1,37 @@
import { Moon, Sun } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useTheme } from "./ThemeProvider";
export function ThemeToggle() {
const { setTheme } = useTheme();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 dark:text-white" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -90,7 +90,7 @@ export function AccessEdit({
<DialogTrigger asChild className={cn(className)}>
{trigger}
</DialogTrigger>
<DialogContent className="sm:max-w-[600px] w-full">
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
<DialogHeader>
<DialogTitle>{op == "add" ? "添加" : "编辑"}</DialogTitle>
</DialogHeader>

View File

@ -3,7 +3,7 @@ import { z } from "zod";
export const accessTypeMap: Map<string, [string, string]> = new Map([
["tencent", ["腾讯云", "/imgs/providers/tencent.svg"]],
["aliyun", ["阿里云", "/imgs/providers/aliyun.svg"]],
["ssh", ["SSH部署", "/imgs/providers/ssh.png"]],
["ssh", ["SSH部署", "/imgs/providers/ssh.svg"]],
["webhook", ["Webhook", "/imgs/providers/webhook.svg"]],
]);

View File

@ -31,7 +31,7 @@ export const targetTypeMap: Map<string, [string, string]> = new Map([
["aliyun-cdn", ["阿里云-CDN", "/imgs/providers/aliyun.svg"]],
["aliyun-oss", ["阿里云-OSS", "/imgs/providers/aliyun.svg"]],
["tencent-cdn", ["腾讯云-CDN", "/imgs/providers/tencent.svg"]],
["ssh", ["SSH部署", "/imgs/providers/ssh.png"]],
["ssh", ["SSH部署", "/imgs/providers/ssh.svg"]],
["webhook", ["Webhook", "/imgs/providers/webhook.svg"]],
]);

View File

@ -3,9 +3,12 @@ import ReactDOM from "react-dom/client";
import "./global.css";
import { RouterProvider } from "react-router-dom";
import { router } from "./router.tsx";
import { ThemeProvider } from "./components/ThemeProvider.tsx";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<RouterProvider router={router} />
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<RouterProvider router={router} />
</ThemeProvider>
</React.StrictMode>
);

View File

@ -21,6 +21,7 @@ import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
import { ConfigProvider } from "@/providers/config";
import { getPb } from "@/repository/api";
import { ThemeToggle } from "@/components/ThemeToggle";
export default function Dashboard() {
const navigate = useNavigate();
@ -49,12 +50,12 @@ export default function Dashboard() {
<>
<ConfigProvider>
<div className="grid min-h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr]">
<div className="hidden border-r bg-muted/40 md:block">
<div className="hidden border-r dark:border-stone-500 bg-muted/40 md:block">
<div className="flex h-full max-h-screen flex-col gap-2">
<div className="flex h-14 items-center border-b px-4 lg:h-[60px] lg:px-6">
<div className="flex h-14 items-center border-b dark:border-stone-500 px-4 lg:h-[60px] lg:px-6">
<Link to="/" className="flex items-center gap-2 font-semibold">
<img src="/vite.svg" className="w-[36px] h-[36px]" />
<span className="">Certimate</span>
<span className="dark:text-white">Certimate</span>
</Link>
</div>
<div className="flex-1">
@ -95,7 +96,7 @@ export default function Dashboard() {
</div>
</div>
<div className="flex flex-col">
<header className="flex h-14 items-center gap-4 border-b bg-muted/40 px-4 lg:h-[60px] lg:px-6">
<header className="flex h-14 items-center gap-4 border-b dark:border-stone-500 bg-muted/40 px-4 lg:h-[60px] lg:px-6">
<Sheet>
<SheetTrigger asChild>
<Button
@ -152,6 +153,7 @@ export default function Dashboard() {
</SheetContent>
</Sheet>
<div className="w-full flex-1"></div>
<ThemeToggle />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
@ -182,12 +184,12 @@ export default function Dashboard() {
<div className="fixed right-0 bottom-0 w-full flex justify-between p-5">
<div className=""></div>
<div className="text-muted-foreground text-sm hover:text-stone-900">
<div className="text-muted-foreground text-sm hover:text-stone-900 dark:hover:text-stone-200">
<a
href="https://github.com/usual2970/certimate/releases"
target="_blank"
>
Certimate v0.0.9
Certimate v0.0.10
</a>
</div>
</div>

View File

@ -5,7 +5,9 @@ const SettingLayout = () => {
return (
<div>
<Toaster />
<div className="text-muted-foreground border-b py-5"></div>
<div className="text-muted-foreground border-b dark:border-stone-500 py-5">
</div>
<div className="w-full sm:w-[35em] mt-10 flex flex-col p-3 mx-auto">
{/* <div className="text-muted-foreground">
<span className="transition-all text-sm bg-gray-400 px-3 py-1 rounded-sm text-white cursor-pointer">

View File

@ -39,7 +39,7 @@ const Access = () => {
</div>
) : (
<>
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b sm:p-2 mt-5">
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
<div className="w-48"></div>
<div className="w-48"></div>
@ -52,7 +52,7 @@ const Access = () => {
</div>
{accesses.map((access) => (
<div
className="flex flex-col sm:flex-row text-secondary-foreground border-b sm:p-2 hover:bg-muted/50 text-sm"
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
key={access.id}
>
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center">

View File

@ -152,12 +152,15 @@ const Edit = () => {
<>
<div className="">
<Toaster />
<div className="border-b h-10 text-muted-foreground">
<div className="border-b dark:border-stone-500 h-10 text-muted-foreground">
{domain?.id ? "编辑" : "新增"}
</div>
<div className="max-w-[35em] mx-auto mt-10">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8 dark:text-stone-200"
>
<FormField
control={form.control}
name="domain"

View File

@ -170,7 +170,7 @@ const Home = () => {
</>
) : (
<>
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b sm:p-2 mt-5">
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
<div className="w-40"></div>
<div className="w-48"></div>
<div className="w-32"></div>
@ -185,7 +185,7 @@ const Home = () => {
{domains.map((domain) => (
<div
className="flex flex-col sm:flex-row text-secondary-foreground border-b sm:p-2 hover:bg-muted/50 text-sm"
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
key={domain.id}
>
<div className="sm:w-40 w-full pt-1 sm:pt-0 flex items-center">

View File

@ -66,7 +66,7 @@ const History = () => {
</>
) : (
<>
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b sm:p-2 mt-5">
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
<div className="w-48"></div>
<div className="w-24"></div>
@ -82,7 +82,7 @@ const History = () => {
{deployments?.map((deployment) => (
<div
key={deployment.id}
className="flex flex-col sm:flex-row text-secondary-foreground border-b sm:p-2 hover:bg-muted/50 text-sm"
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
>
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center">
{deployment.expand.domain?.domain}

View File

@ -85,7 +85,10 @@ const Password = () => {
return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8 dark:text-stone-200"
>
<FormField
control={form.control}
name="oldPassword"