improve multi language

This commit is contained in:
yoan
2024-11-23 12:55:31 +08:00
parent 47050769fc
commit 37df882ed3
22 changed files with 291 additions and 105 deletions

View File

@@ -5,6 +5,7 @@ import { Textarea } from "../ui/textarea";
import { Button } from "../ui/button";
import { Label } from "../ui/label";
import { CustomFile, saveFiles2ZIP } from "@/lib/file";
import { useTranslation } from "react-i18next";
type WorkflowLogDetailProps = {
open: boolean;
@@ -12,6 +13,7 @@ type WorkflowLogDetailProps = {
certificate?: Certificate;
};
const CertificateDetail = ({ open, onOpenChange, certificate }: WorkflowLogDetailProps) => {
const { t } = useTranslation();
const handleDownloadClick = async () => {
const zipName = `${certificate?.id}-${certificate?.san}.zip`;
const files: CustomFile[] = [
@@ -30,7 +32,7 @@ const CertificateDetail = ({ open, onOpenChange, certificate }: WorkflowLogDetai
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent className="sm:max-w-2xl">
<SheetContent className="sm:max-w-2xl dark:text-stone-200">
<SheetHeader>
<SheetTitle></SheetTitle>
</SheetHeader>
@@ -43,15 +45,15 @@ const CertificateDetail = ({ open, onOpenChange, certificate }: WorkflowLogDetai
handleDownloadClick();
}}
>
{t("certificate.action.download")}
</Button>
</div>
<div className="flex flex-col space-y-3">
<Label></Label>
<Label>{t("certificate.props.certificate")}</Label>
<Textarea value={certificate?.certificate} rows={10} readOnly={true} />
</div>
<div className="flex flex-col space-y-3">
<Label></Label>
<Label>{t("certificate.props.private.key")}</Label>
<Textarea value={certificate?.privateKey} rows={10} readOnly={true} />
</div>
</div>

View File

@@ -6,7 +6,8 @@ import { diffDays, getLeftDays } from "@/lib/time";
import { list } from "@/repository/certificate";
import { ColumnDef } from "@tanstack/react-table";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useNavigate, useSearchParams } from "react-router-dom";
type CertificateListProps = {
withPagination?: boolean;
@@ -18,8 +19,13 @@ const CertificateList = ({ withPagination }: CertificateListProps) => {
const [open, setOpen] = useState(false);
const [selectedCertificate, setSelectedCertificate] = useState<CertificateType>();
const { t } = useTranslation();
const [searchParams] = useSearchParams();
const fetchData = async (page: number, pageSize?: number) => {
const resp = await list({ page: page, perPage: pageSize });
const state = searchParams.get("state");
const resp = await list({ page: page, perPage: pageSize, state: state ?? "" });
setData(resp.items);
setPageCount(resp.totalPages);
};
@@ -29,7 +35,7 @@ const CertificateList = ({ withPagination }: CertificateListProps) => {
const columns: ColumnDef<CertificateType>[] = [
{
accessorKey: "san",
header: "域名",
header: t("certificate.props.domain"),
cell: ({ row }) => {
let san: string = row.getValue("san");
if (!san) {
@@ -51,7 +57,7 @@ const CertificateList = ({ withPagination }: CertificateListProps) => {
},
{
accessorKey: "expireAt",
header: "有效期限",
header: t("certificate.props.expiry"),
cell: ({ row }) => {
const expireAt: string = row.getValue("expireAt");
const data = row.original;
@@ -61,20 +67,22 @@ const CertificateList = ({ withPagination }: CertificateListProps) => {
<div className="">
{leftDays > 0 ? (
<div className="text-green-500">
{leftDays} / {allDays}
{leftDays} / {allDays} {t("certificate.props.expiry.days")}
</div>
) : (
<div className="text-red-500"></div>
<div className="text-red-500">{t("certificate.props.expiry.expired")}</div>
)}
<div>{new Date(expireAt).toLocaleString().split(" ")[0]} </div>
<div>
{new Date(expireAt).toLocaleString().split(" ")[0]} {t("certificate.props.expiry.text.expire")}
</div>
</div>
);
},
},
{
accessorKey: "workflow",
header: "所属工作流",
header: t("certificate.props.workflow"),
cell: ({ row }) => {
const name = row.original.expand.workflow?.name;
const workflowId: string = row.getValue("workflow");
@@ -95,7 +103,7 @@ const CertificateList = ({ withPagination }: CertificateListProps) => {
},
{
accessorKey: "created",
header: "颁发时间",
header: t("certificate.props.created"),
cell: ({ row }) => {
const date: string = row.getValue("created");
return new Date(date).toLocaleString();
@@ -113,7 +121,7 @@ const CertificateList = ({ withPagination }: CertificateListProps) => {
handleView(row.original.id);
}}
>
{t("certificate.action.view")}
</Button>
</div>
);
@@ -140,15 +148,16 @@ const CertificateList = ({ withPagination }: CertificateListProps) => {
pageCount={pageCount}
withPagination={withPagination}
fallback={
<div className="flex flex-col">
<div className="text-muted-foreground">😀</div>
<div className="flex flex-col items-center">
<div className="text-muted-foreground">{t("certificate.nodata")}</div>
<Button
size={"sm"}
className="w-[120px] mt-3"
onClick={() => {
navigate("/workflow/detail");
}}
>
{t("workflow.action.create")}
</Button>
</div>
}

View File

@@ -4,6 +4,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
import { Button } from "../ui/button";
import { useEffect, useState } from "react";
import Show from "../Show";
import { useTranslation } from "react-i18next";
interface DataTableProps<TData extends { id: string }, TValue> {
columns: ColumnDef<TData, TValue>[];
@@ -29,6 +30,8 @@ export function DataTable<TData extends { id: string }, TValue>({
pageSize: 10,
});
const { t } = useTranslation();
const pagination = {
pageIndex,
pageSize,
@@ -88,7 +91,7 @@ export function DataTable<TData extends { id: string }, TValue>({
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
{fallback ? fallback : "暂无数据"}
{fallback ? fallback : t("common.text.nodata")}
</TableCell>
</TableRow>
)}
@@ -100,13 +103,13 @@ export function DataTable<TData extends { id: string }, TValue>({
<div className="flex items-center space-x-2 dark:text-stone-200">
{table.getCanPreviousPage() && (
<Button variant="outline" size="sm" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>
{t("common.pagination.prev")}
</Button>
)}
{table.getCanNextPage && (
<Button variant="outline" size="sm" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
{t("common.pagination.next")}
</Button>
)}
</div>

View File

@@ -8,7 +8,7 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "
import { Input } from "../ui/input";
import { Button } from "../ui/button";
import { useTranslation } from "react-i18next";
import { memo, useEffect, useState } from "react";
import { memo, useEffect, useMemo, useState } from "react";
import { Textarea } from "../ui/textarea";
type WorkflowNameEditDialogProps = {
@@ -31,9 +31,11 @@ const WorkflowNameBaseInfoDialog = ({ trigger }: WorkflowNameEditDialogProps) =>
resolver: zodResolver(formSchema),
});
const memoWorkflow = useMemo(() => workflow, [workflow]);
useEffect(() => {
form.reset({ name: workflow.name, description: workflow.description });
}, [workflow]);
}, [memoWorkflow]);
const { t } = useTranslation();
@@ -55,7 +57,7 @@ const WorkflowNameBaseInfoDialog = ({ trigger }: WorkflowNameEditDialogProps) =>
<DialogTrigger>{trigger}</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogTitle className="dark:text-stone-200">{t("workflow.baseinfo.title")}</DialogTitle>
</DialogHeader>
<div>
<Form {...form}>
@@ -64,17 +66,17 @@ const WorkflowNameBaseInfoDialog = ({ trigger }: WorkflowNameEditDialogProps) =>
e.stopPropagation();
form.handleSubmit(onSubmit)(e);
}}
className="space-y-8"
className="space-y-8 dark:text-stone-200"
>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormLabel>{t("workflow.props.name")}</FormLabel>
<FormControl>
<Input
placeholder="请输入流程名称"
placeholder={t("workflow.props.name.placeholder")}
{...field}
value={field.value}
defaultValue={workflow.name}
@@ -94,10 +96,10 @@ const WorkflowNameBaseInfoDialog = ({ trigger }: WorkflowNameEditDialogProps) =>
name="description"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormLabel>{t("workflow.props.description")}</FormLabel>
<FormControl>
<Textarea
placeholder="请输入流程说明"
placeholder={t("workflow.props.description.placeholder")}
{...field}
value={field.value}
defaultValue={workflow.description}

View File

@@ -6,6 +6,7 @@ import { DataTable } from "./DataTable";
import { useSearchParams } from "react-router-dom";
import { Check, X } from "lucide-react";
import WorkflowLogDetail from "./WorkflowLogDetail";
import { useTranslation } from "react-i18next";
const WorkflowLog = () => {
const [data, setData] = useState<WorkflowRunLog[]>([]);
@@ -14,6 +15,8 @@ const WorkflowLog = () => {
const [searchParams] = useSearchParams();
const id = searchParams.get("id");
const { t } = useTranslation();
const [open, setOpen] = useState(false);
const [selectedLog, setSelectedLog] = useState<WorkflowRunLog>();
@@ -26,7 +29,7 @@ const WorkflowLog = () => {
const columns: ColumnDef<WorkflowRunLog>[] = [
{
accessorKey: "succeed",
header: "状态",
header: t("workflow.history.props.state"),
cell: ({ row }) => {
const succeed: boolean = row.getValue("succeed");
if (succeed) {
@@ -35,7 +38,7 @@ const WorkflowLog = () => {
<div className="text-white bg-green-500 w-8 h-8 rounded-full flex items-center justify-center">
<Check size={18} />
</div>
<div className="text-sone-700"></div>
<div className="text-sone-700">{t("workflow.history.props.state.success")}</div>
</div>
);
} else {
@@ -44,7 +47,7 @@ const WorkflowLog = () => {
<div className="text-white bg-red-500 w-8 h-8 rounded-full flex items-center justify-center">
<X size={18} />
</div>
<div className="text-stone-700"></div>
<div className="text-stone-700">{t("workflow.history.props.state.failed")}</div>
</div>
);
}
@@ -52,7 +55,7 @@ const WorkflowLog = () => {
},
{
accessorKey: "error",
header: "原因",
header: t("workflow.history.props.reason"),
cell: ({ row }) => {
let error: string = row.getValue("error");
if (!error) {
@@ -63,7 +66,7 @@ const WorkflowLog = () => {
},
{
accessorKey: "created",
header: "时间",
header: t("workflow.history.props.time"),
cell: ({ row }) => {
const date: string = row.getValue("created");
return new Date(date).toLocaleString();
@@ -79,7 +82,7 @@ const WorkflowLog = () => {
return (
<div className="w-full md:w-[960px]">
<div>
<div className="text-muted-foreground mb-5"></div>
<div className="text-muted-foreground mb-5">{t("workflow.history.page.title")}</div>
<DataTable columns={columns} data={data} onPageChange={fetchData} pageCount={pageCount} onRowClick={handleRowClick} />
</div>

View File

@@ -2,6 +2,7 @@ import { WorkflowOutput, WorkflowRunLog, WorkflowRunLogItem } from "@/domain/wor
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "../ui/sheet";
import { Check, X } from "lucide-react";
import { ScrollArea } from "../ui/scroll-area";
import { useTranslation } from "react-i18next";
type WorkflowLogDetailProps = {
open: boolean;
@@ -9,11 +10,12 @@ type WorkflowLogDetailProps = {
log?: WorkflowRunLog;
};
const WorkflowLogDetail = ({ open, onOpenChange, log }: WorkflowLogDetailProps) => {
const { t } = useTranslation();
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent className="sm:max-w-5xl">
<SheetHeader>
<SheetTitle></SheetTitle>
<SheetTitle>{t("workflow.history.page.title")}</SheetTitle>
</SheetHeader>
<div className="flex flex-col">
@@ -23,7 +25,7 @@ const WorkflowLogDetail = ({ open, onOpenChange, log }: WorkflowLogDetailProps)
<div className="w-8 h-8 bg-green-500 flex items-center justify-center rounded-full text-white">
<Check size={18} />
</div>
<div className="text-stone-700"></div>
<div className="text-stone-700">{t("workflow.history.props.state.success")}</div>
</div>
<div className="text-muted-foreground">{new Date(log.created).toLocaleString()}</div>
@@ -34,7 +36,7 @@ const WorkflowLogDetail = ({ open, onOpenChange, log }: WorkflowLogDetailProps)
<div className="w-8 h-8 bg-red-500 flex items-center justify-center rounded-full text-white">
<X size={18} />
</div>
<div className="text-stone-700"></div>
<div className="text-stone-700">{t("workflow.history.props.state.failed")}</div>
</div>
<div className="text-red-500 max-w-[400px] truncate">{log?.error}</div>