pelagia-portal/App/app/(portal)/profile/change-password-form.tsx
Hardik eb402e03ef fix(profile+vendors): profile reachable for all roles incl SSO; submitter vendor creation
Profile (fixes Safari/SSO no-password redirect):
- User lookup falls back to email when JWT id is stale (SSO users)
- generateDownloadUrl wrapped in try/catch so storage never crashes the page
- Signature gate now uses approve_po permission (approvers only)
- SSO/no-password users see a Set Password form (current-password field hidden)

Vendors:
- New create_vendor permission for all PO roles incl. submitters
- Submitters create UNVERIFIED vendors (no Vendor ID); simple form mode
- verifyVendor action + Verify menu item (manage_vendors)
- Vendors auto-verify when a PO closes with them (receipt confirm + import)
- Add Vendor button on /inventory/vendors

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 18:53:33 +05:30

97 lines
3 KiB
TypeScript

"use client";
import { useState } from "react";
import { changePassword } from "./actions";
export function ChangePasswordForm({ hasPassword = true }: { hasPassword?: boolean }) {
const [success, setSuccess] = useState(false);
const [error, setError] = useState("");
const [pending, setPending] = useState(false);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const form = e.currentTarget;
const fd = new FormData(form);
const newPw = fd.get("newPassword") as string;
const confirmPw = fd.get("confirmPassword") as string;
if (newPw !== confirmPw) {
setError("New passwords do not match");
return;
}
setPending(true);
setSuccess(false);
setError("");
const result = await changePassword(fd);
setPending(false);
if ("error" in result) {
setError(result.error);
} else {
setSuccess(true);
form.reset();
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
{hasPassword && (
<div>
<label className="block text-sm font-medium text-neutral-700 mb-1.5">
Current password
</label>
<input
type="password"
name="currentPassword"
autoComplete="current-password"
className="w-full rounded-lg border border-neutral-300 px-3 py-2.5 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20"
/>
</div>
)}
<div>
<label className="block text-sm font-medium text-neutral-700 mb-1.5">
New password
</label>
<input
type="password"
name="newPassword"
required
minLength={8}
autoComplete="new-password"
className="w-full rounded-lg border border-neutral-300 px-3 py-2.5 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20"
/>
</div>
<div>
<label className="block text-sm font-medium text-neutral-700 mb-1.5">
Confirm new password
</label>
<input
type="password"
name="confirmPassword"
required
minLength={8}
autoComplete="new-password"
className="w-full rounded-lg border border-neutral-300 px-3 py-2.5 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20"
/>
</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">
Password changed successfully.
</p>
)}
<button
type="submit"
disabled={pending}
className="rounded-lg bg-primary-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-primary-700 disabled:opacity-60 transition-colors"
>
{pending ? "Saving…" : hasPassword ? "Change Password" : "Set Password"}
</button>
</form>
);
}