refactor: improve notification naming and introduce type-safe enums

- Rename billFwd* to billsNotification* for clarity
- Rename rentDueNotification* to rentNotification* for consistency
- Rename utilBillsProofOfPayment to billsProofOfPayment
- Introduce enums for type safety:
  - BillsNotificationStrategy (WhenPayed, WhenAttached)
  - BillsNotificationStatus (Scheduled, Sent, Failed)
  - RentNotificationStatus (Sent, Failed)
- Replace "pending" status with "scheduled" for better semantics
- Fix function names to proper camelCase
- Fix incorrect import path in web-app/app/lib/format.ts

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Knee Cola
2026-01-06 13:05:22 +01:00
parent 0556ad2533
commit fb35e0278e
13 changed files with 160 additions and 137 deletions

View File

@@ -2,7 +2,7 @@
import { z } from 'zod';
import { getDbClient } from '../dbClient';
import { BillingLocation, FileAttachment, YearMonth, EmailStatus } from '@evidencija-rezija/shared-code';
import { BillingLocation, FileAttachment, YearMonth, EmailStatus, BillsNotificationStrategy } from '@evidencija-rezija/shared-code';
import { ObjectId } from 'mongodb';
import { withUser } from '@/app/lib/auth';
import { AuthenticatedUser } from '../types/next-auth';
@@ -20,11 +20,11 @@ export type State = {
tenantName?: string[];
tenantStreet?: string[];
tenantTown?: string[];
billFwdEnabled?: string[];
billsNotificationEnabled?: string[];
tenantEmail?: string[];
tenantEmailStatus?: string[];
billFwdStrategy?: string[];
rentDueNotificationEnabled?: string[];
billsNotificationStrategy?: string[];
rentNotificationEnabled?: string[];
rentDueDay?: string[];
rentAmount?: string[];
updateScope?: string[];
@@ -44,12 +44,12 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
tenantName: z.string().max(30).optional().nullable(),
tenantStreet: z.string().max(27).optional().nullable(),
tenantTown: z.string().max(27).optional().nullable(),
billFwdEnabled: z.boolean().optional().nullable(),
billsNotificationEnabled: z.boolean().optional().nullable(),
tenantEmail: z.string().email(t("tenant-email-invalid")).optional().or(z.literal("")).nullable(),
tenantEmailStatus: z.enum([EmailStatus.Unverified, EmailStatus.VerificationPending, EmailStatus.Verified, EmailStatus.Unsubscribed]).optional().nullable(),
tenantEmailLanguage: z.enum(["hr", "en"]).optional().nullable(),
billFwdStrategy: z.enum(["when-payed", "when-attached"]).optional().nullable(),
rentDueNotificationEnabled: z.boolean().optional().nullable(),
billsNotificationStrategy: z.enum([BillsNotificationStrategy.WhenPayed, BillsNotificationStrategy.WhenAttached]).optional().nullable(),
rentNotificationEnabled: z.boolean().optional().nullable(),
rentDueDay: z.coerce.number().min(1).max(31).optional().nullable(),
rentAmount: z.coerce.number().int(t("rent-amount-integer")).positive(t("rent-amount-positive")).optional().nullable(),
addToSubsequentMonths: z.boolean().optional().nullable(),
@@ -86,7 +86,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
path: ["tenantTown"],
})
.refine((data) => {
if (data.billFwdEnabled || data.rentDueNotificationEnabled) {
if (data.billsNotificationEnabled || data.rentNotificationEnabled) {
return !!data.tenantEmail && data.tenantEmail.trim().length > 0;
}
return true;
@@ -95,7 +95,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
path: ["tenantEmail"],
})
.refine((data) => {
if (data.rentDueNotificationEnabled) {
if (data.rentNotificationEnabled) {
return !!data.rentAmount && data.rentAmount > 0;
}
return true;
@@ -134,12 +134,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
tenantName: formData.get('tenantName') || null,
tenantStreet: formData.get('tenantStreet') || null,
tenantTown: formData.get('tenantTown') || null,
billFwdEnabled: formData.get('billFwdEnabled') === 'on',
billsNotificationEnabled: formData.get('billsNotificationEnabled') === 'on',
tenantEmail: formData.get('tenantEmail') || null,
tenantEmailStatus: formData.get('tenantEmailStatus') as "unverified" | "verification-pending" | "verified" | "unsubscribed" | undefined,
tenantEmailLanguage: formData.get('tenantEmailLanguage') as "hr" | "en" | undefined,
billFwdStrategy: formData.get('billFwdStrategy') as "when-payed" | "when-attached" | undefined,
rentDueNotificationEnabled: formData.get('rentDueNotificationEnabled') === 'on',
billsNotificationStrategy: formData.get('billsNotificationStrategy') as BillsNotificationStrategy | undefined,
rentNotificationEnabled: formData.get('rentNotificationEnabled') === 'on',
rentDueDay: formData.get('rentDueDay') || null,
rentAmount: formData.get('rentAmount') || null,
addToSubsequentMonths: formData.get('addToSubsequentMonths') === 'on',
@@ -161,12 +161,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
tenantName,
tenantStreet,
tenantTown,
billFwdEnabled,
billsNotificationEnabled,
tenantEmail,
tenantEmailStatus,
tenantEmailLanguage,
billFwdStrategy,
rentDueNotificationEnabled,
billsNotificationStrategy,
rentNotificationEnabled,
rentDueDay,
rentAmount,
addToSubsequentMonths,
@@ -220,12 +220,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
tenantName: tenantName || null,
tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null,
billFwdEnabled: billFwdEnabled || false,
billsNotificationEnabled: billsNotificationEnabled || false,
tenantEmail: tenantEmail || null,
tenantEmailStatus: finalEmailStatus,
tenantEmailLanguage: tenantEmailLanguage || null,
billFwdStrategy: billFwdStrategy || "when-payed",
rentDueNotificationEnabled: rentDueNotificationEnabled || false,
billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed,
rentNotificationEnabled: rentNotificationEnabled || false,
rentDueDay: rentDueDay || null,
rentAmount: rentAmount || null,
}
@@ -253,12 +253,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
tenantName: tenantName || null,
tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null,
billFwdEnabled: billFwdEnabled || false,
billsNotificationEnabled: billsNotificationEnabled || false,
tenantEmail: tenantEmail || null,
tenantEmailStatus: finalEmailStatus,
tenantEmailLanguage: tenantEmailLanguage || null,
billFwdStrategy: billFwdStrategy || "when-payed",
rentDueNotificationEnabled: rentDueNotificationEnabled || false,
billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed,
rentNotificationEnabled: rentNotificationEnabled || false,
rentDueDay: rentDueDay || null,
rentAmount: rentAmount || null,
}
@@ -279,12 +279,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
tenantName: tenantName || null,
tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null,
billFwdEnabled: billFwdEnabled || false,
billsNotificationEnabled: billsNotificationEnabled || false,
tenantEmail: tenantEmail || null,
tenantEmailStatus: finalEmailStatus,
tenantEmailLanguage: tenantEmailLanguage || null,
billFwdStrategy: billFwdStrategy || "when-payed",
rentDueNotificationEnabled: rentDueNotificationEnabled || false,
billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed,
rentNotificationEnabled: rentNotificationEnabled || false,
rentDueDay: rentDueDay || null,
rentAmount: rentAmount || null,
}
@@ -304,12 +304,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
tenantName: tenantName || null,
tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null,
billFwdEnabled: billFwdEnabled || false,
billsNotificationEnabled: billsNotificationEnabled || false,
tenantEmail: tenantEmail || null,
tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified,
tenantEmailLanguage: tenantEmailLanguage || null,
billFwdStrategy: billFwdStrategy || "when-payed",
rentDueNotificationEnabled: rentDueNotificationEnabled || false,
billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed,
rentNotificationEnabled: rentNotificationEnabled || false,
rentDueDay: rentDueDay || null,
rentAmount: rentAmount || null,
yearMonth: yearMonth,
@@ -381,12 +381,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
tenantName: tenantName || null,
tenantStreet: tenantStreet || null,
tenantTown: tenantTown || null,
billFwdEnabled: billFwdEnabled || false,
billsNotificationEnabled: billsNotificationEnabled || false,
tenantEmail: tenantEmail || null,
tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified,
tenantEmailLanguage: tenantEmailLanguage || null,
billFwdStrategy: billFwdStrategy || "when-payed",
rentDueNotificationEnabled: rentDueNotificationEnabled || false,
billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed,
rentNotificationEnabled: rentNotificationEnabled || false,
rentDueDay: rentDueDay || null,
rentAmount: rentAmount || null,
yearMonth: { year: monthData.year, month: monthData.month },
@@ -495,8 +495,8 @@ export const fetchAllLocations = withUser(async (user:AuthenticatedUser, year:nu
// "bills.hub3aText": 1,
// project only file name - leave out file content so that
// less data is transferred to the client
"utilBillsProofOfPayment.fileName": 1,
"utilBillsProofOfPayment.uploadedAt": 1,
"billsProofOfPayment.fileName": 1,
"billsProofOfPayment.uploadedAt": 1,
},
},
{
@@ -558,7 +558,7 @@ export const fetchLocationById = async (locationID:string) => {
projection: {
// don't include the attachment binary data in the response
"bills.attachment.fileContentsBase64": 0,
"utilBillsProofOfPayment.fileContentsBase64": 0,
"billsProofOfPayment.fileContentsBase64": 0,
},
}
);
@@ -691,7 +691,7 @@ const serializeAttachment = async (file: File | null):Promise<FileAttachment | n
* @param ipAddress - Optional IP address for rate limiting
* @returns Promise with success status
*/
export const uploadUtilBillsProofOfPayment = async (
export const uploadBillsProofOfPayment = async (
shareId: string,
formData: FormData,
ipAddress?: string
@@ -729,7 +729,7 @@ export const uploadUtilBillsProofOfPayment = async (
const dbClient = await getDbClient();
const location = await dbClient.collection<BillingLocation>("lokacije")
.findOne({ _id: locationID }, { projection: { userId: 1, utilBillsProofOfPayment: 1, shareTTL: 1 } });
.findOne({ _id: locationID }, { projection: { userId: 1, billsProofOfPayment: 1, shareTTL: 1 } });
if (!location || !location.userId) {
return { success: false, error: 'Invalid request' };
@@ -741,12 +741,12 @@ export const uploadUtilBillsProofOfPayment = async (
}
// Check if proof of payment already uploaded
if (location.utilBillsProofOfPayment) {
if (location.billsProofOfPayment) {
return { success: false, error: 'Proof of payment already uploaded for this location' };
}
// 4. FILE VALIDATION
const file = formData.get('utilBillsProofOfPayment') as File;
const file = formData.get('billsProofOfPayment') as File;
if (!file || file.size === 0) {
return { success: false, error: 'No file provided' };
@@ -770,7 +770,7 @@ export const uploadUtilBillsProofOfPayment = async (
.updateOne(
{ _id: locationID },
{ $set: {
utilBillsProofOfPayment: attachment
billsProofOfPayment: attachment
} }
);
@@ -786,7 +786,7 @@ export const uploadUtilBillsProofOfPayment = async (
/**
* Upload rent proof of payment (for tenants via share link)
* Similar to uploadUtilBillsProofOfPayment but for rent payments
* Similar to uploadBillsProofOfPayment but for rent payments
*/
export const uploadRentProofOfPayment = async (
shareId: string,