Rename tenantFirstName to tenantName with updated validation

- Renamed tenantFirstName to tenantName in BillingLocation interface
- Updated LocationEditForm field to accept full name (first and last)
- Set maximum length to 30 characters for tenant name field
- Updated all database operations to use tenantName
- Changed English label from "Tenant First Name" to "Tenant First and Last Name"
- Updated Croatian translations to match (Ime i prezime podstanara)
- Updated form validation schema and error messages
- Removed old tenantLastName-related translations

🤖 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-22 22:17:10 +01:00
parent a1c683528c
commit b4e459b2d5
6 changed files with 33 additions and 38 deletions

View File

@@ -15,7 +15,7 @@ export type State = {
errors?: { errors?: {
locationName?: string[]; locationName?: string[];
generateTenantCode?: string[]; generateTenantCode?: string[];
tenantFirstName?: string[]; tenantName?: string[];
tenantStreet?: string[]; tenantStreet?: string[];
tenantTown?: string[]; tenantTown?: string[];
autoBillFwd?: string[]; autoBillFwd?: string[];
@@ -36,7 +36,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
_id: z.string(), _id: z.string(),
locationName: z.coerce.string().min(1, t("location-name-required")), locationName: z.coerce.string().min(1, t("location-name-required")),
generateTenantCode: z.boolean().optional().nullable(), generateTenantCode: z.boolean().optional().nullable(),
tenantFirstName: z.string().optional().nullable(), tenantName: z.string().max(30).optional().nullable(),
tenantStreet: z.string().max(27).optional().nullable(), tenantStreet: z.string().max(27).optional().nullable(),
tenantTown: z.string().max(27).optional().nullable(), tenantTown: z.string().max(27).optional().nullable(),
autoBillFwd: z.boolean().optional().nullable(), autoBillFwd: z.boolean().optional().nullable(),
@@ -53,12 +53,12 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
// Add conditional validation: if generateTenantCode is true, tenant fields are required // Add conditional validation: if generateTenantCode is true, tenant fields are required
.refine((data) => { .refine((data) => {
if (data.generateTenantCode) { if (data.generateTenantCode) {
return !!data.tenantFirstName && data.tenantFirstName.trim().length > 0; return !!data.tenantName && data.tenantName.trim().length > 0;
} }
return true; return true;
}, { }, {
message: t("tenant-first-name-required"), message: t("tenant-name-required"),
path: ["tenantFirstName"], path: ["tenantName"],
}) })
.refine((data) => { .refine((data) => {
if (data.generateTenantCode) { if (data.generateTenantCode) {
@@ -113,7 +113,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
const validatedFields = FormSchema(t).safeParse({ const validatedFields = FormSchema(t).safeParse({
locationName: formData.get('locationName'), locationName: formData.get('locationName'),
generateTenantCode: formData.get('generateTenantCode') === 'on', generateTenantCode: formData.get('generateTenantCode') === 'on',
tenantFirstName: formData.get('tenantFirstName') || null, tenantName: formData.get('tenantName') || null,
tenantStreet: formData.get('tenantStreet') || null, tenantStreet: formData.get('tenantStreet') || null,
tenantTown: formData.get('tenantTown') || null, tenantTown: formData.get('tenantTown') || null,
autoBillFwd: formData.get('autoBillFwd') === 'on', autoBillFwd: formData.get('autoBillFwd') === 'on',
@@ -137,7 +137,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
const { const {
locationName, locationName,
generateTenantCode, generateTenantCode,
tenantFirstName, tenantName,
tenantStreet, tenantStreet,
tenantTown, tenantTown,
autoBillFwd, autoBillFwd,
@@ -179,7 +179,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
$set: { $set: {
name: locationName, name: locationName,
generateTenantCode: generateTenantCode || false, generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null, tenantName: tenantName || null,
tenantStreet: tenantStreet || null, tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null, tenantTown: tenantTown || null,
autoBillFwd: autoBillFwd || false, autoBillFwd: autoBillFwd || false,
@@ -209,7 +209,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
$set: { $set: {
name: locationName, name: locationName,
generateTenantCode: generateTenantCode || false, generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null, tenantName: tenantName || null,
tenantStreet: tenantStreet || null, tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null, tenantTown: tenantTown || null,
autoBillFwd: autoBillFwd || false, autoBillFwd: autoBillFwd || false,
@@ -232,7 +232,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
$set: { $set: {
name: locationName, name: locationName,
generateTenantCode: generateTenantCode || false, generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null, tenantName: tenantName || null,
tenantStreet: tenantStreet || null, tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null, tenantTown: tenantTown || null,
autoBillFwd: autoBillFwd || false, autoBillFwd: autoBillFwd || false,
@@ -254,7 +254,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
name: locationName, name: locationName,
notes: null, notes: null,
generateTenantCode: generateTenantCode || false, generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null, tenantName: tenantName || null,
tenantStreet: tenantStreet || null, tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null, tenantTown: tenantTown || null,
autoBillFwd: autoBillFwd || false, autoBillFwd: autoBillFwd || false,
@@ -328,7 +328,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
name: locationName, name: locationName,
notes: null, notes: null,
generateTenantCode: generateTenantCode || false, generateTenantCode: generateTenantCode || false,
tenantFirstName: tenantFirstName || null, tenantName: tenantName || null,
tenantStreet: tenantStreet || null, tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null, tenantTown: tenantTown || null,
autoBillFwd: autoBillFwd || false, autoBillFwd: autoBillFwd || false,

View File

@@ -51,8 +51,8 @@ export interface BillingLocation {
notes: string|null; notes: string|null;
/** (optional) whether to generate 2D code for tenant */ /** (optional) whether to generate 2D code for tenant */
generateTenantCode?: boolean | null; generateTenantCode?: boolean | null;
/** (optional) tenant first name */ /** (optional) tenant name */
tenantFirstName?: string | null; tenantName?: string | null;
/** (optional) tenant street */ /** (optional) tenant street */
tenantStreet?: string | null; tenantStreet?: string | null;
/** (optional) tenant town */ /** (optional) tenant town */

View File

@@ -45,7 +45,7 @@ export const LocationEditForm: FC<LocationEditFormProps> = ({ location, yearMont
// Track tenant field values for real-time validation // Track tenant field values for real-time validation
const [tenantFields, setTenantFields] = useState({ const [tenantFields, setTenantFields] = useState({
tenantFirstName: location?.tenantFirstName ?? "", tenantName: location?.tenantName ?? "",
tenantStreet: location?.tenantStreet ?? "", tenantStreet: location?.tenantStreet ?? "",
tenantTown: location?.tenantTown ?? "", tenantTown: location?.tenantTown ?? "",
tenantEmail: location?.tenantEmail ?? "", tenantEmail: location?.tenantEmail ?? "",
@@ -101,20 +101,21 @@ export const LocationEditForm: FC<LocationEditFormProps> = ({ location, yearMont
<> <>
<div className="form-control w-full"> <div className="form-control w-full">
<label className="label"> <label className="label">
<span className="label-text">{t("tenant-first-name-label")}</span> <span className="label-text">{t("tenant-name-label")}</span>
</label> </label>
<input <input
id="tenantFirstName" id="tenantName"
name="tenantFirstName" name="tenantName"
type="text" type="text"
placeholder={t("tenant-first-name-placeholder")} maxLength={30}
placeholder={t("tenant-name-placeholder")}
className="input input-bordered w-full placeholder:text-gray-600" className="input input-bordered w-full placeholder:text-gray-600"
defaultValue={location?.tenantFirstName ?? ""} defaultValue={location?.tenantName ?? ""}
onChange={(e) => handleTenantFieldChange("tenantFirstName", e.target.value)} onChange={(e) => handleTenantFieldChange("tenantName", e.target.value)}
/> />
<div id="tenantFirstName-error" aria-live="polite" aria-atomic="true"> <div id="tenantName-error" aria-live="polite" aria-atomic="true">
{state.errors?.tenantFirstName && {state.errors?.tenantName &&
state.errors.tenantFirstName.map((error: string) => ( state.errors.tenantName.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}> <p className="mt-2 text-sm text-red-500" key={error}>
{error} {error}
</p> </p>

View File

@@ -16,7 +16,7 @@ export interface ViewLocationCardProps {
export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettings}) => { export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettings}) => {
const { _id, name, yearMonth, bills, tenantFirstName, tenantStreet, tenantTown } = location; const { _id, name, yearMonth, bills, tenantName, tenantStreet, tenantTown } = location;
const t = useTranslations("home-page.location-card"); const t = useTranslations("home-page.location-card");
@@ -25,7 +25,7 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
const paymentParams:PaymentParams = { const paymentParams:PaymentParams = {
Iznos: (monthlyExpense/100).toFixed(2).replace(".",","), Iznos: (monthlyExpense/100).toFixed(2).replace(".",","),
ImePlatitelja: tenantFirstName ?? "", ImePlatitelja: tenantName ?? "",
AdresaPlatitelja: tenantStreet ?? "", AdresaPlatitelja: tenantStreet ?? "",
SjedistePlatitelja: tenantTown ?? "", SjedistePlatitelja: tenantTown ?? "",
Primatelj: (userSettings?.firstName && userSettings?.lastName) ? `${userSettings.firstName} ${userSettings.lastName}` : "", Primatelj: (userSettings?.firstName && userSettings?.lastName) ? `${userSettings.firstName} ${userSettings.lastName}` : "",

View File

@@ -140,10 +140,8 @@
"tenant-2d-code-legend": "TENANT 2D CODE", "tenant-2d-code-legend": "TENANT 2D CODE",
"tenant-2d-code-info": "2D barcode allows the tenant to quickly and easily pay the amount they owe you for paid utility bills to your IBAN. The barcode will be displayed when the tenant opens the link to the statement for the given month.", "tenant-2d-code-info": "2D barcode allows the tenant to quickly and easily pay the amount they owe you for paid utility bills to your IBAN. The barcode will be displayed when the tenant opens the link to the statement for the given month.",
"tenant-2d-code-toggle-label": "generate 2d code", "tenant-2d-code-toggle-label": "generate 2d code",
"tenant-first-name-label": "Tenant First Name", "tenant-name-label": "Tenant First and Last Name",
"tenant-first-name-placeholder": "Enter tenant's first name", "tenant-name-placeholder": "Enter tenant's first and last name",
"tenant-last-name-label": "Tenant Last Name",
"tenant-last-name-placeholder": "Enter tenant's last name",
"tenant-street-label": "Tenant Street", "tenant-street-label": "Tenant Street",
"tenant-street-placeholder": "Enter tenant's street", "tenant-street-placeholder": "Enter tenant's street",
"tenant-town-label": "Tenant Town", "tenant-town-label": "Tenant Town",
@@ -175,8 +173,7 @@
"update-all-months": "all months", "update-all-months": "all months",
"validation": { "validation": {
"location-name-required": "Relaestate name is required", "location-name-required": "Relaestate name is required",
"tenant-first-name-required": "tenant first name is missing", "tenant-name-required": "tenant name is missing",
"tenant-last-name-required": "tenant last name is missing",
"tenant-street-required": "tenant street is missing", "tenant-street-required": "tenant street is missing",
"tenant-town-required": "tenant town is missing", "tenant-town-required": "tenant town is missing",
"tenant-email-required": "tenant email is missing", "tenant-email-required": "tenant email is missing",

View File

@@ -139,10 +139,8 @@
"tenant-2d-code-legend": "2D BARKOD ZA PODSTANARA", "tenant-2d-code-legend": "2D BARKOD ZA PODSTANARA",
"tenant-2d-code-info": "2D barkod omogućuje podstanaru da brzo i jednostavno na vaš IBAN uplati iznos koji vam duguje za plaćene režije. Barkod će biti prikazan kada podstanar otvori poveznicu na obračun za zadani mjesec.", "tenant-2d-code-info": "2D barkod omogućuje podstanaru da brzo i jednostavno na vaš IBAN uplati iznos koji vam duguje za plaćene režije. Barkod će biti prikazan kada podstanar otvori poveznicu na obračun za zadani mjesec.",
"tenant-2d-code-toggle-label": "generiraj 2D barkod", "tenant-2d-code-toggle-label": "generiraj 2D barkod",
"tenant-first-name-label": "Ime podstanara", "tenant-name-label": "Ime i prezime podstanara",
"tenant-first-name-placeholder": "Unesite ime podstanara", "tenant-name-placeholder": "Unesite ime i prezime podstanara",
"tenant-last-name-label": "Prezime podstanara",
"tenant-last-name-placeholder": "Unesite prezime podstanara",
"tenant-street-label": "Ulica podstanara", "tenant-street-label": "Ulica podstanara",
"tenant-street-placeholder": "Unesite ulicu podstanara", "tenant-street-placeholder": "Unesite ulicu podstanara",
"tenant-town-label": "Grad podstanara", "tenant-town-label": "Grad podstanara",
@@ -174,8 +172,7 @@
"update-all-months": "sve mjesece", "update-all-months": "sve mjesece",
"validation": { "validation": {
"location-name-required": "Ime nekretnine je obavezno", "location-name-required": "Ime nekretnine je obavezno",
"tenant-first-name-required": "nedostaje ime podstanara", "tenant-name-required": "nedostaje ime i prezime podstanara",
"tenant-last-name-required": "nedostaje prezime podstanara",
"tenant-street-required": "nedostaje ulica podstanara", "tenant-street-required": "nedostaje ulica podstanara",
"tenant-town-required": "nedostaje grad podstanara", "tenant-town-required": "nedostaje grad podstanara",
"tenant-email-required": "nedostaje email podstanara", "tenant-email-required": "nedostaje email podstanara",