feat: detailed bill reminders and sidebar polish
Made-with: Cursor
This commit is contained in:
parent
bc44ed3809
commit
e5e7550c8c
32
src/App.css
32
src/App.css
|
|
@ -83,12 +83,26 @@
|
|||
gap: 4px;
|
||||
}
|
||||
|
||||
.template-section {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.template-section-label {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: #9ca3af;
|
||||
margin: 4px 0 6px;
|
||||
margin: 4px 0 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.template-section-count {
|
||||
font-size: 10px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.7);
|
||||
}
|
||||
|
||||
.template-item {
|
||||
|
|
@ -121,6 +135,22 @@
|
|||
border: 1px solid rgba(148, 163, 184, 0.7);
|
||||
}
|
||||
|
||||
.template-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.template-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.template-description {
|
||||
font-size: 11px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
margin-top: auto;
|
||||
padding-top: 12px;
|
||||
|
|
|
|||
20
src/App.tsx
20
src/App.tsx
|
|
@ -61,8 +61,13 @@ function App() {
|
|||
)
|
||||
if (!sectionTemplates.length) return null
|
||||
return (
|
||||
<div key={section}>
|
||||
<div className="template-section-label">{section}</div>
|
||||
<div key={section} className="template-section">
|
||||
<div className="template-section-label">
|
||||
{section}
|
||||
<span className="template-section-count">
|
||||
{sectionTemplates.length}
|
||||
</span>
|
||||
</div>
|
||||
{sectionTemplates.map((t) => (
|
||||
<button
|
||||
key={t.id}
|
||||
|
|
@ -73,8 +78,15 @@ function App() {
|
|||
}
|
||||
onClick={() => setSelectedId(t.id)}
|
||||
>
|
||||
<span>{t.label}</span>
|
||||
<span className="template-pill">{t.id}</span>
|
||||
<div className="template-main">
|
||||
<span className="template-label">{t.label}</span>
|
||||
{('description' in t && (t as any).description) ? (
|
||||
<span className="template-description">
|
||||
{(t as any).description}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<span className="template-pill">{t.section}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -60,11 +60,23 @@ export function BaseEmailShell({
|
|||
<div>
|
||||
<div className="footer-brand">Yaltopia Home</div>
|
||||
<div className="footer-meta">
|
||||
Transactional email preview · Not an actual bill
|
||||
Transactional email · Please do not reply to this address.
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer-powered">
|
||||
Powered by <strong>Yaltopia Home</strong>
|
||||
<div>
|
||||
Powered by <strong>Yaltopia Home</strong>
|
||||
</div>
|
||||
<div className="footer-approved">
|
||||
This is an approved message from Yaltopia Home.{' '}
|
||||
<a
|
||||
href="https://yaltopia.home/verify-email"
|
||||
className="footer-link"
|
||||
>
|
||||
Verify this email
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -206,6 +206,15 @@ body {
|
|||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.footer-approved {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
color: #e5e7eb;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.bank-details {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
"Liberation Mono", "Courier New", monospace;
|
||||
|
|
@ -217,6 +226,10 @@ body {
|
|||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.bank-row + .bank-row {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-block;
|
||||
padding: 3px 9px;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
AppointmentBookedEmail,
|
||||
BillPaymentReminderEmail,
|
||||
ElectricBillReminderEmail,
|
||||
InvitationTeamMemberEmail,
|
||||
ListingApprovedEmail,
|
||||
NewsletterEmail,
|
||||
|
|
@ -8,6 +9,9 @@ import {
|
|||
PasswordResetEmail,
|
||||
PropertyFoundRequestEmail,
|
||||
PropertyRequestReceivedEmail,
|
||||
RentBillReminderEmail,
|
||||
SecurityBillReminderEmail,
|
||||
WaterBillReminderEmail,
|
||||
} from './templates'
|
||||
|
||||
export const templates = [
|
||||
|
|
@ -51,7 +55,7 @@ export const templates = [
|
|||
},
|
||||
{
|
||||
id: 'bill-reminder',
|
||||
label: 'Bill payment reminder',
|
||||
label: 'Bill summary (all charges)',
|
||||
section: 'Billing',
|
||||
component: BillPaymentReminderEmail,
|
||||
props: {
|
||||
|
|
@ -64,12 +68,106 @@ export const templates = [
|
|||
total: '$1,006.20',
|
||||
dueDate: 'February 5, 2026',
|
||||
paymentUrl: 'https://pay.yaltopia.home/invoice/02934392392',
|
||||
bankDetails: {
|
||||
bank: 'Yaltopia Bank',
|
||||
accountName: 'Yaltopia Home Estates',
|
||||
accountNumber: '0293439239',
|
||||
reference: 'RICKY-UNIT-12',
|
||||
},
|
||||
bankDetails: [
|
||||
{
|
||||
bank: 'Yaltopia Bank',
|
||||
accountName: 'Yaltopia Home Estates',
|
||||
accountNumber: '0293439239',
|
||||
reference: 'RICKY-UNIT-12',
|
||||
},
|
||||
{
|
||||
bank: 'Metro Homes Bank',
|
||||
accountName: 'Yaltopia Home Estates',
|
||||
accountNumber: '0043920091',
|
||||
reference: 'YH-RICKY-UNIT-12',
|
||||
},
|
||||
],
|
||||
paymentOptionsUrl: 'https://yaltopia.home/payments',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'bill-rent',
|
||||
label: 'Rent reminder only',
|
||||
section: 'Billing',
|
||||
component: RentBillReminderEmail,
|
||||
props: {
|
||||
recipientName: 'Ricky Ricardo',
|
||||
period: 'February 2026',
|
||||
amount: '$900.00',
|
||||
dueDate: 'February 5, 2026',
|
||||
paymentUrl: 'https://pay.yaltopia.home/invoice/RENT-02934392392',
|
||||
bankDetails: [
|
||||
{
|
||||
bank: 'Yaltopia Bank',
|
||||
accountName: 'Yaltopia Home Estates',
|
||||
accountNumber: '0293439239',
|
||||
reference: 'RENT-RICKY-UNIT-12',
|
||||
},
|
||||
],
|
||||
paymentOptionsUrl: 'https://yaltopia.home/payments',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'bill-water',
|
||||
label: 'Water bill reminder',
|
||||
section: 'Billing',
|
||||
component: WaterBillReminderEmail,
|
||||
props: {
|
||||
recipientName: 'Ricky Ricardo',
|
||||
period: 'February 2026',
|
||||
amount: '$24.60',
|
||||
dueDate: 'February 5, 2026',
|
||||
bankDetails: [
|
||||
{
|
||||
bank: 'City Water Bank',
|
||||
accountName: 'Yaltopia Home Water',
|
||||
accountNumber: '2024002460',
|
||||
reference: 'WATER-RICKY-UNIT-12',
|
||||
},
|
||||
],
|
||||
paymentOptionsUrl: 'https://yaltopia.home/payments',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'bill-security',
|
||||
label: 'Security fee reminder',
|
||||
section: 'Billing',
|
||||
component: SecurityBillReminderEmail,
|
||||
props: {
|
||||
recipientName: 'Ricky Ricardo',
|
||||
period: 'February 2026',
|
||||
amount: '$35.40',
|
||||
dueDate: 'February 5, 2026',
|
||||
bankDetails: [
|
||||
{
|
||||
bank: 'Yaltopia Guard Bank',
|
||||
accountName: 'Yaltopia Home Security',
|
||||
accountNumber: '8835003540',
|
||||
reference: 'SEC-RICKY-UNIT-12',
|
||||
},
|
||||
],
|
||||
paymentOptionsUrl: 'https://yaltopia.home/payments',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'bill-electric',
|
||||
label: 'Electric bill reminder',
|
||||
section: 'Billing',
|
||||
component: ElectricBillReminderEmail,
|
||||
props: {
|
||||
recipientName: 'Ricky Ricardo',
|
||||
period: 'February 2026',
|
||||
amount: '$46.20',
|
||||
dueDate: 'February 5, 2026',
|
||||
bankDetails: [
|
||||
{
|
||||
bank: 'Gridline Bank',
|
||||
accountName: 'Yaltopia Home Electric',
|
||||
accountNumber: '6646004620',
|
||||
reference: 'ELEC-RICKY-UNIT-12',
|
||||
},
|
||||
],
|
||||
paymentOptionsUrl: 'https://yaltopia.home/payments',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -127,6 +127,13 @@ export function PropertyRequestReceivedEmail(
|
|||
)
|
||||
}
|
||||
|
||||
export interface BankDetails {
|
||||
bank: string
|
||||
accountName: string
|
||||
accountNumber: string
|
||||
reference: string
|
||||
}
|
||||
|
||||
export interface BillReminderEmailProps extends CommonEmailProps {
|
||||
period: string
|
||||
rent?: string
|
||||
|
|
@ -136,12 +143,8 @@ export interface BillReminderEmailProps extends CommonEmailProps {
|
|||
total: string
|
||||
dueDate: string
|
||||
paymentUrl?: string
|
||||
bankDetails?: {
|
||||
bank: string
|
||||
accountName: string
|
||||
accountNumber: string
|
||||
reference: string
|
||||
}
|
||||
bankDetails?: BankDetails[]
|
||||
paymentOptionsUrl?: string
|
||||
}
|
||||
|
||||
export function BillPaymentReminderEmail(props: BillReminderEmailProps) {
|
||||
|
|
@ -156,6 +159,7 @@ export function BillPaymentReminderEmail(props: BillReminderEmailProps) {
|
|||
dueDate,
|
||||
paymentUrl,
|
||||
bankDetails,
|
||||
paymentOptionsUrl,
|
||||
} = props
|
||||
return (
|
||||
<BaseEmailShell
|
||||
|
|
@ -210,14 +214,27 @@ export function BillPaymentReminderEmail(props: BillReminderEmailProps) {
|
|||
</span>
|
||||
)}
|
||||
</div>
|
||||
{bankDetails && (
|
||||
{bankDetails && bankDetails.length > 0 && (
|
||||
<div className="bank-details">
|
||||
<div>Bank: {bankDetails.bank}</div>
|
||||
<div>Account name: {bankDetails.accountName}</div>
|
||||
<div>Account number: {bankDetails.accountNumber}</div>
|
||||
<div>Reference: {bankDetails.reference}</div>
|
||||
{bankDetails.map((bank) => (
|
||||
<div key={`${bank.bank}-${bank.accountNumber}`} className="bank-row">
|
||||
<div>{bank.bank}</div>
|
||||
<div>{bank.accountName}</div>
|
||||
<div>Acct: {bank.accountNumber}</div>
|
||||
<div>Ref: {bank.reference}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{paymentOptionsUrl && (
|
||||
<p className="body-text-muted">
|
||||
View all approved payment options at{' '}
|
||||
<a href={paymentOptionsUrl} className="secondary-link">
|
||||
yaltopia.home/payments
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
<p className="body-text-muted">
|
||||
If you have already paid, you can ignore this reminder.
|
||||
</p>
|
||||
|
|
@ -225,6 +242,59 @@ export function BillPaymentReminderEmail(props: BillReminderEmailProps) {
|
|||
)
|
||||
}
|
||||
|
||||
export interface SingleChargeReminderProps extends CommonEmailProps {
|
||||
period: string
|
||||
amount: string
|
||||
dueDate: string
|
||||
paymentUrl?: string
|
||||
bankDetails?: BankDetails[]
|
||||
paymentOptionsUrl?: string
|
||||
}
|
||||
|
||||
export function RentBillReminderEmail(props: SingleChargeReminderProps) {
|
||||
const { amount, ...rest } = props
|
||||
return (
|
||||
<BillPaymentReminderEmail
|
||||
{...rest}
|
||||
rent={amount}
|
||||
total={amount}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function WaterBillReminderEmail(props: SingleChargeReminderProps) {
|
||||
const { amount, ...rest } = props
|
||||
return (
|
||||
<BillPaymentReminderEmail
|
||||
{...rest}
|
||||
water={amount}
|
||||
total={amount}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function SecurityBillReminderEmail(props: SingleChargeReminderProps) {
|
||||
const { amount, ...rest } = props
|
||||
return (
|
||||
<BillPaymentReminderEmail
|
||||
{...rest}
|
||||
security={amount}
|
||||
total={amount}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function ElectricBillReminderEmail(props: SingleChargeReminderProps) {
|
||||
const { amount, ...rest } = props
|
||||
return (
|
||||
<BillPaymentReminderEmail
|
||||
{...rest}
|
||||
electric={amount}
|
||||
total={amount}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export interface NewsletterEmailProps extends CommonEmailProps {
|
||||
title: string
|
||||
intro: string
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user