Fix: clearing a field in the edit modal did not persist
The modal's submit handler wrapped every optional field with
"if (value.trim()) fields.x = value.trim()". When the user
deleted the content of a field, the value was empty, the guard
returned false, and the field was OMITTED from the PATCH entirely.
PB then left the existing value in place.
Symptom: edit a field, delete its content, save. The DB still
has the old value.
Fix: always send every field. Empty string ("") clears a value
in PocketBase (verified end-to-end). Required-field validation
(surname, first_names, enterprise_no, enterprise_name) still
guards against empty required values up-front.
Also fixed the modal's React Query invalidation to target the
list pages' actual keys (["companies"], ["directors"]) in
addition to the incorrect ["pb-list"] key. This makes the
list page refetch after save.
Same fix applied to DeleteConfirmModal for consistency.
This commit is contained in:
parent
7f0337a39c
commit
a79145f0d2
2 changed files with 65 additions and 59 deletions
|
|
@ -35,7 +35,13 @@ export default function DeleteConfirmModal({ collection, record, open, onClose }
|
|||
setLoading(true);
|
||||
try {
|
||||
await pbDelete({ collection, id: record.id });
|
||||
// Match the actual query keys used by the list pages
|
||||
// ("companies" / "directors"), not just "pb-list".
|
||||
await qc.invalidateQueries({ queryKey: ["pb-list", collection] });
|
||||
await qc.invalidateQueries({ queryKey: ["companies"] });
|
||||
await qc.invalidateQueries({ queryKey: ["directors"] });
|
||||
await qc.invalidateQueries({ queryKey: ["company", record.id] });
|
||||
await qc.invalidateQueries({ queryKey: ["director", record.id] });
|
||||
onClose();
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Delete failed. Please try again.");
|
||||
|
|
|
|||
|
|
@ -250,6 +250,12 @@ export default function EditEntityModal({ entity, record, open, onClose, onAfter
|
|||
try {
|
||||
const fields: Record<string, string | number | string[]> = {};
|
||||
|
||||
// Always send every field — empty string clears the value in PB.
|
||||
// The previous "if (value.trim())" guard LOST the user's intent to
|
||||
// clear a field: empty fields were silently skipped, so a delete in
|
||||
// the UI never landed in PB. We now send the literal value the user
|
||||
// left in the field, including "" to clear, and only validate
|
||||
// required fields up-front.
|
||||
if (isDirector) {
|
||||
if (!surname.trim() || !firstNames.trim()) {
|
||||
setError("Surname and first names are required.");
|
||||
|
|
@ -258,24 +264,24 @@ export default function EditEntityModal({ entity, record, open, onClose, onAfter
|
|||
}
|
||||
fields.surname = surname.trim();
|
||||
fields.first_names = firstNames.trim();
|
||||
if (idNo.trim()) fields.id_no = idNo.trim();
|
||||
if (telephone.trim()) fields.telephone = telephone.trim();
|
||||
if (email.trim()) fields.email = email.trim();
|
||||
if (postalAddress.trim()) fields.postal_address = postalAddress.trim();
|
||||
if (directorType.trim()) fields.director_type = directorType.trim();
|
||||
if (notes.trim()) fields.notes = notes.trim();
|
||||
// Structured address fields (added 2026-08-18)
|
||||
if (dateOfBirth.trim()) fields.date_of_birth = dateOfBirth.trim();
|
||||
if (resAddr1.trim()) fields.residential_address_line_1 = resAddr1.trim();
|
||||
if (resAddr2.trim()) fields.residential_address_line_2 = resAddr2.trim();
|
||||
if (resTown.trim()) fields.residential_town_city = resTown.trim();
|
||||
if (resProvince.trim()) fields.residential_province = resProvince.trim();
|
||||
if (resPostalCode.trim()) fields.residential_postal_code = resPostalCode.trim();
|
||||
if (postAddr1.trim()) fields.postal_address_line_1 = postAddr1.trim();
|
||||
if (postAddr2.trim()) fields.postal_address_line_2 = postAddr2.trim();
|
||||
if (postTown.trim()) fields.postal_town_city = postTown.trim();
|
||||
if (postProvince.trim()) fields.postal_province = postProvince.trim();
|
||||
if (postPostalCode.trim()) fields.postal_postal_code = postPostalCode.trim();
|
||||
fields.id_no = idNo.trim();
|
||||
fields.telephone = telephone.trim();
|
||||
fields.email = email.trim();
|
||||
fields.postal_address = postalAddress.trim();
|
||||
fields.director_type = directorType.trim();
|
||||
fields.notes = notes.trim();
|
||||
// Structured address fields
|
||||
fields.date_of_birth = dateOfBirth.trim();
|
||||
fields.residential_address_line_1 = resAddr1.trim();
|
||||
fields.residential_address_line_2 = resAddr2.trim();
|
||||
fields.residential_town_city = resTown.trim();
|
||||
fields.residential_province = resProvince.trim();
|
||||
fields.residential_postal_code = resPostalCode.trim();
|
||||
fields.postal_address_line_1 = postAddr1.trim();
|
||||
fields.postal_address_line_2 = postAddr2.trim();
|
||||
fields.postal_town_city = postTown.trim();
|
||||
fields.postal_province = postProvince.trim();
|
||||
fields.postal_postal_code = postPostalCode.trim();
|
||||
} else {
|
||||
if (!enterpriseNo.trim() || !enterpriseName.trim()) {
|
||||
setError("Enterprise number and name are required.");
|
||||
|
|
@ -284,47 +290,36 @@ export default function EditEntityModal({ entity, record, open, onClose, onAfter
|
|||
}
|
||||
fields.enterprise_no = enterpriseNo.trim().toUpperCase();
|
||||
fields.enterprise_name = enterpriseName.trim().toUpperCase();
|
||||
if (enterpriseType.trim()) fields.enterprise_type = enterpriseType.trim();
|
||||
if (registrationDate.trim()) fields.registration_date = registrationDate.trim();
|
||||
if (physicalAddress.trim()) fields.physical_address = physicalAddress.trim();
|
||||
if (postalAddressCo.trim()) fields.postal_address = postalAddressCo.trim();
|
||||
if (companyEmail.trim()) fields.company_email = companyEmail.trim();
|
||||
if (notesCo.trim()) fields.notes = notesCo.trim();
|
||||
// Structured address fields (added 2026-08-18)
|
||||
if (physAddr1.trim()) fields.physical_address_line_1 = physAddr1.trim();
|
||||
if (physAddr2.trim()) fields.physical_address_line_2 = physAddr2.trim();
|
||||
if (physTown.trim()) fields.physical_town_city = physTown.trim();
|
||||
if (physProvince.trim()) fields.physical_province = physProvince.trim();
|
||||
if (physPostalCode.trim()) fields.physical_postal_code = physPostalCode.trim();
|
||||
if (postCoAddr1.trim()) fields.postal_address_line_1 = postCoAddr1.trim();
|
||||
if (postCoAddr2.trim()) fields.postal_address_line_2 = postCoAddr2.trim();
|
||||
if (postCoTown.trim()) fields.postal_town_city = postCoTown.trim();
|
||||
if (postCoProvince.trim()) fields.postal_province = postCoProvince.trim();
|
||||
if (postCoPostalCode.trim()) fields.postal_postal_code = postCoPostalCode.trim();
|
||||
// Company-level fields (added 2026-08-18)
|
||||
if (financialYearEndMonth !== "" || (record as Company).financial_year_end_month != null) {
|
||||
fields.financial_year_end_month = financialYearEndMonth !== "" ? Number(financialYearEndMonth) : 0;
|
||||
}
|
||||
if (authorisedShares !== "" || (record as Company).authorised_shares != null) {
|
||||
fields.authorised_shares = authorisedShares !== "" ? Number(authorisedShares) : 0;
|
||||
}
|
||||
if (companyCellPhone.trim()) fields.company_cell_phone = companyCellPhone.trim();
|
||||
if (publicOfficerName.trim()) fields.public_officer_name = publicOfficerName.trim();
|
||||
if (companySecretaryName.trim()) fields.company_secretary_name = companySecretaryName.trim();
|
||||
if (sarsLoginUsername.trim()) fields.sars_login_username = sarsLoginUsername.trim();
|
||||
if (sarsLoginPassword.trim()) fields.sars_login_password = sarsLoginPassword.trim();
|
||||
if (sarsPayeNo.trim()) fields.sars_paye_no = sarsPayeNo.trim();
|
||||
if (sarsVatNo.trim()) fields.sars_vat_no = sarsVatNo.trim();
|
||||
if (sarsTaxNo.trim()) fields.sars_tax_number = sarsTaxNo.trim();
|
||||
// Period fields
|
||||
const vatPeriod = sarsVatReturnsPeriod;
|
||||
const payePeriod = sarsPayeReturnPeriod;
|
||||
if (vatPeriod !== "" || (record as Company).sars_vat_returns_period != null) {
|
||||
fields.sars_vat_returns_period = vatPeriod !== "" ? Number(vatPeriod) : 0;
|
||||
}
|
||||
if (payePeriod !== "" || (record as Company).sars_paye_return_period != null) {
|
||||
fields.sars_paye_return_period = payePeriod !== "" ? Number(payePeriod) : 0;
|
||||
}
|
||||
fields.enterprise_type = enterpriseType.trim();
|
||||
fields.registration_date = registrationDate.trim();
|
||||
fields.physical_address = physicalAddress.trim();
|
||||
fields.postal_address = postalAddressCo.trim();
|
||||
fields.company_email = companyEmail.trim();
|
||||
fields.notes = notesCo.trim();
|
||||
// Structured address fields
|
||||
fields.physical_address_line_1 = physAddr1.trim();
|
||||
fields.physical_address_line_2 = physAddr2.trim();
|
||||
fields.physical_town_city = physTown.trim();
|
||||
fields.physical_province = physProvince.trim();
|
||||
fields.physical_postal_code = physPostalCode.trim();
|
||||
fields.postal_address_line_1 = postCoAddr1.trim();
|
||||
fields.postal_address_line_2 = postCoAddr2.trim();
|
||||
fields.postal_town_city = postCoTown.trim();
|
||||
fields.postal_province = postCoProvince.trim();
|
||||
fields.postal_postal_code = postCoPostalCode.trim();
|
||||
// Company-level fields
|
||||
fields.financial_year_end_month = financialYearEndMonth !== "" ? Number(financialYearEndMonth) : 0;
|
||||
fields.authorised_shares = authorisedShares !== "" ? Number(authorisedShares) : 0;
|
||||
fields.company_cell_phone = companyCellPhone.trim();
|
||||
fields.public_officer_name = publicOfficerName.trim();
|
||||
fields.company_secretary_name = companySecretaryName.trim();
|
||||
fields.sars_login_username = sarsLoginUsername.trim();
|
||||
fields.sars_login_password = sarsLoginPassword.trim();
|
||||
fields.sars_paye_no = sarsPayeNo.trim();
|
||||
fields.sars_vat_no = sarsVatNo.trim();
|
||||
fields.sars_tax_number = sarsTaxNo.trim();
|
||||
fields.sars_vat_returns_period = sarsVatReturnsPeriod !== "" ? Number(sarsVatReturnsPeriod) : 0;
|
||||
fields.sars_paye_return_period = sarsPayeReturnPeriod !== "" ? Number(sarsPayeReturnPeriod) : 0;
|
||||
// Groups — always send current selection (empty array is valid to clear)
|
||||
fields.groups = selectedGroups;
|
||||
}
|
||||
|
|
@ -332,7 +327,12 @@ export default function EditEntityModal({ entity, record, open, onClose, onAfter
|
|||
await pbPatch({ collection, id: record.id, fields });
|
||||
|
||||
// Invalidate both list and detail caches
|
||||
// Invalidate every key the detail page or list pages might be holding.
|
||||
// The list pages use "companies" / "directors" keys (not "pb-list"),
|
||||
// and the detail page uses "director" / "company" keys.
|
||||
await qc.invalidateQueries({ queryKey: ["pb-list", collection] });
|
||||
await qc.invalidateQueries({ queryKey: ["companies"] });
|
||||
await qc.invalidateQueries({ queryKey: ["directors"] });
|
||||
await qc.invalidateQueries({ queryKey: ["director", record.id] });
|
||||
await qc.invalidateQueries({ queryKey: ["company", record.id] });
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue