Add comment field to user profile in admin panel

- Prisma: User.comment String? column + migration
- UserContactEditor: comment shown in view mode, textarea in edit mode
- updateUserContact action: saves comment to DB
This commit is contained in:
2026-05-06 14:06:53 +00:00
parent 4f3b389f05
commit 48721759d3
5 changed files with 68 additions and 29 deletions
+62 -28
View File
@@ -14,8 +14,10 @@ const inputStyle = {
} as React.CSSProperties;
const focusHandlers = {
onFocus: (e: React.FocusEvent<HTMLInputElement>) => (e.currentTarget.style.borderColor = "var(--foreground)"),
onBlur: (e: React.FocusEvent<HTMLInputElement>) => (e.currentTarget.style.borderColor = "var(--border)"),
onFocus: (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) =>
(e.currentTarget.style.borderColor = "var(--foreground)"),
onBlur: (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) =>
(e.currentTarget.style.borderColor = "var(--border)"),
};
interface Props {
@@ -24,9 +26,10 @@ interface Props {
email: string;
phone: string | null;
birthday: Date | null;
comment: string | null;
}
export function UserContactEditor({ userId, name, email, phone, birthday }: Props) {
export function UserContactEditor({ userId, name, email, phone, birthday, comment }: Props) {
const [editing, setEditing] = useState(false);
const [nameVal, setNameVal] = useState(name);
const [emailVal, setEmailVal] = useState(email);
@@ -34,42 +37,59 @@ export function UserContactEditor({ userId, name, email, phone, birthday }: Prop
const [birthdayVal, setBirthdayVal] = useState(
birthday ? birthday.toISOString().slice(0, 10) : ""
);
const [commentVal, setCommentVal] = useState(comment ?? "");
const [pending, startTransition] = useTransition();
function handleSave() {
startTransition(async () => {
await updateUserContact(userId, { name: nameVal, email: emailVal, phone: phoneVal, birthday: birthdayVal });
await updateUserContact(userId, {
name: nameVal,
email: emailVal,
phone: phoneVal,
birthday: birthdayVal,
comment: commentVal,
});
setEditing(false);
});
}
if (!editing) {
return (
<div className="flex items-start gap-6 flex-wrap">
<div className="space-y-0.5">
<p className="text-xs uppercase tracking-widest font-bold" style={{ color: "var(--muted-foreground)" }}>
Телефон
</p>
<p className="text-sm">{phone || "—"}</p>
<div className="space-y-3">
<div className="flex items-start gap-6 flex-wrap">
<div className="space-y-0.5">
<p className="text-xs uppercase tracking-widest font-bold" style={{ color: "var(--muted-foreground)" }}>
Телефон
</p>
<p className="text-sm">{phone || "—"}</p>
</div>
<div className="space-y-0.5">
<p className="text-xs uppercase tracking-widest font-bold" style={{ color: "var(--muted-foreground)" }}>
День рождения
</p>
<p className="text-sm">
{birthday
? new Date(birthday).toLocaleDateString("ru-RU", { day: "numeric", month: "long", year: "numeric" })
: "—"}
</p>
</div>
<button
type="button"
onClick={() => setEditing(true)}
className="text-xs underline self-end pb-0.5"
style={{ color: "var(--muted-foreground)" }}
>
Изменить
</button>
</div>
<div className="space-y-0.5">
<p className="text-xs uppercase tracking-widest font-bold" style={{ color: "var(--muted-foreground)" }}>
День рождения
</p>
<p className="text-sm">
{birthday
? new Date(birthday).toLocaleDateString("ru-RU", { day: "numeric", month: "long", year: "numeric" })
: "—"}
</p>
</div>
<button
type="button"
onClick={() => setEditing(true)}
className="text-xs underline self-end pb-0.5"
style={{ color: "var(--muted-foreground)" }}
>
Изменить
</button>
{comment && (
<div className="space-y-0.5">
<p className="text-xs uppercase tracking-widest font-bold" style={{ color: "var(--muted-foreground)" }}>
Комментарий
</p>
<p className="text-sm whitespace-pre-wrap" style={{ color: "var(--foreground)" }}>{comment}</p>
</div>
)}
</div>
);
}
@@ -127,6 +147,20 @@ export function UserContactEditor({ userId, name, email, phone, birthday }: Prop
/>
</div>
</div>
<div className="space-y-1">
<label className="text-xs uppercase tracking-widest font-bold" style={{ color: "var(--muted-foreground)" }}>
Комментарий
</label>
<textarea
value={commentVal}
onChange={(e) => setCommentVal(e.target.value)}
rows={3}
placeholder="Заметки об этом пользователе..."
style={{ ...inputStyle, resize: "vertical" }}
onFocus={(e) => (e.currentTarget.style.borderColor = "var(--foreground)")}
onBlur={(e) => (e.currentTarget.style.borderColor = "var(--border)")}
/>
</div>
<div className="flex gap-2">
<button
type="button"