fix: text color inversion on dark theme(#79)
This commit is contained in:
@@ -308,8 +308,8 @@ export default function AppConfigs({}: {}) {
|
||||
{configs && (
|
||||
<div className="flex w-full items-start gap-2">
|
||||
<Textarea
|
||||
className="h-16 max-h-32 min-h-9 resize-y bg-white"
|
||||
placeholder="Support HTML format, such as <div>info</div>"
|
||||
className="h-16 max-h-32 min-h-9 resize-y bg-white dark:bg-neutral-700"
|
||||
placeholder="Support HTML format, such as <div class='text-red-500'>Info</div>"
|
||||
rows={5}
|
||||
// defaultValue={configs.system_notification}
|
||||
value={notification}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { constructMetadata } from "@/lib/utils";
|
||||
import { DeleteAccountSection } from "@/components/dashboard/delete-account";
|
||||
import { DashboardHeader } from "@/components/dashboard/header";
|
||||
import { UserApiKeyForm } from "@/components/forms/user-api-key-form";
|
||||
import { UserEmailForm } from "@/components/forms/user-email-form";
|
||||
import { UserNameForm } from "@/components/forms/user-name-form";
|
||||
import { UserPasswordForm } from "@/components/forms/user-password-form";
|
||||
import { UserRoleForm } from "@/components/forms/user-role-form";
|
||||
@@ -26,6 +27,14 @@ export default async function SettingsPage() {
|
||||
text="Manage account and website settings"
|
||||
/>
|
||||
<div className="divide-y divide-muted pb-10">
|
||||
<UserEmailForm
|
||||
user={{
|
||||
id: user.id,
|
||||
name: user.name || "",
|
||||
email: user.email || "",
|
||||
emailVerified: user.emailVerified,
|
||||
}}
|
||||
/>
|
||||
<UserNameForm user={{ id: user.id, name: user.name || "" }} />
|
||||
{user.role === "ADMIN" && (
|
||||
<UserRoleForm user={{ id: user.id, role: user.role }} />
|
||||
|
||||
3
auth.ts
3
auth.ts
@@ -14,6 +14,7 @@ declare module "next-auth" {
|
||||
team: string;
|
||||
active: number;
|
||||
apiKey: string;
|
||||
emailVerified: Date;
|
||||
} & DefaultSession["user"];
|
||||
}
|
||||
}
|
||||
@@ -49,6 +50,7 @@ export const {
|
||||
session.user.active = token.active as number;
|
||||
session.user.team = token.team as string;
|
||||
session.user.apiKey = token.apiKey as string;
|
||||
session.user.emailVerified = token.emailVerified as Date;
|
||||
}
|
||||
|
||||
return session;
|
||||
@@ -67,6 +69,7 @@ export const {
|
||||
token.active = dbUser.active;
|
||||
token.team = dbUser.team || "free";
|
||||
token.apiKey = dbUser.apiKey;
|
||||
token.emailVerified = dbUser.emailVerified;
|
||||
|
||||
return token;
|
||||
},
|
||||
|
||||
130
components/forms/user-email-form.tsx
Normal file
130
components/forms/user-email-form.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useTransition } from "react";
|
||||
import { updateUserName } from "@/actions/update-user-name";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { User } from "@prisma/client";
|
||||
import { format } from "date-fns";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { formatDate, formatTime } from "@/lib/utils";
|
||||
import { userEmailSchema } from "@/lib/validations/user";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { SectionColumns } from "@/components/dashboard/section-columns";
|
||||
import { Icons } from "@/components/shared/icons";
|
||||
|
||||
import { TimeAgoIntl } from "../shared/time-ago";
|
||||
import { Badge } from "../ui/badge";
|
||||
|
||||
interface UserEmailFormProps {
|
||||
user: Pick<User, "id" | "name" | "email" | "emailVerified">;
|
||||
}
|
||||
|
||||
export type FormData = {
|
||||
email: string;
|
||||
};
|
||||
|
||||
export function UserEmailForm({ user }: UserEmailFormProps) {
|
||||
const { update } = useSession();
|
||||
const [updated, setUpdated] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const updateUserNameWithId = updateUserName.bind(null, user.id);
|
||||
const userEmailVerified = new Date(user.emailVerified || "").getSeconds();
|
||||
|
||||
const t = useTranslations("Setting");
|
||||
|
||||
const checkUpdate = (value) => {
|
||||
setUpdated(user.email !== value);
|
||||
};
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: { errors },
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(userEmailSchema),
|
||||
defaultValues: {
|
||||
email: user?.email || "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
startTransition(async () => {
|
||||
const { status } = await updateUserNameWithId(data);
|
||||
|
||||
if (status !== "success") {
|
||||
toast.error("Something went wrong.", {
|
||||
description: "Your name was not updated. Please try again.",
|
||||
});
|
||||
} else {
|
||||
await update();
|
||||
setUpdated(false);
|
||||
toast.success("Your name has been updated.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<SectionColumns
|
||||
title={t("Your Account Email")}
|
||||
description={t("Bind your account to an email address")}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="email">
|
||||
{t("Email")}
|
||||
</Label>
|
||||
<Input
|
||||
id="email"
|
||||
className="flex-1"
|
||||
size={32}
|
||||
{...register("email")}
|
||||
disabled
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant={updated ? "blue" : "disable"}
|
||||
disabled={isPending || !updated}
|
||||
className="h-9 w-[67px] shrink-0 px-0 sm:w-[130px]"
|
||||
>
|
||||
{isPending ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>{t("Save")}</p>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-2 text-xs font-semibold">
|
||||
{user.emailVerified ? (
|
||||
<Badge variant={"default"}>
|
||||
<Icons.shieldUser className="mr-1 size-4 text-green-600" />
|
||||
{t("Verified at {date}", {
|
||||
date: format(
|
||||
new Date(user.emailVerified),
|
||||
"MM/dd/yyyy HH:mm:ss",
|
||||
),
|
||||
})}
|
||||
</Badge>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant={"secondary"}>{t("Unverified")}</Badge>
|
||||
{/* <p className="ml-2">点击验证</p> */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.email && (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.email.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</SectionColumns>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -120,6 +120,24 @@ export const Icons = {
|
||||
layers: Layers,
|
||||
databaseZap: DatabaseZap,
|
||||
boxes: Boxes,
|
||||
shieldUser: ({ ...props }: LucideProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
{...props}
|
||||
>
|
||||
<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
|
||||
<path d="M6.376 18.91a6 6 0 0 1 11.249.003" />
|
||||
<circle cx="12" cy="11" r="4" />
|
||||
</svg>
|
||||
),
|
||||
cloudUpload: ({ ...props }: LucideProps) => (
|
||||
<svg
|
||||
role="presentation"
|
||||
|
||||
@@ -5,6 +5,10 @@ export const userNameSchema = z.object({
|
||||
name: z.string().min(3).max(32),
|
||||
});
|
||||
|
||||
export const userEmailSchema = z.object({
|
||||
email: z.string().min(3).email(),
|
||||
});
|
||||
|
||||
export const userRoleSchema = z.object({
|
||||
role: z.nativeEnum(UserRole),
|
||||
});
|
||||
|
||||
@@ -778,6 +778,11 @@
|
||||
"Forward Email Targets": "Forward Email Targets",
|
||||
"Set forward email address targets, split by comma if more than one, such as: 1@a-com,2@b-com, Only works when email forwarding is enabled": "Set forward email address targets, split by comma if more than one, such as: 1@a.com,2@b.com, Only works when email forwarding is enabled",
|
||||
"Email Forward White List": "Email Forward White List",
|
||||
"Set email forward white list, split by comma, such as: a-wrdo,b-wrdo": "Set email forward white list (Catch-All and Email Forwarding are both enabled), split by comma, such as: a@wr.do,b@wr.do; If not set, will forward all emails"
|
||||
"Set email forward white list, split by comma, such as: a-wrdo,b-wrdo": "Set email forward white list (Catch-All and Email Forwarding are both enabled), split by comma, such as: a@wr.do,b@wr.do; If not set, will forward all emails",
|
||||
"Your Account Email": "Your Account Email",
|
||||
"Bind your account to an email address": "Bind your account to an email address",
|
||||
"Email": "Email",
|
||||
"Unverified": "Unverified",
|
||||
"Verified at {date}": "Verified at {date}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,6 +777,11 @@
|
||||
"Forward Email Targets": "目标收件箱",
|
||||
"Set forward email address targets, split by comma if more than one, such as: 1@a-com,2@b-com, Only works when email forwarding is enabled": "设置转发目标邮箱,多个邮件地址请用逗号分隔,例如:1@a.com,2@b.com,仅在启用邮件转发时生效",
|
||||
"Email Forward White List": "转发邮箱白名单",
|
||||
"Set email forward white list, split by comma, such as: a-wrdo,b-wrdo": "设置转发邮箱白名单(Catch-All 和 Email Forwarding 同时生效),多个邮箱地址请用逗号分隔,例如:a@wr.do,b@wr.do; 若不设置,则转发所有邮件"
|
||||
"Set email forward white list, split by comma, such as: a-wrdo,b-wrdo": "设置转发邮箱白名单(Catch-All 和 Email Forwarding 同时生效),多个邮箱地址请用逗号分隔,例如:a@wr.do,b@wr.do; 若不设置,则转发所有邮件",
|
||||
"Your Account Email": "账户邮箱",
|
||||
"Bind your account to an email address": "为你的账户绑定一个邮箱",
|
||||
"Unverified": "未验证邮箱",
|
||||
"Email": "邮箱",
|
||||
"Verified at {date}": "已于 {date} 验证"
|
||||
}
|
||||
}
|
||||
|
||||
102
public/sw.js
102
public/sw.js
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 it is too large
Load Diff
File diff suppressed because one or more lines are too long
1
public/workbox-e9849328.js
Normal file
1
public/workbox-e9849328.js
Normal file
File diff suppressed because one or more lines are too long
4
types/next-auth.d.ts
vendored
4
types/next-auth.d.ts
vendored
@@ -4,6 +4,10 @@ import { JWT } from "next-auth/jwt";
|
||||
|
||||
export type ExtendedUser = User & {
|
||||
role: UserRole;
|
||||
team: string;
|
||||
active: number;
|
||||
apiKey: string;
|
||||
emailVerified: string;
|
||||
};
|
||||
|
||||
declare module "next-auth/jwt" {
|
||||
|
||||
Reference in New Issue
Block a user