Improved user interface translations and clarity in Simplified
This commit is contained in:
@@ -4,6 +4,7 @@ import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { User } from "@prisma/client";
|
||||
import { PenLine, RefreshCwIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { toast } from "sonner";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
|
||||
@@ -70,6 +71,7 @@ function TableColumnSekleton() {
|
||||
|
||||
export default function DomainList({ user, action }: DomainListProps) {
|
||||
const { isMobile } = useMediaQuery();
|
||||
const t = useTranslations("List");
|
||||
const [isShowForm, setShowForm] = useState(false);
|
||||
const [formType, setFormType] = useState<FormType>("add");
|
||||
const [currentEditDomain, setCurrentEditDomain] =
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { DashboardHeader } from "@/components/dashboard/header";
|
||||
|
||||
export default function DashboardLoading() {
|
||||
return (
|
||||
<>
|
||||
<DashboardHeader heading="Dashboard" text="" />
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3 lg:grid-cols-3">
|
||||
<Skeleton className="h-32 w-full rounded-lg" />
|
||||
|
||||
@@ -13,11 +13,9 @@ import {
|
||||
DashboardInfoCard,
|
||||
HeroCard,
|
||||
} from "@/components/dashboard/dashboard-info-card";
|
||||
import { DashboardHeader } from "@/components/dashboard/header";
|
||||
import { ErrorBoundary } from "@/components/shared/error-boundary";
|
||||
|
||||
import UserRecordsList from "./records/record-list";
|
||||
import LiveLog from "./urls/live-logs";
|
||||
import UserUrlsList from "./urls/url-list";
|
||||
|
||||
export const metadata = constructMetadata({
|
||||
@@ -87,10 +85,6 @@ async function DnsRecordsCardSection({
|
||||
);
|
||||
}
|
||||
|
||||
async function LiveLogSection() {
|
||||
return <LiveLog admin={false} />;
|
||||
}
|
||||
|
||||
async function UserUrlsListSection({
|
||||
user,
|
||||
}: {
|
||||
@@ -148,7 +142,7 @@ export default async function DashboardPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<DashboardHeader heading="Dashboard" text="" />
|
||||
{/* <DashboardHeader heading="Dashboard" text="" /> */}
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3 xl:grid-cols-3">
|
||||
<ErrorBoundary
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { User } from "@prisma/client";
|
||||
import { PenLine, RefreshCwIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { toast } from "sonner";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
|
||||
@@ -87,9 +88,10 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
useState<UserRecordFormData | null>(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [tab, setTab] = useState("app");
|
||||
const isAdmin = action.includes("/admin");
|
||||
|
||||
const t = useTranslations("List");
|
||||
|
||||
const { mutate } = useSWRConfig();
|
||||
|
||||
const { data, isLoading } = useSWR<{
|
||||
@@ -144,30 +146,30 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
<CardHeader className="flex flex-row items-center">
|
||||
{isAdmin ? (
|
||||
<CardDescription className="text-balance text-lg font-bold">
|
||||
<span>Total Subdomains:</span>{" "}
|
||||
<span>{t("Total Subdomains")}:</span>{" "}
|
||||
<span className="font-bold">{data && data.total}</span>
|
||||
</CardDescription>
|
||||
) : (
|
||||
<div className="grid gap-2">
|
||||
<CardTitle>Subdomains</CardTitle>
|
||||
<CardTitle>{t("Subdomain List")}</CardTitle>
|
||||
<CardDescription className="hidden text-balance sm:block">
|
||||
Please read the{" "}
|
||||
{t("Please read the")}{" "}
|
||||
<Link
|
||||
target="_blank"
|
||||
className="font-semibold text-yellow-600 after:content-['↗'] hover:underline"
|
||||
href="/docs/dns-records#legitimacy-review"
|
||||
>
|
||||
Legitimacy review
|
||||
{t("legitimacy review")}
|
||||
</Link>{" "}
|
||||
before using. See{" "}
|
||||
{t("before using")}. {t("See")}{" "}
|
||||
<Link
|
||||
target="_blank"
|
||||
className="text-blue-500 hover:underline"
|
||||
href="/docs/examples/vercel"
|
||||
>
|
||||
examples
|
||||
{t("examples")}
|
||||
</Link>{" "}
|
||||
for more usage.
|
||||
{t("for more usage")}.
|
||||
</CardDescription>
|
||||
</div>
|
||||
)}
|
||||
@@ -194,7 +196,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
}}
|
||||
>
|
||||
<Icons.add className="size-4" />
|
||||
<span className="hidden sm:inline">Add Record</span>
|
||||
<span className="hidden sm:inline">{t("Add Record")}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
@@ -203,28 +205,28 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
<TableHeader className="bg-gray-100/50 dark:bg-primary-foreground">
|
||||
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-9">
|
||||
<TableHead className="col-span-1 flex items-center font-bold">
|
||||
Type
|
||||
{t("Type")}
|
||||
</TableHead>
|
||||
<TableHead className="col-span-1 flex items-center font-bold">
|
||||
Name
|
||||
{t("Name")}
|
||||
</TableHead>
|
||||
<TableHead className="col-span-2 hidden items-center font-bold sm:flex">
|
||||
Content
|
||||
{t("Content")}
|
||||
</TableHead>
|
||||
<TableHead className="col-span-1 hidden items-center font-bold sm:flex">
|
||||
TTL
|
||||
{t("TTL")}
|
||||
</TableHead>
|
||||
<TableHead className="col-span-1 hidden items-center justify-center font-bold sm:flex">
|
||||
Status
|
||||
{t("Status")}
|
||||
</TableHead>
|
||||
<TableHead className="col-span-1 hidden items-center font-bold sm:flex">
|
||||
User
|
||||
{t("User")}
|
||||
</TableHead>
|
||||
<TableHead className="col-span-1 hidden items-center justify-center font-bold sm:flex">
|
||||
Updated
|
||||
{t("Updated")}
|
||||
</TableHead>
|
||||
<TableHead className="col-span-1 flex items-center justify-center font-bold">
|
||||
Actions
|
||||
{t("Actions")}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -279,7 +281,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
/>
|
||||
) : (
|
||||
<Badge className="rounded-md" variant={"yellow"}>
|
||||
Pending
|
||||
{t("Pending")}
|
||||
</Badge>
|
||||
)}
|
||||
{record.active !== 1 && (
|
||||
@@ -291,16 +293,21 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
<TooltipContent>
|
||||
{record.active === 0 && (
|
||||
<ul className="list-disc px-3">
|
||||
<li>The target is currently inaccessible.</li>
|
||||
<li>
|
||||
Please check the target and try again.
|
||||
{t("The target is currently inaccessible")}.
|
||||
</li>
|
||||
<li>
|
||||
If the target is not activated within 3
|
||||
days, <br />
|
||||
the administrator will{" "}
|
||||
{t("Please check the target and try again")}
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
{t(
|
||||
"If the target is not activated within 3 days",
|
||||
)}
|
||||
, <br />
|
||||
{t("the administrator will")}{" "}
|
||||
<strong className="text-red-500">
|
||||
delete this record
|
||||
{t("delete this record")}
|
||||
</strong>
|
||||
.
|
||||
</li>
|
||||
@@ -309,8 +316,10 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
{record.active === 2 && (
|
||||
<ul className="list-disc px-3">
|
||||
<li>
|
||||
The record is currently pending for admin
|
||||
approval.
|
||||
{t(
|
||||
"The record is currently pending for admin approval",
|
||||
)}
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
@@ -347,7 +356,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
setShowForm(!isShowForm);
|
||||
}}
|
||||
>
|
||||
<p>Edit</p>
|
||||
<p>{t("Edit")}</p>
|
||||
<PenLine className="ml-1 size-4" />
|
||||
</Button>
|
||||
) : record.active === 2 &&
|
||||
@@ -364,7 +373,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
setShowForm(!isShowForm);
|
||||
}}
|
||||
>
|
||||
<p>Review</p>
|
||||
<p>{t("Review")}</p>
|
||||
</Button>
|
||||
) : (
|
||||
"--"
|
||||
@@ -375,7 +384,9 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
) : (
|
||||
<EmptyPlaceholder className="shadow-none">
|
||||
<EmptyPlaceholder.Icon name="globe" />
|
||||
<EmptyPlaceholder.Title>No Subdomain</EmptyPlaceholder.Title>
|
||||
<EmptyPlaceholder.Title>
|
||||
{t("No Subdomains")}
|
||||
</EmptyPlaceholder.Title>
|
||||
<EmptyPlaceholder.Description>
|
||||
You don't have any subdomain yet. Start creating
|
||||
record.
|
||||
@@ -398,7 +409,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
</Card>
|
||||
|
||||
<Modal
|
||||
className="max-h-[90vh] overflow-y-auto md:max-w-2xl"
|
||||
className="max-h-[99vh] overflow-y-auto md:max-w-2xl"
|
||||
showModal={isShowForm}
|
||||
setShowModal={setShowForm}
|
||||
>
|
||||
|
||||
@@ -28,19 +28,19 @@ export default async function DashboardPage() {
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<StaticInfoCard
|
||||
title="Url to Screenshot"
|
||||
desc="Take a screenshot of the webpage."
|
||||
desc="Take a screenshot of the webpage"
|
||||
link="/dashboard/scrape/screenshot"
|
||||
icon="camera"
|
||||
/>
|
||||
<StaticInfoCard
|
||||
title="Url to Meta Info"
|
||||
desc="Extract website metadata."
|
||||
desc="Extract website metadata"
|
||||
link="/dashboard/scrape/meta-info"
|
||||
icon="globe"
|
||||
/>
|
||||
<StaticInfoCard
|
||||
title="Url to QR Code"
|
||||
desc="Generate QR Code from URL."
|
||||
desc="Generate QR Code from URL"
|
||||
link="/dashboard/scrape/qrcode"
|
||||
icon="qrcode"
|
||||
/>
|
||||
@@ -48,13 +48,13 @@ export default async function DashboardPage() {
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<StaticInfoCard
|
||||
title="Url to Markdown"
|
||||
desc="Convert website content to Markdown format."
|
||||
desc="Convert website content to Markdown format"
|
||||
link="/dashboard/scrape/markdown"
|
||||
icon="heading1"
|
||||
/>
|
||||
<StaticInfoCard
|
||||
title="Url to Text"
|
||||
desc="Extract website text."
|
||||
desc="Convert website content to text"
|
||||
link="/dashboard/scrape/markdown"
|
||||
icon="fileText"
|
||||
/>
|
||||
|
||||
@@ -187,7 +187,7 @@ export default function UserUrlsList({ user, action }: UrlListProps) {
|
||||
const rendeEmpty = () => (
|
||||
<EmptyPlaceholder className="col-span-full shadow-none">
|
||||
<EmptyPlaceholder.Icon name="link" />
|
||||
<EmptyPlaceholder.Title>No urls</EmptyPlaceholder.Title>
|
||||
<EmptyPlaceholder.Title>{t("No urls")}</EmptyPlaceholder.Title>
|
||||
<EmptyPlaceholder.Description>
|
||||
You don't have any url yet. Start creating url.
|
||||
</EmptyPlaceholder.Description>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { Link as LinkIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import { nFormatter } from "@/lib/utils";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
@@ -72,6 +72,7 @@ export async function DashboardInfoCard({
|
||||
link: string;
|
||||
icon?: keyof typeof Icons;
|
||||
}) {
|
||||
const t = useTranslations("Components");
|
||||
const Icon = Icons[icon || "arrowRight"];
|
||||
return (
|
||||
<Card className="grids group animate-fade-in bg-gray-50/70 backdrop-blur-lg dark:bg-primary-foreground">
|
||||
@@ -81,7 +82,7 @@ export async function DashboardInfoCard({
|
||||
className="font-semibold text-slate-500 duration-500 group-hover:text-blue-500 group-hover:underline"
|
||||
href={link}
|
||||
>
|
||||
{title}
|
||||
{t(title)}
|
||||
</Link>
|
||||
</CardTitle>
|
||||
<Icon className="size-4 text-muted-foreground" />
|
||||
@@ -94,12 +95,15 @@ export async function DashboardInfoCard({
|
||||
<CountUp count={monthTotal} />
|
||||
{total !== undefined && (
|
||||
<p className="align-top text-base text-slate-500">
|
||||
/ {nFormatter(limit)} <span className="text-xs">(monthly)</span>
|
||||
/ {nFormatter(limit)}{" "}
|
||||
<span className="text-xs">({t("monthly")})</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground">total: {total}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("total")}: {total}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
@@ -114,6 +118,7 @@ export function HeroCard({
|
||||
monthTotal: number;
|
||||
limit: number;
|
||||
}) {
|
||||
const t = useTranslations("Components");
|
||||
return (
|
||||
<div className="grids group relative mb-4 h-full w-full shrink-0 origin-left overflow-hidden rounded-lg border bg-gray-50/70 px-5 pt-5 text-left duration-500 before:absolute before:right-1 before:top-1 before:z-[2] before:h-12 before:w-12 before:rounded-full before:bg-violet-500 before:blur-lg before:duration-500 after:absolute after:right-8 after:top-3 after:z-[2] after:h-20 after:w-20 after:rounded-full after:bg-rose-300 after:blur-lg after:duration-500 hover:border-cyan-600 hover:decoration-2 hover:duration-500 hover:before:-bottom-8 hover:before:right-12 hover:before:blur hover:before:[box-shadow:_20px_20px_20px_30px_#a21caf] hover:after:-right-8 group-hover:before:duration-500 group-hover:after:duration-500 dark:bg-primary-foreground md:max-w-[350px]">
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
@@ -121,7 +126,7 @@ export function HeroCard({
|
||||
href="/emails"
|
||||
className="text-lg font-bold duration-500 group-hover:text-blue-500 group-hover:underline"
|
||||
>
|
||||
Email box
|
||||
{t("Email box")}
|
||||
</Link>
|
||||
<Icons.mail className="size-4 text-muted-foreground" />
|
||||
</div>
|
||||
@@ -134,12 +139,15 @@ export function HeroCard({
|
||||
<CountUp count={monthTotal} />
|
||||
{total !== undefined && (
|
||||
<p className="align-top text-base text-slate-500">
|
||||
/ {nFormatter(limit)} <span className="text-xs">(monthly)</span>
|
||||
/ {nFormatter(limit)}{" "}
|
||||
<span className="text-xs">({t("monthly")})</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground">total: {total}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("total")}: {total}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -157,6 +165,7 @@ export async function StaticInfoCard({
|
||||
icon?: keyof typeof Icons;
|
||||
}) {
|
||||
const Icon = Icons[icon || "arrowRight"];
|
||||
const t = useTranslations("Components");
|
||||
return (
|
||||
<Card className="grids group bg-gray-50/70 backdrop-blur-lg dark:bg-primary-foreground">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
@@ -165,13 +174,13 @@ export async function StaticInfoCard({
|
||||
className="font-semibold text-slate-500 duration-500 group-hover:text-blue-500 group-hover:underline"
|
||||
href={link}
|
||||
>
|
||||
{title}
|
||||
{t(title)}
|
||||
</Link>
|
||||
</CardTitle>
|
||||
<Icon className="size-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-xs text-muted-foreground">{desc}</p>
|
||||
{desc && <p className="text-xs text-muted-foreground">{t(desc)}</p>}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Dispatch, SetStateAction, useState, useTransition } from "react";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { User } from "@prisma/client";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import useSWR from "swr";
|
||||
@@ -61,9 +62,11 @@ export function RecordForm({
|
||||
const [currentZoneName, setCurrentZoneName] = useState(
|
||||
initData?.zone_name || "wr.do",
|
||||
);
|
||||
const [email, setEmail] = useState(user.email);
|
||||
const [email, setEmail] = useState(initData?.user.email || user.email);
|
||||
const isAdmin = action.indexOf("admin") > -1;
|
||||
|
||||
const t = useTranslations("List");
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
@@ -204,24 +207,26 @@ export function RecordForm({
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-t-lg bg-muted px-4 py-2 text-lg font-semibold">
|
||||
{type === "add" ? "Create" : "Edit"} record
|
||||
{type === "add" ? t("Create record") : t("Edit record")}
|
||||
</div>
|
||||
{siteConfig.enableSubdomainApply && (
|
||||
<ul className="m-2 list-disc gap-1 rounded-md bg-yellow-600/10 p-2 px-5 pr-2 text-xs font-medium text-yellow-600 dark:bg-yellow-500/10 dark:text-yellow-500">
|
||||
<li>The administrator has enabled application mode.</li>
|
||||
<li>{t("The administrator has enabled application mode")}.</li>
|
||||
<li>
|
||||
After submission, you need to wait for administrator approval before
|
||||
the record takes effect.
|
||||
{t(
|
||||
"After submission, you need to wait for administrator approval before the record takes effect",
|
||||
)}
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
<form className="p-4" onSubmit={onSubmit}>
|
||||
{isAdmin && (
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns required title="User email">
|
||||
<FormSectionColumns required title={t("User email")}>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="content">
|
||||
User email
|
||||
{t("User email")}
|
||||
</Label>
|
||||
<Input
|
||||
id="email"
|
||||
@@ -229,6 +234,7 @@ export function RecordForm({
|
||||
size={32}
|
||||
defaultValue={email || ""}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={type === "edit"}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
@@ -248,12 +254,12 @@ export function RecordForm({
|
||||
|
||||
{siteConfig.enableSubdomainApply && (
|
||||
<FormSectionColumns
|
||||
title="What are you planning to use the subdomain for?"
|
||||
title={t("What are you planning to use the subdomain for?")}
|
||||
required
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="comment">
|
||||
What are you planning to use the subdomain for?
|
||||
{t("What are you planning to use the subdomain for?")}
|
||||
</Label>
|
||||
<Textarea
|
||||
id="comment"
|
||||
@@ -263,12 +269,12 @@ export function RecordForm({
|
||||
/>
|
||||
</div>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
At least 20 characters
|
||||
{t("At least 20 characters")}
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
)}
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Domain" required>
|
||||
<FormSectionColumns title={t("Domain")} required>
|
||||
{isLoading ? (
|
||||
<Skeleton className="h-9 w-full" />
|
||||
) : (
|
||||
@@ -293,17 +299,17 @@ export function RecordForm({
|
||||
))
|
||||
) : (
|
||||
<Button className="w-full" variant="ghost">
|
||||
No domains configured
|
||||
{t("No domains configured")}
|
||||
</Button>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Required. Select a domain.
|
||||
{t("Required")}. {t("Select a domain")}.
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns title="Type" required>
|
||||
<FormSectionColumns title={t("Type")} required>
|
||||
<Select
|
||||
onValueChange={(value: RecordType) => {
|
||||
setValue("type", value);
|
||||
@@ -323,14 +329,16 @@ export function RecordForm({
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">Required.</p>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
{t("Required")}.
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
</div>
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Name" required>
|
||||
<FormSectionColumns title={t("Name")} required>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="name">
|
||||
Name (required)
|
||||
{t("Name")}
|
||||
</Label>
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
@@ -354,7 +362,7 @@ export function RecordForm({
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
Required. E.g. www.
|
||||
{t("Required")}. {t("Example")} www
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -363,15 +371,15 @@ export function RecordForm({
|
||||
required
|
||||
title={
|
||||
currentRecordType === "CNAME"
|
||||
? "Content"
|
||||
? t("Content")
|
||||
: currentRecordType === "A"
|
||||
? "IPv4 address"
|
||||
: "Content"
|
||||
? t("IPv4 address")
|
||||
: t("Content")
|
||||
}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="content">
|
||||
Content
|
||||
t("Content")
|
||||
</Label>
|
||||
<Input
|
||||
id="content"
|
||||
@@ -388,10 +396,10 @@ export function RecordForm({
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
{currentRecordType === "CNAME"
|
||||
? "Required. E.g. www.example.com"
|
||||
? `${t("Required")}. ${t("Example")} www.example.com`
|
||||
: currentRecordType === "A"
|
||||
? "Required. E.g. 8.8.8.8"
|
||||
: "Required."}
|
||||
? `${t("Required")}. ${t("Example")} 8.8.8.8`
|
||||
: t("Required")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -418,14 +426,14 @@ export function RecordForm({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Optional. Time To Live.
|
||||
{t("Optional")}. {t("Time To Live")}.
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
{["A", "CNAME"].includes(currentRecordType) && (
|
||||
<FormSectionColumns title="Proxy">
|
||||
<FormSectionColumns title={t("Proxy")}>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="proxy">
|
||||
Proxy
|
||||
{t("Proxy")}
|
||||
</Label>
|
||||
<Switch
|
||||
id="proxied"
|
||||
@@ -434,7 +442,7 @@ export function RecordForm({
|
||||
/>
|
||||
</div>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Proxy status.
|
||||
{t("Proxy status")}.
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
)}
|
||||
@@ -452,7 +460,7 @@ export function RecordForm({
|
||||
{isDeleting ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>Delete</p>
|
||||
<p>{t("Delete")}</p>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
@@ -462,7 +470,7 @@ export function RecordForm({
|
||||
className="w-[80px] px-0"
|
||||
onClick={() => setShowForm(false)}
|
||||
>
|
||||
Cancle
|
||||
{t("Cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
@@ -473,7 +481,7 @@ export function RecordForm({
|
||||
{isPending ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>{type === "edit" ? "Update" : "Save"}</p>
|
||||
<p>{type === "edit" ? t("Update") : t("Save")}</p>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,10 @@ description: 选择你的部署方式
|
||||
|
||||
<DocsLang en="/docs/developer/deploy" zh="/docs/developer/deploy-zh" />
|
||||
|
||||
<Callout type="warning" twClass="mt-4">
|
||||
在阅读此文档之前,建议首先阅读 [快速开始](/docs/developer/quick-start-zh),以确认准备好依赖的环境、变量。
|
||||
</Callout>
|
||||
|
||||
## 使用 Vercel 部署(推荐)
|
||||
|
||||
<Callout type="warning" twClass="mt-4">
|
||||
|
||||
@@ -5,6 +5,11 @@ description: Choose your deployment method
|
||||
|
||||
<DocsLang en="/docs/developer/deploy" zh="/docs/developer/deploy-zh" />
|
||||
|
||||
<Callout type="warning" twClass="mt-4">
|
||||
Before reading this document, it is recommended to first read [Quick Start](/docs/developer/quick-start),
|
||||
to confirm that the necessary environment variables are ready.
|
||||
</Callout>
|
||||
|
||||
## Deploy with Vercel (Recommended)
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/oiov/wr.do.git&project-name=wrdo&env=DATABASE_URL&env=AUTH_SECRET&env=RESEND_API_KEY&env=NEXT_PUBLIC_EMAIL_R2_DOMAIN&env=NEXT_PUBLIC_OPEN_SIGNUP&env=GITHUB_TOKEN)
|
||||
|
||||
+76
-2
@@ -49,7 +49,46 @@
|
||||
"Learn more about": "Learn more about",
|
||||
"Create Api Key": "Create Api Key",
|
||||
"Show": "Show",
|
||||
"per page": "per page"
|
||||
"per page": "per page",
|
||||
"Total Subdomains": "Total Subdomains",
|
||||
"Subdomain List": "Subdomains",
|
||||
"Please read the": "Please read the",
|
||||
"Legitimacy review": "Legitimacy review",
|
||||
"before using": "before using",
|
||||
"See": "See",
|
||||
"examples": "examples",
|
||||
"for more usage": "for more usage",
|
||||
"Add Record": "Add Record",
|
||||
"Type": "Type",
|
||||
"Name": "Name",
|
||||
"Content": "Content",
|
||||
"TTL": "TTL",
|
||||
"Status": "Status",
|
||||
"Pending": "Pending",
|
||||
"The record is currently pending for admin approval": "The record is currently pending for admin approval",
|
||||
"The target is currently inaccessible": "The target is currently inaccessible",
|
||||
"Please check the target and try again": "Please check the target and try again",
|
||||
"If the target is not activated within 3 days": "If the target is not activated within 3 days",
|
||||
"the administrator will": "the administrator will",
|
||||
"delete this record": "delete this record",
|
||||
"Review": "Review",
|
||||
"No Subdomains": "No Subdomains",
|
||||
"No urls": "No urls",
|
||||
"Create record": "Create record",
|
||||
"Edit record": "Edit record",
|
||||
"The administrator has enabled application mode": "The administrator has enabled application mode",
|
||||
"After submission, you need to wait for administrator approval before the record takes effect": "After submission, you need to wait for administrator approval before the record takes effect",
|
||||
"What are you planning to use the subdomain for?": "What are you planning to use the subdomain for?",
|
||||
"At least 20 characters": "At least 20 characters",
|
||||
"User email": "User email",
|
||||
"Domain": "Domain",
|
||||
"No domains configured": "No domains configured",
|
||||
"Select a domain": "Select a domain",
|
||||
"IPv4 address": "IPv4 address",
|
||||
"Example": "E.g.",
|
||||
"Time To Live": "Time To Live",
|
||||
"Proxy": "Proxy",
|
||||
"Proxy status": "DNS response is replaced by Cloudflare Anycast IP"
|
||||
},
|
||||
"Components": {
|
||||
"Dashboard": "Dashboard",
|
||||
@@ -72,7 +111,42 @@
|
||||
"Realtime Visits": "Realtime Visits",
|
||||
"See documentation": "See documentation",
|
||||
"Manage Short URLs": "Manage Short URLs",
|
||||
"List and manage short urls": "List and manage short urls"
|
||||
"List and manage short urls": "List and manage short urls",
|
||||
"Manage DNS Records": "Manage DNS Records",
|
||||
"List and manage records": "List and manage records",
|
||||
"Scraping API Overview": "Scraping API Overview",
|
||||
"Quickly extract valuable structured website data": "Quickly extract valuable structured website data",
|
||||
"Url to Screenshot": "Url to Screenshot",
|
||||
"Quickly extract website screenshots": "Quickly extract website screenshots",
|
||||
"extracting url as screenshot": "extracting url as screenshot",
|
||||
"Url to QR Code": "Url to QR Code",
|
||||
"Quickly extract website QR codes": "Quickly extract website QR codes",
|
||||
"extracting url as qrcode": "extracting url as qrcode",
|
||||
"Url to Meta Info": "Url to Meta Info",
|
||||
"extracting url as meta info": "extracting url as meta info",
|
||||
"Url to Markdown": "Url to Markdown",
|
||||
"Quickly extract website content and convert it to Markdown format": "Quickly extract website content and convert it to Markdown format",
|
||||
"extracting url as markdown": "extracting url as markdown",
|
||||
"extracting url as text": "extracting url as text",
|
||||
"Account Settings": "Account Settings",
|
||||
"Manage account and website settings": "Manage account and website settings",
|
||||
"Setup Guide": "Setup Guide",
|
||||
"Admin Panel": "Admin Panel",
|
||||
"Access only for users with ADMIN role": "Access only for users with ADMIN role",
|
||||
"Domains Management": "Domains Management",
|
||||
"List and manage domains": "List and manage domains",
|
||||
"User Management": "User Management",
|
||||
"List and manage all users": "List and manage all users",
|
||||
"Email box": "Email box",
|
||||
"monthly": "monthly",
|
||||
"total": "total",
|
||||
"Short URLs": "Short URLs",
|
||||
"DNS Records": "DNS Records",
|
||||
"Url to Text": "Url to Text",
|
||||
"Take a screenshot of the webpage": "Take a screenshot of the webpage",
|
||||
"Extract website metadata": "Extract website metadata",
|
||||
"Convert website content to Markdown format": "Convert website content to Markdown format",
|
||||
"Convert website content to text": "Convert website content to text"
|
||||
},
|
||||
"Landing": {
|
||||
"settings": "Settings",
|
||||
|
||||
+60
-11
@@ -49,7 +49,46 @@
|
||||
"Learn more about": "了解更多关于",
|
||||
"Create Api Key": "创建 API 密钥",
|
||||
"Show": "显示",
|
||||
"per page": "条/页"
|
||||
"per page": "条/页",
|
||||
"Total Subdomains": "总计",
|
||||
"Subdomain List": "子域名列表",
|
||||
"Please read the": "请阅读",
|
||||
"legitimacy review": "链接合法性审查",
|
||||
"before using": "在使用之前",
|
||||
"See": "查看",
|
||||
"examples": "示例",
|
||||
"for more usage": "了解更多用法",
|
||||
"Add Record": "添加记录",
|
||||
"Type": "类型",
|
||||
"Name": "名称",
|
||||
"Content": "内容",
|
||||
"TTL": "TTL",
|
||||
"Status": "状态",
|
||||
"Pending": "审核中",
|
||||
"The record is currently pending for admin approval": "正在等待管理员审核",
|
||||
"The target is currently inaccessible": "目标链接目前无法访问",
|
||||
"Please check the target and try again": "请检查解析记录并重试",
|
||||
"If the target is not activated within 3 days": "如果目标链接在 3 天内依然无法访问",
|
||||
"the administrator will": "管理员将",
|
||||
"delete this record": "删除此记录",
|
||||
"Review": "审核",
|
||||
"No Subdomains": "暂无子域名",
|
||||
"No urls": "暂无短链接",
|
||||
"Create record": "创建记录",
|
||||
"Edit record": "编辑记录",
|
||||
"The administrator has enabled application mode": "管理员已启用 [用户申请 - 管理员审核] 模式",
|
||||
"After submission, you need to wait for administrator approval before the record takes effect": "提交后, 您需要等待管理员审核才能生效",
|
||||
"What are you planning to use the subdomain for?": "您计划使用此域名做什么?",
|
||||
"At least 20 characters": "至少 20 个字符",
|
||||
"User email": "用户邮箱",
|
||||
"Domain": "根域名",
|
||||
"No domains configured": "未配置域名",
|
||||
"Select a domain": "选择一个域名",
|
||||
"IPv4 address": "IPv4 地址",
|
||||
"Example": "例如",
|
||||
"Time To Live": "生效时间",
|
||||
"Proxy": "代理记录",
|
||||
"Proxy status": "DNS 响应被 Cloudflare Anycast IP 替代"
|
||||
},
|
||||
"Components": {
|
||||
"Dashboard": "用户面板",
|
||||
@@ -97,16 +136,26 @@
|
||||
"Domains Management": "域名管理",
|
||||
"List and manage domains": "展示域名列表并管理你的域名服务",
|
||||
"User Management": "用户管理",
|
||||
"List and manage all users": "展示用户列表并管理所有用户"
|
||||
"List and manage all users": "展示用户列表并管理所有用户",
|
||||
"Email box": "邮件箱",
|
||||
"monthly": "每月",
|
||||
"total": "总计",
|
||||
"Short URLs": "短链接",
|
||||
"DNS Records": "DNS 记录",
|
||||
"Url to Text": "网址转文本",
|
||||
"Take a screenshot of the webpage": "使用 API 提取网页的截图",
|
||||
"Extract website metadata": "使用 API 提取网页的元数据",
|
||||
"Convert website content to Markdown format": "使用 API 将网页内容转换为 Markdown 格式",
|
||||
"Convert website content to text": "使用 API 将网页内容转换为文本"
|
||||
},
|
||||
"Landing": {
|
||||
"settings": "设置",
|
||||
"Dashboard": "管理面板",
|
||||
"Dashboard": "控制面板",
|
||||
"deployWithVercel": "使用",
|
||||
"now": "部署私有版本",
|
||||
"onePlatformPowers": "一站式域名",
|
||||
"endlessSolutions": "服务平台",
|
||||
"platformDescription": " 短链生成、子域名托管、无限邮箱服务,开放API接口,一站式域名管理解决方案。",
|
||||
"onePlatformPowers": " ",
|
||||
"endlessSolutions": "一站式域名服务平台",
|
||||
"platformDescription": "集成短链生成、子域名托管、无限邮箱服务,以及开放API接口,一站式域名管理解决方案,释放你的域名潜力",
|
||||
"documents": "参考文档",
|
||||
"signInForFree": "免费登录",
|
||||
"exampleImageAlt": "示例",
|
||||
@@ -140,11 +189,11 @@
|
||||
},
|
||||
"System": {
|
||||
"MENU": "菜单",
|
||||
"Dashboard": "用户面板",
|
||||
"Dashboard": "控制台",
|
||||
"Short Urls": "短链接",
|
||||
"Emails": "邮件",
|
||||
"DNS Records": "DNS 记录",
|
||||
"WRoom": "WRoom",
|
||||
"Emails": "邮件箱",
|
||||
"DNS Records": "子域名",
|
||||
"WRoom": "聊天室",
|
||||
"OPEN API": "开放API",
|
||||
"Overview": "概览面板",
|
||||
"Screenshot": "截图API",
|
||||
@@ -156,7 +205,7 @@
|
||||
"Domains": "域名管理",
|
||||
"Users": "用户管理",
|
||||
"URLs": "短链管理",
|
||||
"Records": "DNS 记录管理",
|
||||
"Records": "子域名管理",
|
||||
"OPTIONS": "选项",
|
||||
"Settings": "账户设置",
|
||||
"Documentation": "使用文档",
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user