Role-aware registration: Customer/Employee/Owner/Contractor/Sub-Con/Vendor
- New "Your role" options matching the portal roles - Customer/Owner = self; Employee shows an Employee ID field; Contractor/Sub-Con/Vendor show their ID + property owner name - Validation adapts per role Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -75,11 +75,12 @@ export const countryCodes: CountryCode[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const relationshipOptions = [
|
export const relationshipOptions = [
|
||||||
"Homeowner",
|
"Customer",
|
||||||
|
"Employee",
|
||||||
|
"Owner",
|
||||||
"Contractor",
|
"Contractor",
|
||||||
"Property Manager",
|
"Sub-Con",
|
||||||
"Tenant",
|
"Vendor",
|
||||||
"Authorized Representative",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const sectors = [
|
export const sectors = [
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function RegisterFlow() {
|
|||||||
const [cc, setCc] = useState("+1");
|
const [cc, setCc] = useState("+1");
|
||||||
const [phone, setPhone] = useState("");
|
const [phone, setPhone] = useState("");
|
||||||
const [pw, setPw] = useState("");
|
const [pw, setPw] = useState("");
|
||||||
const [relationship, setRelationship] = useState("Homeowner");
|
const [relationship, setRelationship] = useState("Customer");
|
||||||
const [alloeNo, setAlloeNo] = useState("");
|
const [alloeNo, setAlloeNo] = useState("");
|
||||||
const [alloeFirst, setAlloeFirst] = useState("");
|
const [alloeFirst, setAlloeFirst] = useState("");
|
||||||
const [alloeLast, setAlloeLast] = useState("");
|
const [alloeLast, setAlloeLast] = useState("");
|
||||||
@@ -42,15 +42,16 @@ export function RegisterFlow() {
|
|||||||
const [emailVerified, setEmailVerified] = useState(false);
|
const [emailVerified, setEmailVerified] = useState(false);
|
||||||
const [phoneVerified, setPhoneVerified] = useState(false);
|
const [phoneVerified, setPhoneVerified] = useState(false);
|
||||||
|
|
||||||
const isSelf = relationship === "Homeowner";
|
const isSelf = relationship === "Customer" || relationship === "Owner";
|
||||||
|
const isEmployee = relationship === "Employee";
|
||||||
const country = countryCodes.find((c) => c.code === cc)!;
|
const country = countryCodes.find((c) => c.code === cc)!;
|
||||||
const phoneOk = phone.replace(/\D/g, "").length === country.digits;
|
const phoneOk = phone.replace(/\D/g, "").length === country.digits;
|
||||||
const pwOk = sso ? true : passwordStrength(pw).score >= 3;
|
const pwOk = sso ? true : passwordStrength(pw).score >= 3;
|
||||||
const alloeIdOk = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/.test(alloeNo);
|
const alloeIdOk = /^(?=.*[a-zA-Z])(?=.*\d).{4,}$/.test(alloeNo);
|
||||||
|
|
||||||
const step0Valid =
|
const step0Valid =
|
||||||
first.trim() && last.trim() && EMAIL_RE.test(sso?.email || email) && phoneOk && pwOk &&
|
first.trim() && last.trim() && EMAIL_RE.test(sso?.email || email) && phoneOk && pwOk &&
|
||||||
termsOk && privacyOk && (isSelf || (alloeIdOk && alloeFirst.trim() && alloeLast.trim()));
|
termsOk && privacyOk && (isSelf || (alloeIdOk && (isEmployee || (alloeFirst.trim() && alloeLast.trim()))));
|
||||||
|
|
||||||
const step1Valid = /^LUP-\d{6}$/.test(allotment) || plotVerified;
|
const step1Valid = /^LUP-\d{6}$/.test(allotment) || plotVerified;
|
||||||
const step2Valid = emailVerified && phoneVerified;
|
const step2Valid = emailVerified && phoneVerified;
|
||||||
@@ -221,26 +222,38 @@ function StepAccount(p: {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{p.isSelf ? (
|
{p.isSelf ? (
|
||||||
<div style={{ marginTop: 12 }}><FlashNote tone="success">Homeowner account — you own this property.</FlashNote></div>
|
<div style={{ marginTop: 12 }}><FlashNote tone="success">{p.relationship === "Owner" ? "Owner" : "Customer"} account — you own this property.</FlashNote></div>
|
||||||
) : (
|
) : (() => {
|
||||||
|
const isEmployee = p.relationship === "Employee";
|
||||||
|
const cfg = ({
|
||||||
|
Employee: { banner: "Registering as a LynkedUp Pro team member.", title: "Employee details", idLabel: "Employee ID", idPh: "EMP-12345" },
|
||||||
|
Contractor:{ banner: "Registering on behalf of the property owner.", title: "Contractor details", idLabel: "Contractor ID", idPh: "Alphanumeric ID" },
|
||||||
|
"Sub-Con": { banner: "Registering on behalf of the property owner.", title: "Sub-contractor details", idLabel: "Sub-contractor ID", idPh: "Alphanumeric ID" },
|
||||||
|
Vendor: { banner: "Registering on behalf of the property owner.", title: "Vendor details", idLabel: "Vendor ID", idPh: "Alphanumeric ID" },
|
||||||
|
} as Record<string, { banner: string; title: string; idLabel: string; idPh: string }>)[p.relationship]
|
||||||
|
?? { banner: "Registering on behalf of the property owner.", title: "Property Owner Details", idLabel: "Owner / Property ID", idPh: "Alphanumeric ID" };
|
||||||
|
return (
|
||||||
<div style={{ marginTop: 12 }}>
|
<div style={{ marginTop: 12 }}>
|
||||||
<FlashNote tone="info">Registering on behalf of the property owner.</FlashNote>
|
<FlashNote tone="info">{cfg.banner}</FlashNote>
|
||||||
<div className="dashed-block" style={{ marginTop: 12 }}>
|
<div className="dashed-block" style={{ marginTop: 12 }}>
|
||||||
<div className="row between" style={{ marginBottom: 12 }}>
|
<div className="row between" style={{ marginBottom: 12 }}>
|
||||||
<strong style={{ fontSize: 13.5 }}>Property Owner Details</strong>
|
<strong style={{ fontSize: 13.5 }}>{cfg.title}</strong>
|
||||||
{p.alloeNo && (p.alloeIdOk ? <Badge tone="green"><Icon name="check" size={12} /> Valid</Badge> : <Badge tone="gray">Checking…</Badge>)}
|
{p.alloeNo && (p.alloeIdOk ? <Badge tone="green"><Icon name="check" size={12} /> Valid</Badge> : <Badge tone="gray">Checking…</Badge>)}
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label className="label">Owner / Property ID</label>
|
<label className="label">{cfg.idLabel}</label>
|
||||||
<input className="input" value={p.alloeNo} onChange={(e) => p.setAlloeNo(e.target.value.toUpperCase())} placeholder="Alphanumeric ID" />
|
<input className="input" value={p.alloeNo} onChange={(e) => p.setAlloeNo(e.target.value.toUpperCase())} placeholder={cfg.idPh} />
|
||||||
</div>
|
</div>
|
||||||
|
{!isEmployee && (
|
||||||
<div className="row gap-3" style={{ marginTop: 12 }}>
|
<div className="row gap-3" style={{ marginTop: 12 }}>
|
||||||
<div className="field grow"><label className="label">Owner first name</label><input className="input" value={p.alloeFirst} onChange={(e) => p.setAlloeFirst(e.target.value)} /></div>
|
<div className="field grow"><label className="label">Owner first name</label><input className="input" value={p.alloeFirst} onChange={(e) => p.setAlloeFirst(e.target.value)} /></div>
|
||||||
<div className="field grow"><label className="label">Owner last name</label><input className="input" value={p.alloeLast} onChange={(e) => p.setAlloeLast(e.target.value)} /></div>
|
<div className="field grow"><label className="label">Owner last name</label><input className="input" value={p.alloeLast} onChange={(e) => p.setAlloeLast(e.target.value)} /></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
|
||||||
<div className="col gap-2" style={{ marginTop: 16 }}>
|
<div className="col gap-2" style={{ marginTop: 16 }}>
|
||||||
<label className="check-row">
|
<label className="check-row">
|
||||||
|
|||||||
Reference in New Issue
Block a user