224 lines
6.4 KiB
TypeScript
Raw Normal View History

"use client";
import { UserIcon, PencilIcon, CheckIcon, XMarkIcon } from "@heroicons/react/24/outline";
import { Button } from "@/components/atoms/button";
import { Input } from "@/components/atoms/input";
/** Data required for displaying personal info card */
interface PersonalInfoData {
firstname: string | null | undefined;
lastname: string | null | undefined;
email: string;
phonenumber: string | null | undefined;
sfNumber: string | null | undefined;
dateOfBirth: string | null | undefined;
gender: string | null | undefined;
}
interface PersonalInfoCardProps {
/** User profile data including read-only fields */
data: PersonalInfoData;
/** Email value for editing (may differ from data.email during edit) */
editEmail: string;
/** Phone number value for editing (may differ from data.phonenumber during edit) */
editPhoneNumber: string;
isEditing: boolean;
isSaving: boolean;
onEdit: () => void;
onCancel: () => void;
onChange: (field: "email" | "phonenumber", value: string) => void;
onSave: () => void;
}
function ReadOnlyField({
label,
value,
hint,
}: {
label: string;
value: string | null | undefined;
hint: string;
}) {
return (
<div>
<label className="block text-sm font-medium text-muted-foreground mb-2">{label}</label>
<div className="bg-card rounded-lg p-4 border border-border shadow-sm">
<p className="text-base text-foreground font-medium">
{value || <span className="text-muted-foreground italic">Not provided</span>}
</p>
<p className="text-xs text-muted-foreground mt-2">{hint}</p>
</div>
</div>
);
}
const EDITABLE_INPUT_CLASS =
"block w-full px-4 py-2.5 border border-input rounded-lg bg-background text-foreground shadow-[var(--cp-shadow-1)] focus:outline-none focus:ring-2 focus:ring-ring focus:border-ring transition-colors";
function EditableEmailField({
email,
editEmail,
isEditing,
onChange,
}: {
email: string;
editEmail: string;
isEditing: boolean;
onChange: (field: "email" | "phonenumber", value: string) => void;
}) {
return (
<div className="sm:col-span-2">
<label className="block text-sm font-medium text-muted-foreground mb-2">Email Address</label>
{isEditing ? (
<Input
type="email"
value={editEmail}
onChange={e => onChange("email", e.target.value)}
className={EDITABLE_INPUT_CLASS}
/>
) : (
<div className="bg-card rounded-lg p-4 border border-border shadow-sm">
<p className="text-base text-foreground font-medium">{email}</p>
<p className="text-xs text-muted-foreground mt-2">
Email can be updated from the portal.
</p>
</div>
)}
</div>
);
}
function EditablePhoneField({
phonenumber,
editPhoneNumber,
isEditing,
onChange,
}: {
phonenumber: string | null | undefined;
editPhoneNumber: string;
isEditing: boolean;
onChange: (field: "email" | "phonenumber", value: string) => void;
}) {
return (
<div>
<label className="block text-sm font-medium text-muted-foreground mb-2">Phone Number</label>
{isEditing ? (
<Input
type="tel"
value={editPhoneNumber}
onChange={e => onChange("phonenumber", e.target.value)}
placeholder="+81 XX-XXXX-XXXX"
className={EDITABLE_INPUT_CLASS}
/>
) : (
<p className="text-base text-foreground py-2">
{phonenumber || <span className="text-muted-foreground italic">Not provided</span>}
</p>
)}
</div>
);
}
export function PersonalInfoCard({
data,
editEmail,
editPhoneNumber,
isEditing,
isSaving,
onEdit,
onCancel,
onChange,
onSave,
}: PersonalInfoCardProps) {
return (
<div className="bg-card text-card-foreground rounded-xl border border-border shadow-[var(--cp-shadow-1)]">
<div className="px-6 py-5 border-b border-border">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<UserIcon className="h-6 w-6 text-primary" />
<h2 className="text-xl font-semibold text-foreground">Personal Information</h2>
</div>
{!isEditing && (
<Button
variant="outline"
size="sm"
onClick={onEdit}
leftIcon={<PencilIcon className="h-4 w-4" />}
>
Edit
</Button>
)}
</div>
</div>
<div className="p-6">
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
<ReadOnlyField
label="First Name"
value={data.firstname}
hint="Name cannot be changed from the portal."
/>
<ReadOnlyField
label="Last Name"
value={data.lastname}
hint="Name cannot be changed from the portal."
/>
<EditableEmailField
email={data.email}
editEmail={editEmail}
isEditing={isEditing}
onChange={onChange}
/>
<ReadOnlyField
label="Customer Number"
value={data.sfNumber}
hint="Customer number is read-only."
/>
<ReadOnlyField
label="Date of Birth"
value={data.dateOfBirth}
hint="Date of birth is stored in billing profile."
/>
<EditablePhoneField
phonenumber={data.phonenumber}
editPhoneNumber={editPhoneNumber}
isEditing={isEditing}
onChange={onChange}
/>
<ReadOnlyField
label="Gender"
value={data.gender}
hint="Gender is stored in billing profile."
/>
</div>
{isEditing && (
<div className="flex items-center justify-end space-x-3 pt-6 border-t border-border mt-6">
<Button
variant="outline"
size="sm"
onClick={onCancel}
disabled={isSaving}
leftIcon={<XMarkIcon className="h-4 w-4" />}
>
Cancel
</Button>
<Button
size="sm"
onClick={onSave}
isLoading={isSaving}
leftIcon={isSaving ? undefined : <CheckIcon className="h-4 w-4" />}
>
{isSaving ? "Saving..." : "Save Changes"}
</Button>
</div>
)}
</div>
</div>
);
}