Harden invoice public links and tighten draft UI.
Use publicId in links, compact key-point/account labels, English business names, and readable favicon deploy perms. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
cbffb23ec3
commit
2e40d5eb4c
@@ -11,6 +11,12 @@ import {
|
||||
import tableStyles from '../pages/BusinessesPage.module.css'
|
||||
import styles from '../pages/BusinessInvoicesPage.module.css'
|
||||
|
||||
function focusKeyPointInput(index: number) {
|
||||
const el = document.querySelector<HTMLInputElement>(`input[data-keypoint-index="${index}"]`)
|
||||
el?.focus()
|
||||
el?.select()
|
||||
}
|
||||
|
||||
type Props = {
|
||||
itemTemplates: InvoiceItemTemplate[]
|
||||
items: DraftLineItem[]
|
||||
@@ -192,9 +198,9 @@ export function InvoiceDraftFields({
|
||||
<div className={styles.repeatStack}>
|
||||
{keyPoints.map((point, index) => (
|
||||
<div key={point.key} className={styles.repeatRow}>
|
||||
<div className={`${tableStyles.field} ${styles.flexGrow}`}>
|
||||
<label>Point {index + 1}</label>
|
||||
<div className={`${tableStyles.field} ${styles.flexGrow} ${styles.fieldNoLabel}`}>
|
||||
<input
|
||||
data-keypoint-index={index}
|
||||
value={point.text}
|
||||
onChange={(e) =>
|
||||
onKeyPointsChange(
|
||||
@@ -203,7 +209,19 @@ export function InvoiceDraftFields({
|
||||
),
|
||||
)
|
||||
}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key !== 'Enter') return
|
||||
e.preventDefault()
|
||||
const next = index + 1
|
||||
if (next < keyPoints.length) {
|
||||
focusKeyPointInput(next)
|
||||
return
|
||||
}
|
||||
onKeyPointsChange([...keyPoints, emptyDraftKeyPoint()])
|
||||
window.setTimeout(() => focusKeyPointInput(next), 0)
|
||||
}}
|
||||
placeholder="e.g. Payment due within 7 days"
|
||||
aria-label={`Key point ${index + 1}`}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
@@ -237,75 +255,85 @@ export function InvoiceDraftFields({
|
||||
<p className={styles.templateHint}>No bank accounts yet.</p>
|
||||
) : (
|
||||
<div className={styles.repeatStack}>
|
||||
{accounts.map((acc) => (
|
||||
<div key={acc.key} className={styles.accountRow}>
|
||||
<div className={`${tableStyles.field} ${styles.accountCol2}`}>
|
||||
<label>Bank name</label>
|
||||
<input
|
||||
value={acc.bankName}
|
||||
onChange={(e) =>
|
||||
onAccountsChange(
|
||||
accounts.map((a) =>
|
||||
a.key === acc.key ? { ...a, bankName: e.target.value } : a,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="Bank name"
|
||||
/>
|
||||
</div>
|
||||
<div className={`${tableStyles.field} ${styles.accountCol2}`}>
|
||||
<label>Account holder</label>
|
||||
<input
|
||||
value={acc.accountHolderName}
|
||||
onChange={(e) =>
|
||||
onAccountsChange(
|
||||
accounts.map((a) =>
|
||||
a.key === acc.key ? { ...a, accountHolderName: e.target.value } : a,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="Account holder name"
|
||||
/>
|
||||
</div>
|
||||
<div className={`${tableStyles.field} ${styles.accountCol3}`}>
|
||||
<label>Card number</label>
|
||||
<input
|
||||
value={acc.cardNumber}
|
||||
onChange={(e) =>
|
||||
onAccountsChange(
|
||||
accounts.map((a) =>
|
||||
a.key === acc.key ? { ...a, cardNumber: e.target.value } : a,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</div>
|
||||
<div className={`${tableStyles.field} ${styles.accountCol5}`}>
|
||||
<label>IBAN</label>
|
||||
<input
|
||||
value={acc.iban}
|
||||
onChange={(e) =>
|
||||
onAccountsChange(
|
||||
accounts.map((a) =>
|
||||
a.key === acc.key ? { ...a, iban: e.target.value } : a,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.removeFieldBtn}
|
||||
onClick={() => onAccountsChange(accounts.filter((a) => a.key !== acc.key))}
|
||||
aria-label="Remove account"
|
||||
title="Remove"
|
||||
{accounts.map((acc, index) => {
|
||||
const showLabels = index === 0
|
||||
return (
|
||||
<div
|
||||
key={acc.key}
|
||||
className={`${styles.accountRow} ${showLabels ? '' : styles.accountRowPlain}`}
|
||||
>
|
||||
<X size={16} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<div className={`${tableStyles.field} ${styles.accountCol2}`}>
|
||||
{showLabels ? <label>Bank name</label> : null}
|
||||
<input
|
||||
value={acc.bankName}
|
||||
onChange={(e) =>
|
||||
onAccountsChange(
|
||||
accounts.map((a) =>
|
||||
a.key === acc.key ? { ...a, bankName: e.target.value } : a,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="Bank name"
|
||||
aria-label="Bank name"
|
||||
/>
|
||||
</div>
|
||||
<div className={`${tableStyles.field} ${styles.accountCol2}`}>
|
||||
{showLabels ? <label>Account holder</label> : null}
|
||||
<input
|
||||
value={acc.accountHolderName}
|
||||
onChange={(e) =>
|
||||
onAccountsChange(
|
||||
accounts.map((a) =>
|
||||
a.key === acc.key ? { ...a, accountHolderName: e.target.value } : a,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="Account holder name"
|
||||
aria-label="Account holder"
|
||||
/>
|
||||
</div>
|
||||
<div className={`${tableStyles.field} ${styles.accountCol3}`}>
|
||||
{showLabels ? <label>Card number</label> : null}
|
||||
<input
|
||||
value={acc.cardNumber}
|
||||
onChange={(e) =>
|
||||
onAccountsChange(
|
||||
accounts.map((a) =>
|
||||
a.key === acc.key ? { ...a, cardNumber: e.target.value } : a,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="Optional"
|
||||
aria-label="Card number"
|
||||
/>
|
||||
</div>
|
||||
<div className={`${tableStyles.field} ${styles.accountCol5}`}>
|
||||
{showLabels ? <label>IBAN</label> : null}
|
||||
<input
|
||||
value={acc.iban}
|
||||
onChange={(e) =>
|
||||
onAccountsChange(
|
||||
accounts.map((a) =>
|
||||
a.key === acc.key ? { ...a, iban: e.target.value } : a,
|
||||
),
|
||||
)
|
||||
}
|
||||
placeholder="Optional"
|
||||
aria-label="IBAN"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.removeFieldBtn}
|
||||
onClick={() => onAccountsChange(accounts.filter((a) => a.key !== acc.key))}
|
||||
aria-label="Remove account"
|
||||
title="Remove"
|
||||
>
|
||||
<X size={16} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user