pelagia-portal/App/app/(portal)/profile/signature-uploader.tsx
2026-05-18 23:18:58 +05:30

128 lines
4.3 KiB
TypeScript

"use client";
import { useRef, useState } from "react";
import { saveSignature, removeSignature } from "./actions";
import { Upload, X } from "lucide-react";
interface Props {
currentSignatureUrl: string | null;
}
export function SignatureUploader({ currentSignatureUrl }: Props) {
const inputRef = useRef<HTMLInputElement>(null);
const [preview, setPreview] = useState<string | null>(null);
const [pending, setPending] = useState(false);
const [removing, setRemoving] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState("");
function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0];
if (!file) return;
setError("");
setSuccess("");
setPreview(URL.createObjectURL(file));
}
async function handleUpload(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const file = inputRef.current?.files?.[0];
if (!file) { setError("Please select a file first"); return; }
const fd = new FormData();
fd.append("signature", file);
setPending(true);
setError("");
setSuccess("");
const result = await saveSignature(fd);
setPending(false);
if ("error" in result) {
setError(result.error);
} else {
setSuccess("Signature saved successfully.");
setPreview(null);
if (inputRef.current) inputRef.current.value = "";
}
}
async function handleRemove() {
setRemoving(true);
setError("");
setSuccess("");
const result = await removeSignature();
setRemoving(false);
if ("error" in result) setError(result.error);
else setSuccess("Signature removed.");
}
const displayUrl = preview ?? currentSignatureUrl;
return (
<div className="space-y-4">
{displayUrl && (
<div className="rounded-lg border border-neutral-200 bg-neutral-50 p-4 inline-block">
<p className="text-xs font-medium text-neutral-500 mb-2">
{preview ? "Preview" : "Current signature"}
</p>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={displayUrl}
alt="Signature"
className="max-h-24 max-w-xs object-contain border border-neutral-200 rounded bg-white p-2"
/>
</div>
)}
<form onSubmit={handleUpload} className="space-y-3">
<div
className="relative rounded-lg border-2 border-dashed border-neutral-300 bg-neutral-50 p-6 text-center cursor-pointer hover:border-primary-400 hover:bg-primary-50 transition-colors"
onClick={() => inputRef.current?.click()}
>
<Upload className="mx-auto h-8 w-8 text-neutral-400 mb-2" />
<p className="text-sm text-neutral-600">
Click to select signature image
</p>
<p className="text-xs text-neutral-400 mt-1">PNG, JPG or WebP max 2 MB</p>
<input
ref={inputRef}
type="file"
name="signature"
accept="image/png,image/jpeg,image/jpg,image/webp"
onChange={handleFileChange}
className="sr-only"
/>
</div>
{error && (
<p className="text-sm text-danger-700 bg-danger-50 rounded-lg px-3 py-2">{error}</p>
)}
{success && (
<p className="text-sm text-success-700 bg-success-50 rounded-lg px-3 py-2">{success}</p>
)}
<div className="flex items-center gap-3">
<button
type="submit"
disabled={pending || !preview}
className="rounded-lg bg-primary-600 px-4 py-2 text-sm font-semibold text-white hover:bg-primary-700 disabled:opacity-50 transition-colors"
>
{pending ? "Saving…" : "Save Signature"}
</button>
{currentSignatureUrl && !preview && (
<button
type="button"
onClick={handleRemove}
disabled={removing}
className="inline-flex items-center gap-1.5 rounded-lg border border-danger-200 bg-white px-3 py-2 text-sm font-medium text-danger-700 hover:bg-danger-50 disabled:opacity-50 transition-colors"
>
<X className="h-3.5 w-3.5" />
{removing ? "Removing…" : "Remove"}
</button>
)}
</div>
</form>
</div>
);
}