+
+ {pageSizeOptions.length > 0 && pagination?.onPageSizeChange && (
+
+ )}
+
+
handleResetPassword(item)}
+ >
+
+
),
];
});
React.useEffect(() => {
- setCurrentPage(1);
+ const handle = setTimeout(() => {
+ setDebouncedSearch(searchTerm);
+ }, 200);
+ return () => clearTimeout(handle);
}, [searchTerm]);
- const handleEdit = (indexInFiltered) => {
- const selected = filteredProfiles[indexInFiltered];
- if (!selected) return;
- const originalIndex = profiles.findIndex((item) => item.establishmentId === selected.establishmentId);
+ React.useEffect(() => {
+ setCurrentPage(1);
+ }, [debouncedSearch]);
+
+ React.useEffect(() => {
+ setCurrentPage(1);
+ }, [pageSize]);
+
+ React.useEffect(() => {
+ if (modalMode) {
+ setActiveStep(0);
+ setConfirmError('');
+ setPasswordError('');
+ setPasswordReuseError('');
+ setSaveError('');
+ }
+ }, [modalMode]);
+
+ const showToast = React.useCallback((type, message) => {
+ if (!message) return;
+ if (toastTimeoutRef.current) {
+ window.clearTimeout(toastTimeoutRef.current);
+ toastTimeoutRef.current = null;
+ }
+ setToast({ type, message });
+ toastTimeoutRef.current = window.setTimeout(() => {
+ setToast(null);
+ toastTimeoutRef.current = null;
+ }, 4000);
+ }, []);
+
+ const closeToast = React.useCallback(() => {
+ if (toastTimeoutRef.current) {
+ window.clearTimeout(toastTimeoutRef.current);
+ toastTimeoutRef.current = null;
+ }
+ setToast(null);
+ }, []);
+
+ React.useEffect(
+ () => () => {
+ if (toastTimeoutRef.current) {
+ window.clearTimeout(toastTimeoutRef.current);
+ toastTimeoutRef.current = null;
+ }
+ },
+ []
+ );
+
+ const mapApiEstablishmentToProfile = React.useCallback((item) => {
+ const base = createEmptyProfile();
+ const primaryUser = Array.isArray(item?.users) && item.users.length ? item.users[0] : item?.establishment_user ?? null;
+ const emiratiMale = item?.emirati_male ?? '';
+ const emiratiFemale = item?.emirati_female ?? '';
+ const nonEmiratiMale = item?.non_emirati_male ?? '';
+ const nonEmiratiFemale = item?.non_emirati_female ?? '';
+ const totalEmirati = item?.total_emirati ?? '';
+ const totalEmployees = item?.total_employees ?? '';
+
+ return {
+ ...base,
+ apiId: item?.id ?? null,
+ establishmentName: item?.factory_name ?? '',
+ emirate: item?.emirate ?? '',
+ isicCode: item?.isic_code ?? '',
+ industryCodeBusiness: item?.industry_code ?? base.industryCodeBusiness,
+ industryCodeCurrent: item?.isic_code ?? base.industryCodeCurrent,
+ industryDescription: item?.industry_description ?? base.industryDescription,
+ establishmentId: item?.establishment_code ?? '',
+ products: base.products,
+ totalEmployees: totalEmployees !== '' && totalEmployees !== null ? String(totalEmployees) : '',
+ createdBy: item?.created_by ?? primaryUser?.name ?? '',
+ createdOn: formatApiDate(item?.created_at),
+ lastUpdated: item?.updated_by ?? formatApiDate(item?.updated_at),
+ contactName: item?.factory_name ?? '',
+ contactEmirate: item?.emirate ?? '',
+ contactEmail: item?.email ?? '',
+ contactMakaniNumber: base.contactMakaniNumber,
+ permanentFactoryCode: item?.permanent_factory_code ?? base.permanentFactoryCode,
+ uniqueLicenseNumber: item?.license_number ?? base.uniqueLicenseNumber,
+ corporateName: item?.factory_name ?? '',
+ corporateEmirate: item?.emirate ?? '',
+ corporateEmail: item?.email ?? '',
+ employmentEmiratiMale: emiratiMale !== '' && emiratiMale !== null ? String(emiratiMale) : '',
+ employmentEmiratiFemale: emiratiFemale !== '' && emiratiFemale !== null ? String(emiratiFemale) : '',
+ employmentNonEmiratiMale: nonEmiratiMale !== '' && nonEmiratiMale !== null ? String(nonEmiratiMale) : '',
+ employmentNonEmiratiFemale: nonEmiratiFemale !== '' && nonEmiratiFemale !== null ? String(nonEmiratiFemale) : '',
+ employmentTotalEmirati: totalEmirati !== '' && totalEmirati !== null ? String(totalEmirati) : '',
+ employmentTotalEmployees: totalEmployees !== '' && totalEmployees !== null ? String(totalEmployees) : '',
+ userProfileName: primaryUser?.name ?? base.userProfileName,
+ userProfileEmail: primaryUser?.email ?? base.userProfileEmail,
+ };
+ }, []);
+
+ React.useEffect(() => {
+ let cancelled = false;
+ const loadProfiles = async () => {
+ try {
+ const response = await fetchEstablishments({ skipAuth: false });
+ if (cancelled) return;
+ const records = Array.isArray(response?.data) ? response.data : [];
+ if (records.length) {
+ setProfiles(records.map(mapApiEstablishmentToProfile));
+ }
+ } catch (error) {
+ if (cancelled) return;
+ console.error('Failed to fetch establishments list.', error);
+ const message = error?.response?.data?.message || error?.message || 'Unable to load establishments. Using default data.';
+ showToast('error', message);
+ }
+ };
+ loadProfiles();
+ return () => {
+ cancelled = true;
+ };
+ }, [mapApiEstablishmentToProfile, showToast]);
+
+ const handleEdit = (identifier) => {
+ if (modalMode && isDirty) {
+ const shouldContinue = window.confirm('You have unsaved changes. Do you want to discard them and continue?');
+ if (!shouldContinue) {
+ return;
+ }
+ }
+ if (!identifier) return;
+ const originalIndex = profiles.findIndex((item) => {
+ if (item.apiId !== null && item.apiId !== undefined && item.apiId === identifier) {
+ return true;
+ }
+ return item.establishmentId === identifier;
+ });
if (originalIndex === -1) return;
setEditingRow(originalIndex);
+ const profileRecord = profiles[originalIndex];
const base = createEmptyProfile();
- const nextForm = {
+ let nextForm = {
...base,
- ...profiles[originalIndex],
- corporateSameAs: Boolean(profiles[originalIndex]?.corporateSameAs),
+ ...profileRecord,
+ corporateSameAs: Boolean(profileRecord?.corporateSameAs),
};
if (nextForm.corporateSameAs) {
Object.entries(contactToCorporateMap).forEach(([source, target]) => {
nextForm[target] = nextForm[source];
});
}
+ Object.assign(nextForm, computeEmploymentTotals(nextForm));
setForm(nextForm);
setModalMode('edit');
+ initialSnapshotRef.current = nextForm;
+ corporateBackupRef.current = captureCorporateValues(nextForm);
+ setIsDirty(false);
+
+ if (profileRecord?.apiId !== null && profileRecord?.apiId !== undefined) {
+ (async () => {
+ try {
+ const response = await fetchEstablishmentDetail(profileRecord.apiId);
+ const detail = response?.data || response;
+ if (!detail) return;
+ const mapped = {
+ ...createEmptyProfile(),
+ ...mapApiEstablishmentToProfile(detail),
+ };
+ mapped.apiId = mapped.apiId ?? profileRecord.apiId;
+ mapped.corporateSameAs = Boolean(mapped.corporateSameAs);
+ if (mapped.corporateSameAs) {
+ Object.entries(contactToCorporateMap).forEach(([source, target]) => {
+ mapped[target] = mapped[source];
+ });
+ }
+ Object.assign(mapped, computeEmploymentTotals(mapped));
+ setForm(mapped);
+ initialSnapshotRef.current = mapped;
+ corporateBackupRef.current = captureCorporateValues(mapped);
+ setIsDirty(false);
+ } catch (error) {
+ const message = error?.response?.data?.message || error?.message || 'Failed to load establishment details.';
+ showToast('error', message);
+ }
+ })();
+ }
};
- const handleDelete = (indexInFiltered) => {
- const selected = filteredProfiles[indexInFiltered];
- if (!selected) return;
- const originalIndex = profiles.findIndex((item) => item.establishmentId === selected.establishmentId);
+ const handleDelete = (establishmentId) => {
+ if (!establishmentId) return;
+ const originalIndex = profiles.findIndex((item) => {
+ if (item.apiId !== null && item.apiId !== undefined) {
+ return item.apiId === establishmentId;
+ }
+ return item.establishmentId === establishmentId;
+ });
+ if (originalIndex === -1) return;
setDeletingRow(originalIndex);
setShowDeleteConfirm(true);
};
+ const handleAddNew = () => {
+ if (modalMode && isDirty) {
+ const shouldContinue = window.confirm('You have unsaved changes. Do you want to discard them and continue?');
+ if (!shouldContinue) {
+ return;
+ }
+ }
+ const emptyProfile = createEmptyProfile();
+ Object.assign(emptyProfile, computeEmploymentTotals(emptyProfile));
+ setModalMode('add');
+ setForm(emptyProfile);
+ setEditingRow(null);
+ setActiveStep(0);
+ setConfirmError('');
+ setCurrentPage(1);
+ setSaveWarning(false);
+ initialSnapshotRef.current = emptyProfile;
+ corporateBackupRef.current = captureCorporateValues(emptyProfile);
+ setIsDirty(false);
+ };
+
const openAddModal = () => {
- setForm(createEmptyProfile());
+ if (modalMode && isDirty) {
+ const shouldContinue = window.confirm('You have unsaved changes. Do you want to discard them and continue?');
+ if (!shouldContinue) {
+ return;
+ }
+ }
+ const emptyProfile = createEmptyProfile();
+ Object.assign(emptyProfile, computeEmploymentTotals(emptyProfile));
+ setForm(emptyProfile);
setEditingRow(null);
setModalMode('add');
+ initialSnapshotRef.current = emptyProfile;
+ corporateBackupRef.current = captureCorporateValues(emptyProfile);
+ setIsDirty(false);
};
const closeModal = () => {
+ if (isDirty) {
+ const shouldClose = window.confirm('You have unsaved changes. Do you really want to discard them?');
+ if (!shouldClose) {
+ return;
+ }
+ }
+ const emptyProfile = createEmptyProfile();
+ Object.assign(emptyProfile, computeEmploymentTotals(emptyProfile));
setModalMode(null);
- setForm(createEmptyProfile());
+ setForm(emptyProfile);
setEditingRow(null);
+ setConfirmError('');
+ setIsSaving(false);
+ setSaveWarning(false);
+ setIsDirty(false);
+ initialSnapshotRef.current = emptyProfile;
+ corporateBackupRef.current = captureCorporateValues(emptyProfile);
};
const closeDeleteConfirm = () => {
@@ -399,10 +1461,41 @@ const CompanyProfile = () => {
setDeletingRow(null);
};
- const handleDeleteConfirm = () => {
+ const handleDeleteConfirm = async () => {
if (deletingRow === null) return;
- setProfiles((prev) => prev.filter((_, index) => index !== deletingRow));
- closeDeleteConfirm();
+ if (isDeletingApi) return;
+ const targetProfile = profiles[deletingRow];
+ if (!targetProfile) {
+ closeDeleteConfirm();
+ return;
+ }
+ const targetId = targetProfile.apiId;
+ if (targetId === null || targetId === undefined) {
+ setProfiles((prev) => prev.filter((_, index) => index !== deletingRow));
+ showToast('success', 'Deleted successfully');
+ closeDeleteConfirm();
+ return;
+ }
+ setIsDeletingApi(true);
+ try {
+ const response = await deleteEstablishment(targetId);
+ const successMessage = response?.message || 'Deleted successfully';
+ setProfiles((prev) =>
+ prev.filter((profile) => {
+ if (targetId !== null && targetId !== undefined) {
+ return profile.apiId !== targetId;
+ }
+ return profile.establishmentId !== targetProfile.establishmentId;
+ })
+ );
+ showToast('success', successMessage);
+ closeDeleteConfirm();
+ } catch (error) {
+ const message = error?.response?.data?.message || error?.message || 'Failed to delete establishment.';
+ showToast('error', message);
+ } finally {
+ setIsDeletingApi(false);
+ }
};
const handleFormChange = (field) => (event) => {
@@ -410,20 +1503,53 @@ const CompanyProfile = () => {
const value = isCheckbox ? event.target.checked : event?.target?.value ?? event;
setForm((prev) => {
const next = { ...prev, [field]: value };
-
+ if (
+ field === 'employmentEmiratiMale' ||
+ field === 'employmentEmiratiFemale' ||
+ field === 'employmentNonEmiratiMale' ||
+ field === 'employmentNonEmiratiFemale'
+ ) {
+ Object.assign(next, computeEmploymentTotals(next));
+ }
if (field === 'corporateSameAs') {
if (value) {
+ corporateBackupRef.current = captureCorporateValues(prev);
Object.entries(contactToCorporateMap).forEach(([source, target]) => {
- next[target] = next[source];
+ next[target] = prev[source];
});
+ } else {
+ const backup = corporateBackupRef.current;
+ corporateFieldKeys.forEach((target) => {
+ next[target] = backup?.[target] ?? '';
+ });
+ corporateBackupRef.current = null;
}
- return next;
}
if (next.corporateSameAs && contactToCorporateMap[field]) {
next[contactToCorporateMap[field]] = value;
}
-
+ if (field === 'userProfilePassword') {
+ setPasswordError(validatePasswordComplexity(String(value || '')));
+ setPasswordReuseError('');
+ }
+ if (field === 'userProfileConfirmPassword') {
+ setConfirmError('');
+ }
+ const fieldsToValidate = new Set([field]);
+ if (field === 'corporateSameAs') {
+ corporateFieldKeys.forEach((key) => fieldsToValidate.add(key));
+ } else if (next.corporateSameAs && contactToCorporateMap[field]) {
+ fieldsToValidate.add(contactToCorporateMap[field]);
+ }
+ setFieldErrors((prevErrors) => {
+ const updatedErrors = { ...prevErrors };
+ fieldsToValidate.forEach((targetField) => {
+ updatedErrors[targetField] = computeFieldErrorMessage(targetField, next[targetField]);
+ });
+ return updatedErrors;
+ });
+ setIsDirty(computeIsDirty(next));
return next;
});
};
@@ -436,54 +1562,214 @@ const CompanyProfile = () => {
return `${day}/${month}/${year}`;
};
- const handleSaveForm = () => {
- if (!form.establishmentName || !form.emirate || !form.isicCode || !form.establishmentId) {
+ const handleSaveForm = async () => {
+ if (!validateStepFields(activeStep)) {
return;
}
- const employmentTotals = ['employmentEmiratiMale', 'employmentNonEmiratiMale', 'employmentEmiratiFemale', 'employmentNonEmiratiFemale'].reduce(
- (acc, key) => acc + (Number(form[key]) || 0),
- 0
- );
- const totalEmirati = ['employmentEmiratiMale', 'employmentEmiratiFemale'].reduce(
- (acc, key) => acc + (Number(form[key]) || 0),
- 0
- );
-
- const computedTotalEmployees =
- form.employmentTotalEmployees || (employmentTotals > 0 ? String(employmentTotals) : form.totalEmployees || '0');
- const computedTotalEmirati =
- form.employmentTotalEmirati || (totalEmirati > 0 ? String(totalEmirati) : form.employmentTotalEmirati || '');
-
- const payload = {
- ...form,
- products: form.products || '0',
- totalEmployees: computedTotalEmployees,
- employmentTotalEmployees: computedTotalEmployees,
- employmentTotalEmirati: computedTotalEmirati,
- createdBy: form.createdBy || 'Admin',
- createdOn: form.createdOn || getToday(),
- lastUpdated: form.lastUpdated || 'Admin',
- corporateSameAs: Boolean(form.corporateSameAs),
- };
-
- if (payload.corporateSameAs) {
- Object.entries(contactToCorporateMap).forEach(([source, target]) => {
- payload[target] = payload[source];
- });
+ if (form.userProfilePassword) {
+ const complexityMessage = validatePasswordComplexity(form.userProfilePassword);
+ if (complexityMessage) {
+ setPasswordError(complexityMessage);
+ return;
+ }
+ const history = Array.isArray(form.passwordHistory) ? form.passwordHistory.slice(0, MAX_PASSWORD_HISTORY) : [];
+ if (history.some((item) => item?.value === form.userProfilePassword)) {
+ setPasswordReuseError('Password must not match any of the last five passwords.');
+ return;
+ }
+ if (!form.userProfileConfirmPassword || form.userProfilePassword !== form.userProfileConfirmPassword) {
+ setConfirmError('Passwords do not match');
+ return;
+ }
}
- if (modalMode === 'edit' && editingRow !== null) {
- setProfiles((prev) =>
- prev.map((item, index) => (index === editingRow ? { ...item, ...payload, lastUpdated: form.lastUpdated || 'Admin' } : item))
+ const websiteErrorMap = {};
+ ['contactWebsite', 'corporateWebsite'].forEach((websiteField) => {
+ const message = computeFieldErrorMessage(websiteField, form[websiteField]);
+ if (message) {
+ websiteErrorMap[websiteField] = message;
+ }
+ });
+ const websiteFieldsWithErrors = Object.keys(websiteErrorMap);
+ if (websiteFieldsWithErrors.length) {
+ setFieldErrors((prevErrors) => ({ ...prevErrors, ...websiteErrorMap }));
+ if (websiteErrorMap.contactWebsite) {
+ setActiveStep(2);
+ } else if (websiteErrorMap.corporateWebsite) {
+ setActiveStep(3);
+ }
+ return;
+ }
+
+ const startTime = performance.now();
+ setIsSaving(true);
+ setSaveWarning(false);
+ setSaveError('');
+
+ try {
+ const employmentTotals = ['employmentEmiratiMale', 'employmentNonEmiratiMale', 'employmentEmiratiFemale', 'employmentNonEmiratiFemale'].reduce(
+ (acc, key) => acc + (Number(form[key]) || 0),
+ 0
+ );
+ const totalEmirati = ['employmentEmiratiMale', 'employmentEmiratiFemale'].reduce(
+ (acc, key) => acc + (Number(form[key]) || 0),
+ 0
);
- } else {
- setProfiles((prev) => [{ ...payload }, ...prev]);
- }
- closeModal();
+ const computedTotalEmployees =
+ form.employmentTotalEmployees || (employmentTotals > 0 ? String(employmentTotals) : form.totalEmployees || '0');
+ const computedTotalEmirati =
+ form.employmentTotalEmirati || (totalEmirati > 0 ? String(totalEmirati) : form.employmentTotalEmirati || '');
+
+ const today = getToday();
+
+ let createdRecordId = null;
+ let apiResultData = null;
+ if (modalMode !== 'edit') {
+ const apiPayload = {
+ establishment_code: form.establishmentId || '',
+ factory_name: form.establishmentName || form.contactName || '',
+ email: form.contactEmail || form.userProfileEmail || '',
+ permanent_factory_code: form.permanentFactoryCode || '',
+ industry_code: form.industryCodeCurrent || form.industryCodeBusiness || '',
+ license_number: form.uniqueLicenseNumber || '',
+ isic_code: form.industryCodeBusiness || form.isicCode || '',
+ emirate: form.emirate || form.contactEmirate || '',
+ emirati_male: Number(form.employmentEmiratiMale) || 0,
+ emirati_female: Number(form.employmentEmiratiFemale) || 0,
+ non_emirati_male: Number(form.employmentNonEmiratiMale) || 0,
+ non_emirati_female: Number(form.employmentNonEmiratiFemale) || 0,
+ total_emirati: Number(totalEmirati) || 0,
+ total_employees: Number(employmentTotals) || 0,
+ establishment_user: {
+ name: form.userProfileName || '',
+ email: form.userProfileEmail || '',
+ password: form.userProfilePassword || '',
+ },
+ };
+ const apiResponse = await createEstablishment(apiPayload);
+ const successMessage = apiResponse?.message || 'Establishment added successfully.';
+ showToast('success', successMessage);
+ const createdRecord = apiResponse?.data;
+ createdRecordId = createdRecord?.id ?? createdRecord?.establishment?.id ?? null;
+ apiResultData = createdRecord;
+ } else {
+ const targetId = form.apiId ?? profiles[editingRow]?.apiId ?? null;
+ if (targetId === null || targetId === undefined) {
+ throw new Error('Unable to update establishment. Missing identifier.');
+ }
+ const establishmentUserPayload = {
+ name: form.userProfileName || '',
+ email: form.userProfileEmail || '',
+ };
+ if (form.userProfilePassword) {
+ establishmentUserPayload.password = form.userProfilePassword;
+ }
+ if (!establishmentUserPayload.name && !establishmentUserPayload.email && !establishmentUserPayload.password) {
+ delete establishmentUserPayload.name;
+ delete establishmentUserPayload.email;
+ }
+ const updatePayload = {
+ establishment_code: form.establishmentId || '',
+ factory_name: form.establishmentName || form.contactName || '',
+ email: form.contactEmail || form.userProfileEmail || '',
+ permanent_factory_code: form.permanentFactoryCode || '',
+ industry_code: form.industryCodeCurrent || form.industryCodeBusiness || '',
+ license_number: form.uniqueLicenseNumber || '',
+ isic_code: form.industryCodeBusiness || form.isicCode || '',
+ emirate: form.emirate || form.contactEmirate || '',
+ emirati_male: Number(form.employmentEmiratiMale) || 0,
+ emirati_female: Number(form.employmentEmiratiFemale) || 0,
+ non_emirati_male: Number(form.employmentNonEmiratiMale) || 0,
+ non_emirati_female: Number(form.employmentNonEmiratiFemale) || 0,
+ total_emirati: Number(totalEmirati) || 0,
+ total_employees: Number(employmentTotals) || 0,
+ };
+ if (establishmentUserPayload.name || establishmentUserPayload.email || establishmentUserPayload.password) {
+ updatePayload.establishment_user = establishmentUserPayload;
+ }
+ const apiResponse = await updateEstablishment(targetId, updatePayload);
+ const successMessage = apiResponse?.message || 'Establishment updated successfully.';
+ showToast('success', successMessage);
+ apiResultData = apiResponse?.data;
+ }
+
+ const payload = {
+ ...form,
+ products: form.products || '0',
+ totalEmployees: computedTotalEmployees,
+ employmentTotalEmployees: computedTotalEmployees,
+ employmentTotalEmirati: computedTotalEmirati,
+ createdBy: form.createdBy || 'Admin',
+ createdOn: form.createdOn || today,
+ lastUpdated: today,
+ corporateSameAs: Boolean(form.corporateSameAs),
+ requiresPasswordReset: modalMode !== 'edit' ? true : Boolean(form.requiresPasswordReset),
+ lastPasswordSetAt: form.userProfilePassword ? today : form.lastPasswordSetAt || today,
+ };
+
+ const nextPasswordHistory = form.userProfilePassword
+ ? [{ value: form.userProfilePassword, changedAt: today }, ...(Array.isArray(form.passwordHistory) ? form.passwordHistory : [])].slice(
+ 0,
+ MAX_PASSWORD_HISTORY
+ )
+ : form.passwordHistory;
+
+ const nextForm = {
+ ...payload,
+ corporateSameAs: Boolean(payload.corporateSameAs),
+ passwordHistory: nextPasswordHistory,
+ apiId: modalMode === 'edit' ? form.apiId ?? profiles[editingRow]?.apiId ?? null : createdRecordId,
+ };
+
+ if (apiResultData) {
+ const mappedFromApi = mapApiEstablishmentToProfile(apiResultData);
+ nextForm.apiId = mappedFromApi.apiId ?? nextForm.apiId;
+ Object.assign(nextForm, mappedFromApi);
+ }
+
+ await new Promise((resolve) => requestAnimationFrame(resolve));
+ if (modalMode === 'edit' && editingRow !== null) {
+ const updatedProfiles = [...profiles];
+ updatedProfiles[editingRow] = nextForm;
+ setProfiles(updatedProfiles);
+ } else {
+ setProfiles((prev) => [nextForm, ...prev]);
+ }
+ const emptyProfile = createEmptyProfile();
+ Object.assign(emptyProfile, computeEmploymentTotals(emptyProfile));
+ setForm(emptyProfile);
+ setModalMode(null);
+ setEditingRow(null);
+ } catch (error) {
+ const message = error?.response?.data?.message || error?.message || 'Failed to save profile. Please try again.';
+ setSaveError(message);
+ setSaveWarning(false);
+ return;
+ } finally {
+ setIsSaving(false);
+ const elapsed = performance.now() - startTime;
+ if (elapsed > 1200) {
+ setSaveWarning(true);
+ }
+ }
};
+ React.useEffect(() => {
+ if (!modalMode) return undefined;
+ const handleBeforeUnload = (event) => {
+ if (isDirty) {
+ event.preventDefault();
+ event.returnValue = '';
+ }
+ };
+ window.addEventListener('beforeunload', handleBeforeUnload);
+ return () => {
+ window.removeEventListener('beforeunload', handleBeforeUnload);
+ };
+ }, [isDirty, modalMode]);
+
const deletingProfile = React.useMemo(() => {
if (deletingRow === null || deletingRow < 0 || deletingRow >= profiles.length) return null;
return profiles[deletingRow];
@@ -491,6 +1777,52 @@ const CompanyProfile = () => {
return (
+ {toast && (
+
+
+
+ {toast.type === 'success' ? (
+
+ ) : (
+
+ )}
+
{toast.message}
+
+
+
+
+
+
+ )}
Company Profile
@@ -528,6 +1860,8 @@ const CompanyProfile = () => {
onPageChange: setCurrentPage,
pageSize,
totalItems: filteredProfiles.length,
+ pageSizeOptions,
+ onPageSizeChange: setPageSize,
}}
/>
@@ -535,17 +1869,19 @@ const CompanyProfile = () => {
-
+
{modalMode === 'edit' ? 'Edit Company Profile' : 'Add Profile'}
@@ -561,394 +1897,105 @@ const CompanyProfile = () => {
-
-
-
-
Identification Particulars
-
-
-
-
-
-
-
+
+
+
+
+ {renderStepContent()}
-
-
-
-
Establishment Contact Details
-
Provide the primary contact information for the establishment.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Corporate/Head Office Contact Details
-
Provide corporate office details or copy from establishment contact details.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Employment in Establishment
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Administrative Details
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
- Cancel
-
-
- {modalMode === 'edit' ? 'Update' : 'Add'}
-
+
+
+ {isSaving ? (
+
+
+ Saving profile…
+
+ ) : saveError ? (
+
{saveError}
+ ) : saveWarning ? (
+ 'Save completed, but took longer than expected. Please verify the result.'
+ ) : (
+ '\u00A0'
+ )}
+
+ {activeStep === formSteps.length - 1 ? (
+
+
+ Cancel
+
+
+ {isSaving ? 'Saving…' : modalMode === 'edit' ? 'Update' : 'Add'}
+
+
+ ) : (
+
+ Next
+
+ )}
@@ -956,7 +2003,10 @@ const CompanyProfile = () => {
{showDeleteConfirm && deletingProfile && (
-
+
{
Cancel
- Delete
+ {isDeletingApi ? 'Deleting…' : 'Delete'}
diff --git a/ipi-survey-platform/src/services/establishments/establishmentService.js b/ipi-survey-platform/src/services/establishments/establishmentService.js
index f1eaf32..dc15d1c 100644
--- a/ipi-survey-platform/src/services/establishments/establishmentService.js
+++ b/ipi-survey-platform/src/services/establishments/establishmentService.js
@@ -1,9 +1,39 @@
-import { getRequest } from '../api/CommonService.js';
+import { getRequest, postRequest, putRequest, deleteRequest } from '../api/CommonService.js';
import resolveEstablishmentId from '../utils/establishment.js';
const dashboardEndpoint = '/establishment_dashboard';
const establishmentsEndpoint = '/establishments';
+export const fetchEstablishments = async (config = {}) => {
+ const response = await getRequest(establishmentsEndpoint, config);
+ return response.data;
+};
+
+export const createEstablishment = async (payload, config = {}) => {
+ const response = await postRequest(establishmentsEndpoint, payload, config);
+ return response.data;
+};
+
+export const deleteEstablishment = async (establishmentId, config = {}) => {
+ const resolvedId = resolveEstablishmentId(establishmentId);
+ if (resolvedId === undefined) {
+ throw new Error('Missing establishment ID for delete request.');
+ }
+ const url = `${establishmentsEndpoint}/${resolvedId}`;
+ const response = await deleteRequest(url, config);
+ return response.data;
+};
+
+export const updateEstablishment = async (establishmentId, payload, config = {}) => {
+ const resolvedId = resolveEstablishmentId(establishmentId);
+ if (resolvedId === undefined) {
+ throw new Error('Missing establishment ID for update request.');
+ }
+ const url = `${establishmentsEndpoint}/${resolvedId}`;
+ const response = await putRequest(url, payload, config);
+ return response.data;
+};
+
export const fetchEstablishmentDashboard = async (establishmentId, config = {}) => {
const resolvedId = resolveEstablishmentId(establishmentId);
if (resolvedId === undefined) {
@@ -25,6 +55,10 @@ export const fetchEstablishmentDetail = async (establishmentId, config = {}) =>
};
export default {
+ fetchEstablishments,
fetchEstablishmentDashboard,
fetchEstablishmentDetail,
+ createEstablishment,
+ updateEstablishment,
+ deleteEstablishment,
};