Add auto tenant notification toggle to LocationEditForm

Added toggle to control automatic tenant notifications with conditional
email field visibility based on the toggle state.

Changes:
- Added autoTenantNotification field to BillingLocation interface
- Updated LocationEditForm with "Notify tenant automatically" toggle
- Email field now only visible when autoTenantNotification is enabled
- Toggle appears after tenant name fields (when generateTenantCode is active)
- Updated updateOrAddLocation action to persist autoTenantNotification flag
- Added localization strings for toggle (Croatian/English)

Field visibility hierarchy:
1. Generate 2D code toggle (always visible)
2. Tenant name fields (visible when #1 is ON)
3. Auto notification toggle (visible when #1 is ON)
4. Email field (visible when #1 AND #3 are ON)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Knee Cola
2025-11-18 09:24:18 +01:00
parent 5dd7d40edf
commit 93cf159c44
5 changed files with 54 additions and 21 deletions

View File

@@ -18,6 +18,7 @@ export type State = {
generateTenantCode?: string[];
tenantFirstName?: string[];
tenantLastName?: string[];
autoTenantNotification?: string[];
tenantEmail?: string[];
};
message?:string | null;
@@ -34,6 +35,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
generateTenantCode: z.boolean().optional().nullable(),
tenantFirstName: z.string().optional().nullable(),
tenantLastName: z.string().optional().nullable(),
autoTenantNotification: z.boolean().optional().nullable(),
tenantEmail: z.string().optional().nullable(),
addToSubsequentMonths: z.boolean().optional().nullable(),
updateScope: z.enum(["current", "subsequent", "all"]).optional().nullable(),
@@ -79,6 +81,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
generateTenantCode: formData.get('generateTenantCode') === 'on',
tenantFirstName: formData.get('tenantFirstName') || null,
tenantLastName: formData.get('tenantLastName') || null,
autoTenantNotification: formData.get('autoTenantNotification') === 'on',
tenantEmail: formData.get('tenantEmail') || null,
addToSubsequentMonths: formData.get('addToSubsequentMonths') === 'on',
updateScope: formData.get('updateScope') as "current" | "subsequent" | "all" | undefined,
@@ -98,6 +101,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
generateTenantCode,
tenantFirstName,
tenantLastName,
autoTenantNotification,
tenantEmail,
addToSubsequentMonths,
updateScope,
@@ -135,6 +139,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null,
tenantLastName: tenantLastName || null,
autoTenantNotification: autoTenantNotification || false,
tenantEmail: tenantEmail || null,
}
}
@@ -160,6 +165,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null,
tenantLastName: tenantLastName || null,
autoTenantNotification: autoTenantNotification || false,
tenantEmail: tenantEmail || null,
}
}
@@ -178,6 +184,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null,
tenantLastName: tenantLastName || null,
autoTenantNotification: autoTenantNotification || false,
tenantEmail: tenantEmail || null,
}
}
@@ -194,6 +201,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null,
tenantLastName: tenantLastName || null,
autoTenantNotification: autoTenantNotification || false,
tenantEmail: tenantEmail || null,
yearMonth: yearMonth,
bills: [],
@@ -262,6 +270,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null,
tenantLastName: tenantLastName || null,
autoTenantNotification: autoTenantNotification || false,
tenantEmail: tenantEmail || null,
yearMonth: { year: monthData.year, month: monthData.month },
bills: [],

View File

@@ -49,6 +49,8 @@ export interface BillingLocation {
tenantFirstName?: string | null;
/** (optional) tenant last name */
tenantLastName?: string | null;
/** (optional) whether to automatically notify tenant */
autoTenantNotification?: boolean | null;
/** (optional) tenant email */
tenantEmail?: string | null;
};

View File

@@ -32,6 +32,11 @@ export const LocationEditForm: FC<LocationEditFormProps> = ({ location, yearMont
location?.generateTenantCode ?? false
);
// Track whether to automatically notify tenant (use persisted value from database)
const [autoTenantNotification, setAutoTenantNotification] = useState(
location?.autoTenantNotification ?? false
);
// Track tenant field values for real-time validation
const [tenantFields, setTenantFields] = useState({
tenantFirstName: location?.tenantFirstName ?? "",
@@ -113,7 +118,7 @@ export const LocationEditForm: FC<LocationEditFormProps> = ({ location, yearMont
</div>
</div>
<div className="form-control w-full">
<div className="form-control w-full mb-4">
<label className="label">
<span className="label-text">{t("tenant-last-name-label")}</span>
</label>
@@ -138,6 +143,20 @@ export const LocationEditForm: FC<LocationEditFormProps> = ({ location, yearMont
</>
)}
<div className="form-control">
<label className="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
name="autoTenantNotification"
className="toggle toggle-primary"
checked={autoTenantNotification}
onChange={(e) => setAutoTenantNotification(e.target.checked)}
/>
<span className="label-text">{t("auto-tenant-notification")}</span>
</label>
</div>
{autoTenantNotification && (
<div className="form-control w-full">
<label className="label">
<span className="label-text">{t("tenant-email-label")}</span>
@@ -160,6 +179,7 @@ export const LocationEditForm: FC<LocationEditFormProps> = ({ location, yearMont
))}
</div>
</div>
)}
{/* Show different options for add vs edit operations */}
{!location ? (

View File

@@ -129,6 +129,7 @@
"tenant-first-name-placeholder": "Enter tenant's first name",
"tenant-last-name-label": "Tenant Last Name",
"tenant-last-name-placeholder": "Enter tenant's last name",
"auto-tenant-notification": "Notify tenant automatically",
"tenant-email-label": "Tenant Email",
"tenant-email-placeholder": "Enter tenant's email",
"warning-missing-tenant-names": "Warning: Tenant first and last name are missing. The 2D barcode will not be displayed to the tenant when they open the shared link until both fields are filled in.",

View File

@@ -128,6 +128,7 @@
"tenant-first-name-placeholder": "Unesite ime podstanara",
"tenant-last-name-label": "Prezime podstanara",
"tenant-last-name-placeholder": "Unesite prezime podstanara",
"auto-tenant-notification": "Automatski obavijesti podstanara",
"tenant-email-label": "Email podstanara",
"tenant-email-placeholder": "Unesite email podstanara",
"warning-missing-tenant-names": "Upozorenje: Ime i prezime podstanara nedostaju. 2D barkod neće biti prikazan podstanaru kada otvori podijeljenu poveznicu dok oba polja ne budu popunjena.",