feat: profile about me sections

This commit is contained in:
trafficlunar 2025-11-10 19:20:33 +00:00
parent df1cdc67e9
commit 46a168b56c
10 changed files with 202 additions and 90 deletions

View file

@ -9,18 +9,48 @@ import { displayNameSchema, usernameSchema } from "@/lib/schemas";
import ProfilePictureSettings from "./profile-picture";
import SubmitDialogButton from "./submit-dialog-button";
import DeleteAccount from "./delete-account";
import z from "zod";
export default function ProfileSettings() {
interface Props {
currentDescription: string | null | undefined;
}
export default function ProfileSettings({ currentDescription }: Props) {
const router = useRouter();
const [description, setDescription] = useState(currentDescription);
const [displayName, setDisplayName] = useState("");
const [username, setUsername] = useState("");
const [descriptionChangeError, setDescriptionChangeError] = useState<string | undefined>(undefined);
const [displayNameChangeError, setDisplayNameChangeError] = useState<string | undefined>(undefined);
const [usernameChangeError, setUsernameChangeError] = useState<string | undefined>(undefined);
const usernameDate = dayjs().add(90, "days");
const handleSubmitDescriptionChange = async (close: () => void) => {
const parsed = z.string().trim().max(256).safeParse(description);
if (!parsed.success) {
setDescriptionChangeError(parsed.error.issues[0].message);
return;
}
const response = await fetch("/api/auth/about-me", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ description }),
});
if (!response.ok) {
const { error } = await response.json();
setDescriptionChangeError(error);
return;
}
close();
router.refresh();
};
const handleSubmitDisplayNameChange = async (close: () => void) => {
const parsed = displayNameSchema.safeParse(displayName);
if (!parsed.success) {
@ -84,17 +114,46 @@ export default function ProfileSettings() {
{/* Profile Picture */}
<ProfilePictureSettings />
{/* Description */}
<div className="grid grid-cols-5 gap-4 max-lg:grid-cols-1">
<div className="col-span-3">
<label className="font-semibold">About Me</label>
<p className="text-sm text-zinc-500">Write about yourself on your profile</p>
</div>
<div className="flex justify-end gap-1 h-min col-span-2">
<div className="flex-1">
<textarea
rows={5}
maxLength={256}
placeholder="(optional) Type about yourself..."
className="pill input rounded-xl! resize-none text-sm w-full"
value={description || ""}
onChange={(e) => setDescription(e.target.value)}
/>
<p className="text-xs text-zinc-400 mt-1 text-right">{(description || "").length}/256</p>
</div>
<SubmitDialogButton
title="Confirm About Me Change"
description="Are you sure? You can change it again later."
error={descriptionChangeError}
onSubmit={handleSubmitDescriptionChange}
/>
</div>
</div>
{/* Change Name */}
<div className="grid grid-cols-2 gap-4 max-lg:grid-cols-1">
<div>
<div className="grid grid-cols-5 gap-4 max-lg:grid-cols-1">
<div className="col-span-3">
<label className="font-semibold">Change Display Name</label>
<p className="text-sm text-zinc-500">This is a display name shown on your profile feel free to change it anytime</p>
</div>
<div className="flex justify-end gap-1 h-min">
<div className="flex justify-end gap-1 h-min col-span-2">
<input
type="text"
className="pill input w-full max-w-64"
className="pill input flex-1"
placeholder="Type here..."
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
@ -114,14 +173,14 @@ export default function ProfileSettings() {
</div>
{/* Change Username */}
<div className="grid grid-cols-2 gap-4 max-lg:grid-cols-1">
<div>
<div className="grid grid-cols-5 gap-4 max-lg:grid-cols-1">
<div className="col-span-3">
<label className="font-semibold">Change Username</label>
<p className="text-sm text-zinc-500">Your unique tag on the site. Can only be changed once every 90 days</p>
</div>
<div className="flex justify-end gap-1">
<div className="relative w-full max-w-64">
<div className="flex justify-end gap-1 col-span-2">
<div className="relative flex-1">
<input
type="text"
className="pill input w-full indent-4"

View file

@ -43,13 +43,13 @@ export default function ProfilePictureSettings() {
}, []);
return (
<div className="grid grid-cols-2">
<div>
<div className="grid grid-cols-5 gap-4 max-lg:grid-cols-1">
<div className="col-span-3">
<label className="font-semibold">Profile Picture</label>
<p className="text-sm text-zinc-500">Manage your profile picture. Can only be changed once every 7 days.</p>
</div>
<div className="flex flex-col">
<div className="flex flex-col col-span-2">
<div className="flex justify-end">
<Dropzone onDrop={handleDrop} options={{ maxFiles: 1 }}>
<p className="text-center text-xs">