/* Willow Creek Portal — Resident experience: dashboard, payments, forms & signing. */
const { PIcon: RIcon, SignaturePad: RSignaturePad, PSection: RSection, KindBadge: RKindBadge, makeReceiptNo: rMakeReceiptNo, ReceiptModal: RReceiptModal } = window.WCPortal;
const { useState: rUseState } = React;
function ResidentPortal({ store, setStore, user, onLogout }) {
const { Card, Button, Badge, Alert, Input, Textarea, ProgressBar, Tabs } = window.WillowCreekDesignSystem_90378c;
const [tab, setTab] = rUseState("home");
const [activeForm, setActiveForm] = rUseState(null);
const [payOpen, setPayOpen] = rUseState(false);
const [receipt, setReceipt] = rUseState(null);
const resident = store.residents.find((r) => r.name === user) || store.residents[0];
const signed = store.signedForms[resident.name] || [];
const published = store.forms.filter((f) => f.status === "published");
const pendingForms = published.filter((f) => !signed.includes(f.id));
const myPayments = store.payments.filter((p) => p.resident === resident.name);
const rate = store.cms.weeklyRate;
const markSigned = (formId) => {
setStore((s) => {
const next = { ...s, signedForms: { ...s.signedForms, [resident.name]: [ ...(s.signedForms[resident.name] || []), formId ] } };
return next;
});
setActiveForm(null);
};
const recordPayment = (amount) => {
const paid = { id: "p" + Date.now(), resident: resident.name, amount, method: "Visa ··4242", status: "Paid", receiptNo: rMakeReceiptNo(),
date: new Date().toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) };
setStore((s) => ({
...s,
payments: [paid, ...s.payments],
residents: s.residents.map((r) => r.name === resident.name ? { ...r, balance: Math.max(0, r.balance - amount) } : r),
}));
setPayOpen(false);
setReceipt(paid);
};
const navItems = [["home", "layout-dashboard", "Overview"], ["payments", "credit-card", "Payments"], ["forms", "file-signature", "Forms & waivers"]];
return (
{/* Sidebar */}
{/* Main */}
{tab === "home" && (
<>
Welcome back, {resident.name.split(" ")[0]}.
One honest day at a time. Here's where things stand.
{resident.balance > 0 && (
} style={{ marginBottom: 20 }}>
Your weekly rate is ${rate}. { e.preventDefault(); setTab("payments"); setPayOpen(true); }}>Pay now to stay current.
)}
{pendingForms.length > 0 && (
1 ? "s" : ""} need your signature`} icon={ } style={{ marginBottom: 20 }}>
{ e.preventDefault(); setTab("forms"); }}>Review and sign — they only take a few minutes.
)}
My bed
{(store.houses.flatMap((h) => h.beds.map((b) => ({ ...b, house: h.name }))).find((b) => b.resident === resident.name) || {}).label || "—"}
{resident.house}
Program phase
Paperwork
>
)}
{tab === "payments" && (
<>
Payments
One transparent weekly rate: ${rate}/week. No surprises.
{myPayments.length === 0 &&
No payments yet.
}
{myPayments.map((p) => (
Weekly program fee
{p.date} · {p.method} · {p.receiptNo}
${p.amount}
{p.status}
setReceipt(p)}
leftIcon={ }>Receipt
))}
Balance due
0 ? "var(--clay-600)" : "var(--brand-primary)", lineHeight: 1 }}>
${resident.balance}
{resident.balance > 0 ? "Covers this week's program fee." : "You're all caught up. Nice."}
{!payOpen ? (
setPayOpen(true)}
leftIcon={ }>Pay ${resident.balance || rate} securely
) : (
setPayOpen(false)} onPay={recordPayment} />
)}
Secured by Stripe — demo mode, no real charge.
>
)}
{tab === "forms" && !activeForm && (
<>
Forms & waivers
Read carefully, fill in your details, and sign. Ask a house manager anything, any time.
{published.map((f) => {
const done = signed.includes(f.id);
return (
{f.title}
{f.required && !done && Required }
{f.body}
{done
? Signed
: setActiveForm(f)}>Review & sign }
);
})}
>
)}
{tab === "forms" && activeForm && (
setActiveForm(null)} onSigned={() => markSigned(activeForm.id)} />
)}
{receipt &&
setReceipt(null)} />}
);
}
/* Stripe-style embedded checkout (demo). */
function StripeCheckout({ amount, name, onCancel, onPay }) {
const { Button } = window.WillowCreekDesignSystem_90378c;
const [busy, setBusy] = rUseState(false);
const [card, setCard] = rUseState("");
const [exp, setExp] = rUseState("");
const [cvc, setCvc] = rUseState("");
const fmtCard = (v) => v.replace(/\D/g, "").slice(0, 16).replace(/(.{4})/g, "$1 ").trim();
const fmtExp = (v) => {
const d = v.replace(/\D/g, "").slice(0, 4);
return d.length > 2 ? d.slice(0, 2) + " / " + d.slice(2) : d;
};
const submit = () => { setBusy(true); setTimeout(() => { setBusy(false); onPay(amount); }, 1100); };
const fieldStyle = { width: "100%", border: "none", outline: "none", fontFamily: "var(--font-sans)",
fontSize: 15, color: "var(--text-heading)", background: "transparent", padding: "12px 14px" };
return (
);
}
function FormSigner({ form, residentName, onBack, onSigned }) {
const { Card, Button, Input, Textarea, Checkbox, Alert } = window.WillowCreekDesignSystem_90378c;
const [agreed, setAgreed] = rUseState(false);
const [hasSig, setHasSig] = rUseState(false);
const [values, setValues] = rUseState({});
const canSign = agreed && hasSig;
return (
All forms
{form.required && REQUIRED }
{form.title}
{form.body}
{form.fields.map((fl, i) => fl.type === "textarea"
?
Signature
setAgreed(e.target.checked)}
label="I have read this document and sign it freely."
description={`Signing as ${residentName} · ${new Date().toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" })}`} />
}>Sign & submit
);
}
window.WCPortal.ResidentPortal = ResidentPortal;