feat: require updateScope selection and improve form UX

Location Edit Form:
- Add validation requiring user to select update scope when editing locations
- Add "no-scope-selected" as placeholder option that must be replaced with valid choice
- Display validation error if user attempts to submit without selecting scope
- Clarify update scope options with improved wording (e.g., "ALL months (past and future)")

Bill Form UX:
- Add emoji icons (👤 tenant, 🔑 landlord) to "who bears cost" options for visual clarity

Translation updates:
- Add "update-scope-required" validation message (EN/HR)
- Improve clarity of update scope option labels
- Standardize Croatian terminology ("zadani" instead of "trenutni" for current month)

🤖 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
2025-12-31 09:47:26 +01:00
parent 7e7eb5a2d8
commit 554dd8617f
4 changed files with 36 additions and 12 deletions

View File

@@ -27,6 +27,7 @@ export type State = {
rentDueNotificationEnabled?: string[];
rentDueDay?: string[];
rentAmount?: string[];
updateScope?: string[];
};
message?:string | null;
};
@@ -51,7 +52,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
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(),
updateScope: z.enum(["current", "subsequent", "all"]).optional().nullable(),
updateScope: z.enum(["no-scope-selected", "current", "subsequent", "all"]),
})
// dont include the _id field in the response
.omit({ _id: true })
@@ -100,6 +101,16 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
}, {
message: t("rent-amount-required"),
path: ["rentAmount"],
})
.refine((data) => {
// When updateScope field is present (editing mode), user must select a valid option
if (data.updateScope === "no-scope-selected") {
return false;
}
return true;
}, {
message: t("update-scope-required"),
path: ["updateScope"],
});
/**
@@ -130,7 +141,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
rentDueDay: formData.get('rentDueDay') || null,
rentAmount: formData.get('rentAmount') || null,
addToSubsequentMonths: formData.get('addToSubsequentMonths') === 'on',
updateScope: formData.get('updateScope') as "current" | "subsequent" | "all" | undefined,
updateScope: formData.get('updateScope') as "current" | "subsequent" | "all" | "",
});
// If form validation fails, return errors early. Otherwise, continue...

View File

@@ -445,11 +445,20 @@ export const LocationEditForm: FC<LocationEditFormProps> = ({ location, yearMont
<InfoBox>{t("update-scope-info")}</InfoBox>
<fieldset className="fieldset mt-2 p-2">
<legend className="fieldset-legend">{t("update-scope-legend")}</legend>
<select defaultValue="current" className="select input-bordered w-full" name="updateScope">
<select className="select input-bordered w-full" name="updateScope">
<option value="no-scope-selected">{t("update-option-placeholder")}</option>
<option value="current">{t("update-current-month")}</option>
<option value="subsequent">{t("update-subsequent-months")}</option>
<option value="all">{t("update-all-months")}</option>
</select>
<div id="updateScope-error" aria-live="polite" aria-atomic="true">
{state.errors?.updateScope &&
state.errors.updateScope.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
</fieldset>
</>
)}