Make public invoices RTL for Farsi businesses and fix invoice back-arrow direction.

Also add optional English name fields on the add-customer modal to match the API.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-11 15:46:18 +03:30
co-authored by Cursor
parent a50685ec69
commit f21041b950
9 changed files with 231 additions and 44 deletions
@@ -1,3 +1,7 @@
.wideModal {
max-width: min(800px, calc(100vw - 32px));
}
.formGrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
@@ -8,15 +12,29 @@
grid-column: 1 / -1;
}
.hint {
margin-bottom: 12px;
font-size: 12px;
line-height: 1.45;
.keyPoints {
margin: 0 0 14px;
padding-inline-start: 1.15em;
list-style: disc;
font-size: 12.5px;
line-height: 1.5;
color: var(--text-secondary);
}
@media (max-width: 520px) {
.keyPoints li {
white-space: nowrap;
}
.keyPoints li + li {
margin-top: 4px;
}
@media (max-width: 640px) {
.formGrid {
grid-template-columns: 1fr;
}
.keyPoints li {
white-space: normal;
}
}
@@ -28,6 +28,8 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
const [closing, setClosing] = useState(false)
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [firstNameEn, setFirstNameEn] = useState('')
const [lastNameEn, setLastNameEn] = useState('')
const [cellNumber, setCellNumber] = useState('')
const [password, setPassword] = useState('')
const [passwordConfirm, setPasswordConfirm] = useState('')
@@ -40,6 +42,8 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
setClosing(false)
setFirstName('')
setLastName('')
setFirstNameEn('')
setLastNameEn('')
setCellNumber('')
setPassword('')
setPasswordConfirm('')
@@ -86,6 +90,16 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
}
}
const trimmedFirstEn = firstNameEn.trim()
const trimmedLastEn = lastNameEn.trim()
if (
(trimmedFirstEn && trimmedFirstEn.length < 2) ||
(trimmedLastEn && trimmedLastEn.length < 2)
) {
setError(t('customers.addModal.errorNameEnLength'))
return
}
setIsSubmitting(true)
setError('')
try {
@@ -93,6 +107,8 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
cellNumber: normalizedCell,
firstName: firstName.trim(),
lastName: lastName.trim(),
...(trimmedFirstEn ? { firstNameEn: trimmedFirstEn } : {}),
...(trimmedLastEn ? { lastNameEn: trimmedLastEn } : {}),
...(trimmedPassword ? { password: trimmedPassword } : {}),
})
onCreated({
@@ -119,7 +135,7 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
onClick={onClose}
>
<div
className={`${modalStyles.modal} ${closing ? modalStyles.modalOut : modalStyles.modalIn}`}
className={`${modalStyles.modal} ${formStyles.wideModal} ${closing ? modalStyles.modalOut : modalStyles.modalIn}`}
onClick={(e) => e.stopPropagation()}
role="dialog"
aria-modal="true"
@@ -132,7 +148,6 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
<h3 id="add-customer-title" className={modalStyles.title}>
{t('customers.addModal.title')}
</h3>
<p className={modalStyles.subtitle}>{t('customers.addModal.subtitle')}</p>
</div>
<button
type="button"
@@ -145,29 +160,58 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
</div>
<div className={modalStyles.body}>
<p className={formStyles.hint}>{t('customers.addModal.hint')}</p>
<ul className={formStyles.keyPoints}>
<li>{t('customers.addModal.pointCreate')}</li>
<li>{t('customers.addModal.pointPassword')}</li>
</ul>
{error && <p className={modalStyles.errorText}>{error}</p>}
<div className={formStyles.formGrid}>
<div className={modalStyles.field}>
<label htmlFor="add-customer-first-name">{t('customers.addModal.firstName')}</label>
<label htmlFor="add-customer-first-name">{t('customers.addModal.firstNameFa')}</label>
<input
id="add-customer-first-name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
disabled={isSubmitting}
dir={isFa ? 'rtl' : 'ltr'}
dir="rtl"
lang="fa"
className="faText"
/>
</div>
<div className={modalStyles.field}>
<label htmlFor="add-customer-last-name">{t('customers.addModal.lastName')}</label>
<label htmlFor="add-customer-last-name">{t('customers.addModal.lastNameFa')}</label>
<input
id="add-customer-last-name"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
disabled={isSubmitting}
dir={isFa ? 'rtl' : 'ltr'}
dir="rtl"
lang="fa"
className="faText"
/>
</div>
<div className={modalStyles.field}>
<label htmlFor="add-customer-first-name-en">{t('customers.addModal.firstNameEn')}</label>
<input
id="add-customer-first-name-en"
value={firstNameEn}
onChange={(e) => setFirstNameEn(e.target.value)}
disabled={isSubmitting}
dir="ltr"
lang="en"
/>
</div>
<div className={modalStyles.field}>
<label htmlFor="add-customer-last-name-en">{t('customers.addModal.lastNameEn')}</label>
<input
id="add-customer-last-name-en"
value={lastNameEn}
onChange={(e) => setLastNameEn(e.target.value)}
disabled={isSubmitting}
dir="ltr"
lang="en"
/>
</div>
<div className={`${modalStyles.field} ${formStyles.formFull}`}>
@@ -233,8 +277,7 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
</div>
</div>
</div>
</div>
,
document.body,
</div>,
document.body,
)
}
@@ -192,6 +192,10 @@
text-decoration: underline;
}
:global([dir='rtl']) .backLink svg {
transform: scaleX(-1);
}
.section {
padding: 16px;
background: var(--glass-bg);