Add tenant information fields to LocationEditForm
Added optional tenant fields (first name, last name, email) to billing locations with a toggle to enable/disable 2D barcode generation for tenants. Changes: - Added generateTenantCode, tenantFirstName, tenantLastName, and tenantEmail fields to BillingLocation interface - Updated LocationEditForm with toggle control and conditional tenant fields - Implemented conditional validation: tenant names required when generateTenantCode is true - Updated updateOrAddLocation action to persist tenant data across all update operations - Added localization strings for tenant fields and validation messages (Croatian/English) The generateTenantCode flag is persisted in the database and controls visibility of tenant name fields. When enabled, both first and last names become mandatory. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,11 @@ import { getTranslations, getLocale } from "next-intl/server";
|
||||
export type State = {
|
||||
errors?: {
|
||||
locationName?: string[];
|
||||
locationNotes?: string[],
|
||||
locationNotes?: string[];
|
||||
generateTenantCode?: string[];
|
||||
tenantFirstName?: string[];
|
||||
tenantLastName?: string[];
|
||||
tenantEmail?: string[];
|
||||
};
|
||||
message?:string | null;
|
||||
};
|
||||
@@ -27,11 +31,34 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
|
||||
_id: z.string(),
|
||||
locationName: z.coerce.string().min(1, t("location-name-required")),
|
||||
locationNotes: z.string(),
|
||||
generateTenantCode: z.boolean().optional().nullable(),
|
||||
tenantFirstName: z.string().optional().nullable(),
|
||||
tenantLastName: z.string().optional().nullable(),
|
||||
tenantEmail: z.string().optional().nullable(),
|
||||
addToSubsequentMonths: z.boolean().optional().nullable(),
|
||||
updateScope: z.enum(["current", "subsequent", "all"]).optional().nullable(),
|
||||
})
|
||||
// dont include the _id field in the response
|
||||
.omit({ _id: true });
|
||||
.omit({ _id: true })
|
||||
// Add conditional validation: if generateTenantCode is true, tenant names are required
|
||||
.refine((data) => {
|
||||
if (data.generateTenantCode) {
|
||||
return !!data.tenantFirstName && data.tenantFirstName.trim().length > 0;
|
||||
}
|
||||
return true;
|
||||
}, {
|
||||
message: t("tenant-first-name-required"),
|
||||
path: ["tenantFirstName"],
|
||||
})
|
||||
.refine((data) => {
|
||||
if (data.generateTenantCode) {
|
||||
return !!data.tenantLastName && data.tenantLastName.trim().length > 0;
|
||||
}
|
||||
return true;
|
||||
}, {
|
||||
message: t("tenant-last-name-required"),
|
||||
path: ["tenantLastName"],
|
||||
});
|
||||
|
||||
/**
|
||||
* Server-side action which adds or updates a bill
|
||||
@@ -49,6 +76,10 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
const validatedFields = FormSchema(t).safeParse({
|
||||
locationName: formData.get('locationName'),
|
||||
locationNotes: formData.get('locationNotes'),
|
||||
generateTenantCode: formData.get('generateTenantCode') === 'on',
|
||||
tenantFirstName: formData.get('tenantFirstName') || null,
|
||||
tenantLastName: formData.get('tenantLastName') || null,
|
||||
tenantEmail: formData.get('tenantEmail') || null,
|
||||
addToSubsequentMonths: formData.get('addToSubsequentMonths') === 'on',
|
||||
updateScope: formData.get('updateScope') as "current" | "subsequent" | "all" | undefined,
|
||||
});
|
||||
@@ -57,13 +88,17 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
if(!validatedFields.success) {
|
||||
return({
|
||||
errors: validatedFields.error.flatten().fieldErrors,
|
||||
message: "Missing Fields",
|
||||
message: t("validation-failed"),
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
locationName,
|
||||
locationNotes,
|
||||
generateTenantCode,
|
||||
tenantFirstName,
|
||||
tenantLastName,
|
||||
tenantEmail,
|
||||
addToSubsequentMonths,
|
||||
updateScope,
|
||||
} = validatedFields.data;
|
||||
@@ -97,6 +132,10 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
$set: {
|
||||
name: locationName,
|
||||
notes: locationNotes,
|
||||
generateTenantCode: generateTenantCode || false,
|
||||
tenantFirstName: tenantFirstName || null,
|
||||
tenantLastName: tenantLastName || null,
|
||||
tenantEmail: tenantEmail || null,
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -108,9 +147,9 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
name: currentLocation.name,
|
||||
$or: [
|
||||
{ "yearMonth.year": { $gt: currentLocation.yearMonth.year } },
|
||||
{
|
||||
"yearMonth.year": currentLocation.yearMonth.year,
|
||||
"yearMonth.month": { $gte: currentLocation.yearMonth.month }
|
||||
{
|
||||
"yearMonth.year": currentLocation.yearMonth.year,
|
||||
"yearMonth.month": { $gte: currentLocation.yearMonth.month }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -118,6 +157,10 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
$set: {
|
||||
name: locationName,
|
||||
notes: locationNotes,
|
||||
generateTenantCode: generateTenantCode || false,
|
||||
tenantFirstName: tenantFirstName || null,
|
||||
tenantLastName: tenantLastName || null,
|
||||
tenantEmail: tenantEmail || null,
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -132,6 +175,10 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
$set: {
|
||||
name: locationName,
|
||||
notes: locationNotes,
|
||||
generateTenantCode: generateTenantCode || false,
|
||||
tenantFirstName: tenantFirstName || null,
|
||||
tenantLastName: tenantLastName || null,
|
||||
tenantEmail: tenantEmail || null,
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -144,6 +191,10 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
userEmail,
|
||||
name: locationName,
|
||||
notes: locationNotes,
|
||||
generateTenantCode: generateTenantCode || false,
|
||||
tenantFirstName: tenantFirstName || null,
|
||||
tenantLastName: tenantLastName || null,
|
||||
tenantEmail: tenantEmail || null,
|
||||
yearMonth: yearMonth,
|
||||
bills: [],
|
||||
});
|
||||
@@ -208,6 +259,10 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
userEmail,
|
||||
name: locationName,
|
||||
notes: locationNotes,
|
||||
generateTenantCode: generateTenantCode || false,
|
||||
tenantFirstName: tenantFirstName || null,
|
||||
tenantLastName: tenantLastName || null,
|
||||
tenantEmail: tenantEmail || null,
|
||||
yearMonth: { year: monthData.year, month: monthData.month },
|
||||
bills: [],
|
||||
});
|
||||
|
||||
@@ -43,6 +43,14 @@ export interface BillingLocation {
|
||||
bills: Bill[];
|
||||
/** (optional) notes */
|
||||
notes: string|null;
|
||||
/** (optional) whether to generate 2D code for tenant */
|
||||
generateTenantCode?: boolean | null;
|
||||
/** (optional) tenant first name */
|
||||
tenantFirstName?: string | null;
|
||||
/** (optional) tenant last name */
|
||||
tenantLastName?: string | null;
|
||||
/** (optional) tenant email */
|
||||
tenantEmail?: string | null;
|
||||
};
|
||||
|
||||
export enum BilledTo {
|
||||
|
||||
Reference in New Issue
Block a user