setting, session and profile

This commit is contained in:
debu-2 2026-10-07 03:19:09 +05:30
parent 48eea5d201
commit 4a563f4f34
31 changed files with 1797 additions and 159 deletions

View file

@ -0,0 +1,132 @@
import { Camera, UserRound } from "lucide-react";
import { useRef, useState } from "react";
import { updateAccountProfile } from "../api";
const MAX_AVATAR_BYTES = 512 * 1024;
const ALLOWED_TYPES = new Set(["image/jpeg", "image/png", "image/webp"]);
function accountInitials(account) {
return `${account?.first_name?.[0] || ""}${account?.last_name?.[0] || ""}`.toUpperCase() || "AG";
}
export default function AccountProfile({ account, onUpdated }) {
const token = sessionStorage.getItem("agmail_access_token");
const inputRef = useRef(null);
const [status, setStatus] = useState({ loading: false, error: "", notice: "" });
async function handleFileChange(event) {
const file = event.target.files?.[0];
event.target.value = "";
if (!file) return;
if (!ALLOWED_TYPES.has(file.type)) {
setStatus({ loading: false, error: "Use a JPG, PNG, or WebP image.", notice: "" });
return;
}
if (file.size > MAX_AVATAR_BYTES) {
setStatus({ loading: false, error: "Profile picture must be 512 KB or smaller.", notice: "" });
return;
}
setStatus({ loading: true, error: "", notice: "" });
try {
const dataUrl = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(String(reader.result || ""));
reader.onerror = () => reject(new Error("Could not read that image."));
reader.readAsDataURL(file);
});
const updated = await updateAccountProfile({ profile_picture_url: dataUrl }, token);
sessionStorage.setItem("agmail_account", JSON.stringify(updated));
onUpdated?.(updated);
setStatus({ loading: false, error: "", notice: "Profile picture updated." });
} catch (error) {
setStatus({ loading: false, error: error.message, notice: "" });
}
}
async function removePhoto() {
setStatus({ loading: true, error: "", notice: "" });
try {
const updated = await updateAccountProfile({ profile_picture_url: null }, token);
sessionStorage.setItem("agmail_account", JSON.stringify(updated));
onUpdated?.(updated);
setStatus({ loading: false, error: "", notice: "Profile picture removed." });
} catch (error) {
setStatus({ loading: false, error: error.message, notice: "" });
}
}
return (
<section className="account-profile-page">
<header className="account-profile-head">
<div>
<p className="account-profile-eyebrow">Account</p>
<h1>Profile</h1>
<p>Update how you appear across Gentic.</p>
</div>
</header>
<div className="account-profile-card">
<div className="account-profile-avatar-block">
<div className="account-profile-avatar">
{account?.profile_picture_url ? (
<img src={account.profile_picture_url} alt="" />
) : (
<span>{accountInitials(account)}</span>
)}
</div>
<div className="account-profile-avatar-actions">
<button
type="button"
className="account-profile-btn"
disabled={status.loading}
onClick={() => inputRef.current?.click()}
>
<Camera size={16} />
{account?.profile_picture_url ? "Change photo" : "Upload photo"}
</button>
{account?.profile_picture_url && (
<button
type="button"
className="account-profile-btn subtle"
disabled={status.loading}
onClick={removePhoto}
>
Remove
</button>
)}
<input
ref={inputRef}
type="file"
accept="image/jpeg,image/png,image/webp"
hidden
onChange={handleFileChange}
/>
</div>
</div>
<div className="account-profile-fields">
<label>
<span>Name</span>
<div className="account-profile-readonly">
<UserRound size={16} />
{account?.first_name} {account?.last_name}
</div>
</label>
<label>
<span>Email</span>
<div className="account-profile-readonly">{account?.email}</div>
</label>
<label>
<span>Phone</span>
<div className="account-profile-readonly">{account?.phone_number || "Not set"}</div>
</label>
</div>
{status.notice && <p className="account-profile-notice" role="status">{status.notice}</p>}
{status.error && <p className="form-error" role="alert">{status.error}</p>}
</div>
</section>
);
}