chamira-hashan / frontend /src /components /admin /BasicProfileForm.tsx
hashan-7's picture
Polish portfolio UI and chatbot interactions
cb35b04
Raw
History Blame Contribute Delete
5.39 kB
import { useState, type ChangeEvent } from 'react';
import { uploadMedia } from '../../services/api';
import type { FullProfile } from '../../types';
interface BasicProfileFormProps {
profile: FullProfile;
onChange: (profile: FullProfile) => void;
}
function BasicProfileForm({ profile, onChange }: BasicProfileFormProps) {
const [isUploading, setIsUploading] = useState(false);
const [uploadMessage, setUploadMessage] = useState('');
const [uploadError, setUploadError] = useState('');
const updateField = (field: keyof FullProfile, value: string | undefined) => {
onChange({
...profile,
[field]: value,
});
};
const handleProfileImageUpload = async (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
event.target.value = '';
if (!file) return;
setIsUploading(true);
setUploadMessage('');
setUploadError('');
try {
const paths = await uploadMedia(file);
const imagePath = paths.find((path) => /\.(png|jpg|jpeg|webp|gif)$/i.test(path));
if (!imagePath) {
setUploadError('Upload completed, but no image path was returned.');
return;
}
updateField('profile_image_path', imagePath);
setUploadMessage('Profile image uploaded and selected.');
} catch (error) {
setUploadError(error instanceof Error ? error.message : 'Failed to upload profile image.');
} finally {
setIsUploading(false);
}
};
return (
<section className="admin-section clean">
<div className="admin-form-toolbar">
<div>
<h2>Basic Profile</h2>
<p className="admin-muted">
Edit the public identity, profile image, title, location, tagline, and bio.
</p>
</div>
</div>
<div className="admin-profile-image-panel">
<div>
<h3>Profile Image</h3>
<p className="admin-muted">
Upload a portrait image. The saved path appears in the public hero section.
</p>
<div className="admin-profile-image-actions">
<label className="admin-file-button">
{isUploading ? 'Uploading...' : 'Upload Profile Image'}
<input
type="file"
accept="image/png,image/jpeg,image/webp,image/gif"
onChange={handleProfileImageUpload}
disabled={isUploading}
/>
</label>
<button
className="admin-secondary-button"
type="button"
onClick={() => updateField('profile_image_path', '')}
disabled={!profile.profile_image_path}
>
Remove Image
</button>
</div>
<label className="admin-image-path-field">
Profile Image Path
<input
type="text"
value={profile.profile_image_path ?? ''}
onChange={(event) => updateField('profile_image_path', event.target.value)}
placeholder="/media/projects/images/profile.webp"
/>
</label>
</div>
<div className="admin-profile-image-preview">
{profile.profile_image_path ? <img src={profile.profile_image_path} alt="Profile preview" /> : <span>H7</span>}
</div>
</div>
{uploadMessage && <p className="admin-success">{uploadMessage}</p>}
{uploadError && <p className="admin-error">{uploadError}</p>}
<div className="admin-form-grid">
<label>
Internal Full Name
<input
type="text"
value={profile.name ?? ''}
onChange={(event) => updateField('name', event.target.value)}
placeholder="H. K. Chamira Hashan"
/>
</label>
<label>
Public Display Name
<input
type="text"
value={profile.display_name ?? ''}
onChange={(event) => updateField('display_name', event.target.value)}
placeholder="Chamira Hashan"
/>
</label>
<label className="wide">
Role / Title
<input
type="text"
value={profile.role ?? ''}
onChange={(event) => updateField('role', event.target.value)}
placeholder="Aspiring Backend & AI/ML Developer | Software Engineering Background"
/>
</label>
<label>
Location
<input
type="text"
value={profile.location ?? ''}
onChange={(event) => updateField('location', event.target.value)}
placeholder="Sri Lanka"
/>
</label>
<label>
Tagline
<input
type="text"
value={profile.tagline ?? ''}
onChange={(event) => updateField('tagline', event.target.value)}
placeholder="Building practical backend, AI/ML, and full-stack software projects."
/>
</label>
<label className="wide">
Bio
<textarea
className="admin-json-editor compact tall"
value={profile.bio ?? ''}
onChange={(event) => updateField('bio', event.target.value)}
placeholder="Short professional bio..."
/>
</label>
</div>
</section>
);
}
export default BasicProfileForm;