moving towards real data
This commit is contained in:
parent
ac3c05cb78
commit
40c4a5a37f
8 changed files with 303 additions and 84 deletions
|
|
@ -34,7 +34,7 @@ const createButtons = ref([
|
|||
{
|
||||
label: "Client",
|
||||
command: () => {
|
||||
modalStore.openCreateClient();
|
||||
modalStore.openModal("createClient");
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template lang="html">
|
||||
<!-- Filter Controls Panel -->
|
||||
<div v-if="lazy && hasFilters" class="filter-controls-panel mb-3 p-3 bg-light rounded">
|
||||
<div v-if="hasFilters" class="filter-controls-panel mb-3 p-3 bg-light rounded">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div v-for="col in filterableColumns" :key="col.fieldName" class="col-md-4 col-lg-3">
|
||||
<label :for="`filter-${col.fieldName}`" class="form-label small fw-semibold">
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Page Jump Controls -->
|
||||
<div v-if="lazy && totalPages > 1" class="page-controls-panel mb-3 p-2 bg-light rounded">
|
||||
<div v-if="totalPages > 1" class="page-controls-panel mb-3 p-2 bg-light rounded">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-auto">
|
||||
<small class="text-muted">Quick navigation:</small>
|
||||
|
|
@ -69,12 +69,13 @@
|
|||
</div>
|
||||
|
||||
<DataTable
|
||||
ref="dataTableRef"
|
||||
:value="data"
|
||||
:rowsPerPageOptions="[5, 10, 20, 50]"
|
||||
:paginator="true"
|
||||
:rows="currentRows"
|
||||
:lazy="lazy"
|
||||
:totalRecords="lazy ? totalRecords : data.length"
|
||||
:totalRecords="lazy ? totalRecords : getFilteredDataLength"
|
||||
@page="handlePage"
|
||||
@sort="handleSort"
|
||||
@filter="handleFilter"
|
||||
|
|
@ -201,6 +202,11 @@ const props = defineProps({
|
|||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
// Total filtered records for non-lazy tables (when server-side filtering is used)
|
||||
totalFilteredRecords: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
// Custom pagination event handler
|
||||
onLazyLoad: {
|
||||
type: Function,
|
||||
|
|
@ -208,7 +214,14 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["rowClick", "lazy-load", "page-change", "sort-change", "filter-change"]);
|
||||
const emit = defineEmits([
|
||||
"rowClick",
|
||||
"lazy-load",
|
||||
"page-change",
|
||||
"sort-change",
|
||||
"filter-change",
|
||||
"data-refresh",
|
||||
]);
|
||||
|
||||
// Computed loading state that considers both prop and global store
|
||||
const loading = computed(() => {
|
||||
|
|
@ -292,6 +305,8 @@ watch(
|
|||
const selectedRows = ref();
|
||||
const pendingFilters = ref({});
|
||||
const selectedPageJump = ref("");
|
||||
const dataTableRef = ref();
|
||||
const currentPageState = ref({ page: 0, first: 0 }); // Track current page for non-lazy tables
|
||||
|
||||
// Computed properties for filtering
|
||||
const filterableColumns = computed(() => {
|
||||
|
|
@ -319,8 +334,12 @@ const hasFilterChanges = computed(() => {
|
|||
});
|
||||
|
||||
const totalPages = computed(() => {
|
||||
if (!props.lazy) return 0;
|
||||
return paginationStore.getTotalPages(props.tableName);
|
||||
if (props.lazy) {
|
||||
return paginationStore.getTotalPages(props.tableName);
|
||||
}
|
||||
// For non-lazy tables, calculate based on current filtered data
|
||||
const filteredDataLength = getFilteredDataLength.value;
|
||||
return Math.ceil(filteredDataLength / currentRows.value) || 1;
|
||||
});
|
||||
|
||||
// Initialize pending filters from store
|
||||
|
|
@ -331,6 +350,17 @@ onMounted(() => {
|
|||
});
|
||||
});
|
||||
|
||||
// Computed property to get filtered data length for non-lazy tables
|
||||
const getFilteredDataLength = computed(() => {
|
||||
if (props.lazy) {
|
||||
return props.totalRecords;
|
||||
}
|
||||
|
||||
// For non-lazy tables, use totalFilteredRecords if provided (server-side filtering),
|
||||
// otherwise fall back to data length (client-side filtering)
|
||||
return props.totalFilteredRecords !== null ? props.totalFilteredRecords : props.data.length;
|
||||
});
|
||||
|
||||
// Filter management methods
|
||||
const applyFilters = () => {
|
||||
// Update store with pending filter values
|
||||
|
|
@ -344,10 +374,16 @@ const applyFilters = () => {
|
|||
);
|
||||
});
|
||||
|
||||
// For lazy tables, reset to first page and trigger reload
|
||||
// Reset to first page when filters change (for both lazy and non-lazy)
|
||||
currentPageState.value = { page: 0, first: 0 };
|
||||
|
||||
// For both lazy and non-lazy tables, trigger reload with new filters
|
||||
if (props.lazy) {
|
||||
paginationStore.resetToFirstPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
} else {
|
||||
// For non-lazy tables, also trigger a reload to get fresh data from server
|
||||
triggerDataRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -360,10 +396,16 @@ const clearFilters = () => {
|
|||
// Clear store filters
|
||||
filtersStore.clearTableFilters(props.tableName);
|
||||
|
||||
// For lazy tables, reset to first page and trigger reload
|
||||
// Reset to first page when filters are cleared (for both lazy and non-lazy)
|
||||
currentPageState.value = { page: 0, first: 0 };
|
||||
|
||||
// For both lazy and non-lazy tables, trigger reload with cleared filters
|
||||
if (props.lazy) {
|
||||
paginationStore.resetToFirstPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
} else {
|
||||
// For non-lazy tables, also trigger a reload to get fresh data from server
|
||||
triggerDataRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -383,21 +425,53 @@ const getActiveFiltersText = () => {
|
|||
|
||||
// Page navigation methods
|
||||
const jumpToPage = () => {
|
||||
if (selectedPageJump.value && props.lazy) {
|
||||
if (selectedPageJump.value) {
|
||||
const pageNumber = parseInt(selectedPageJump.value) - 1; // Convert to 0-based
|
||||
paginationStore.setPage(props.tableName, pageNumber);
|
||||
triggerLazyLoad();
|
||||
|
||||
if (props.lazy) {
|
||||
paginationStore.setPage(props.tableName, pageNumber);
|
||||
triggerLazyLoad();
|
||||
} else {
|
||||
// For non-lazy tables, update our internal state
|
||||
// The DataTable will handle the actual pagination
|
||||
currentPageState.value = {
|
||||
page: pageNumber,
|
||||
first: pageNumber * currentRows.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
selectedPageJump.value = ""; // Reset selection
|
||||
};
|
||||
|
||||
const getPageInfo = () => {
|
||||
return paginationStore.getPageInfo(props.tableName);
|
||||
if (props.lazy) {
|
||||
return paginationStore.getPageInfo(props.tableName);
|
||||
}
|
||||
|
||||
// For non-lazy tables, calculate based on current page state and filtered data
|
||||
const filteredTotal = getFilteredDataLength.value;
|
||||
const rows = currentRows.value;
|
||||
const currentFirst = currentPageState.value.first;
|
||||
const start = filteredTotal > 0 ? currentFirst + 1 : 0;
|
||||
const end = Math.min(filteredTotal, currentFirst + rows);
|
||||
|
||||
return {
|
||||
start,
|
||||
end,
|
||||
total: filteredTotal,
|
||||
};
|
||||
};
|
||||
|
||||
// Handle pagination events
|
||||
const handlePage = (event) => {
|
||||
console.log("Page event:", event);
|
||||
|
||||
// Update current page state for both lazy and non-lazy tables
|
||||
currentPageState.value = {
|
||||
page: event.page,
|
||||
first: event.first,
|
||||
};
|
||||
|
||||
if (props.lazy) {
|
||||
paginationStore.updateTablePagination(props.tableName, {
|
||||
page: event.page,
|
||||
|
|
@ -424,8 +498,11 @@ const handleSort = (event) => {
|
|||
// Handle filter events
|
||||
const handleFilter = (event) => {
|
||||
console.log("Filter event:", event);
|
||||
|
||||
// Reset to first page when filters change (for both lazy and non-lazy)
|
||||
currentPageState.value = { page: 0, first: 0 };
|
||||
|
||||
if (props.lazy) {
|
||||
// Reset to first page when filters change
|
||||
paginationStore.resetToFirstPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
|
|
@ -453,6 +530,37 @@ const triggerLazyLoad = () => {
|
|||
}
|
||||
};
|
||||
|
||||
// Trigger data refresh for non-lazy tables when filters change
|
||||
const triggerDataRefresh = () => {
|
||||
const filters = filtersStore.getTableFilters(props.tableName);
|
||||
const refreshEvent = {
|
||||
filters: filters,
|
||||
page: currentPageState.value.page,
|
||||
first: currentPageState.value.first,
|
||||
rows: currentRows.value,
|
||||
};
|
||||
|
||||
console.log("Triggering data refresh with:", refreshEvent);
|
||||
emit("data-refresh", refreshEvent);
|
||||
};
|
||||
|
||||
const handleFilterInput = (fieldName, value, filterCallback) => {
|
||||
// Update the filter store
|
||||
filtersStore.updateTableFilter(props.tableName, fieldName, value, FilterMatchMode.CONTAINS);
|
||||
|
||||
// Call the PrimeVue callback to update the filter
|
||||
if (filterCallback) {
|
||||
filterCallback();
|
||||
}
|
||||
|
||||
// For non-lazy tables, also trigger a data refresh when individual filters change
|
||||
if (!props.lazy) {
|
||||
// Reset to first page when filters change
|
||||
currentPageState.value = { page: 0, first: 0 };
|
||||
triggerDataRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
const getBadgeColor = (status) => {
|
||||
console.log("DEBUG: - getBadgeColor status", status);
|
||||
switch (status?.toLowerCase()) {
|
||||
|
|
@ -498,6 +606,8 @@ defineExpose({
|
|||
refresh: () => {
|
||||
if (props.lazy) {
|
||||
triggerLazyLoad();
|
||||
} else {
|
||||
triggerDataRefresh();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<form @submit.prevent="handleSubmit" class="dynamic-form">
|
||||
<form class="dynamic-form">
|
||||
<div class="form-container">
|
||||
<div class="form-row">
|
||||
<div
|
||||
|
|
@ -372,11 +372,12 @@
|
|||
<div v-if="showSubmitButton || showCancelButton" class="form-buttons">
|
||||
<Button
|
||||
v-if="showSubmitButton"
|
||||
type="submit"
|
||||
type="button"
|
||||
:label="submitButtonText"
|
||||
:loading="isLoading"
|
||||
:disabled="isFormDisabled"
|
||||
severity="primary"
|
||||
@click="handleSubmit"
|
||||
/>
|
||||
<Button
|
||||
v-if="showCancelButton"
|
||||
|
|
@ -839,9 +840,15 @@ const validateForm = () => {
|
|||
|
||||
// Handle form submission
|
||||
const handleSubmit = async () => {
|
||||
// Prevent double submission
|
||||
if (isSubmitting.value) {
|
||||
console.warn("Form: submission already in progress, ignoring duplicate submission");
|
||||
return;
|
||||
}
|
||||
|
||||
// Always validate on submit if enabled
|
||||
if (props.validateOnSubmit && !validateForm()) {
|
||||
console.warn("Form validation failed on submit");
|
||||
console.warn("Form: validation failed on submit");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -849,17 +856,23 @@ const handleSubmit = async () => {
|
|||
|
||||
try {
|
||||
const formData = getCurrentFormData();
|
||||
console.log("Form: emitting submit event with data:", formData);
|
||||
|
||||
// Only emit the submit event - let parent handle the actual submission
|
||||
// This prevents the dual submission pathway issue
|
||||
emit("submit", formData);
|
||||
|
||||
// Call onSubmit prop if provided (for backward compatibility)
|
||||
if (props.onSubmit && typeof props.onSubmit === "function") {
|
||||
await props.onSubmit(formData);
|
||||
}
|
||||
|
||||
emit("submit", formData);
|
||||
} catch (error) {
|
||||
console.error("Form submission error:", error);
|
||||
} finally {
|
||||
console.error("Form: submission error:", error);
|
||||
// Reset isSubmitting on error so user can retry
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
// Note: Don't reset isSubmitting.value here in finally - let parent control this
|
||||
// The parent should call the exposed stopLoading() method when done
|
||||
};
|
||||
|
||||
// Handle cancel action
|
||||
|
|
|
|||
|
|
@ -14,12 +14,15 @@
|
|||
</div>
|
||||
|
||||
<Form
|
||||
ref="formRef"
|
||||
:fields="formFields"
|
||||
:form-data="formData"
|
||||
:show-cancel-button="true"
|
||||
:validate-on-change="false"
|
||||
:validate-on-blur="true"
|
||||
:validate-on-submit="true"
|
||||
:loading="isSubmitting"
|
||||
:disable-on-loading="true"
|
||||
submit-button-text="Create Client"
|
||||
cancel-button-text="Cancel"
|
||||
@submit="handleSubmit"
|
||||
|
|
@ -41,6 +44,8 @@ const modalStore = useModalStore();
|
|||
// Modal visibility computed property
|
||||
const isVisible = computed(() => modalStore.isModalOpen("createClient"));
|
||||
const customerNames = ref([]);
|
||||
// Form reference for controlling its state
|
||||
const formRef = ref(null);
|
||||
// Form data
|
||||
const formData = reactive({
|
||||
customertype: "",
|
||||
|
|
@ -78,6 +83,16 @@ const modalOptions = {
|
|||
|
||||
// Form field definitions
|
||||
const formFields = computed(() => [
|
||||
{
|
||||
name: "addressTitle",
|
||||
label: "Address Title",
|
||||
type: "text",
|
||||
required: true,
|
||||
placeholder: "Enter address title",
|
||||
helpText: "A short title to identify this address (e.g., Johnson Home, Johnson Office)",
|
||||
cols: 12,
|
||||
md: 6,
|
||||
},
|
||||
{
|
||||
name: "customertype",
|
||||
label: "Client Type",
|
||||
|
|
@ -318,15 +333,38 @@ function getStatusIcon(type) {
|
|||
}
|
||||
}
|
||||
|
||||
// Submission state to prevent double submission
|
||||
const isSubmitting = ref(false);
|
||||
|
||||
// Handle form submission
|
||||
async function handleSubmit() {
|
||||
async function handleSubmit(formDataFromEvent) {
|
||||
// Prevent double submission with detailed logging
|
||||
if (isSubmitting.value) {
|
||||
console.warn(
|
||||
"CreateClientModal: Form submission already in progress, ignoring duplicate submission",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
"CreateClientModal: Form submission started with data:",
|
||||
formDataFromEvent || formData,
|
||||
);
|
||||
|
||||
isSubmitting.value = true;
|
||||
|
||||
try {
|
||||
showStatusMessage("Creating client...", "info");
|
||||
|
||||
// Convert form data to the expected format
|
||||
// Use the form data from the event if provided, otherwise use reactive formData
|
||||
const dataToSubmit = formDataFromEvent || formData;
|
||||
|
||||
console.log("CreateClientModal: Calling API with data:", dataToSubmit);
|
||||
|
||||
// Call API to create client
|
||||
const response = await Api.createClient(formData);
|
||||
const response = await Api.createClient(dataToSubmit);
|
||||
|
||||
console.log("CreateClientModal: API response received:", response);
|
||||
|
||||
if (response && response.success) {
|
||||
showStatusMessage("Client created successfully!", "success");
|
||||
|
|
@ -339,8 +377,15 @@ async function handleSubmit() {
|
|||
throw new Error(response?.message || "Failed to create client");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating client:", error);
|
||||
console.error("CreateClientModal: Error creating client:", error);
|
||||
showStatusMessage(error.message || "Failed to create client. Please try again.", "error");
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
// Also reset the Form component's internal submission state
|
||||
if (formRef.value && formRef.value.stopLoading) {
|
||||
formRef.value.stopLoading();
|
||||
}
|
||||
console.log("CreateClientModal: Form submission completed, isSubmitting reset to false");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +396,7 @@ function handleCancel() {
|
|||
|
||||
// Handle modal close
|
||||
function handleClose() {
|
||||
modalStore.closeCreateClient();
|
||||
modalStore.closeModal("createClient");
|
||||
resetForm();
|
||||
}
|
||||
|
||||
|
|
@ -383,13 +428,9 @@ watch(isVisible, async () => {
|
|||
if (isVisible.value) {
|
||||
try {
|
||||
const names = await Api.getCustomerNames();
|
||||
console.log("Loaded customer names:", names);
|
||||
customerNames.value = names;
|
||||
} catch (error) {
|
||||
console.error("Error loading customer names:", error);
|
||||
// Set some test data to debug if autocomplete works
|
||||
customerNames.value = ["Test Customer 1", "Test Customer 2", "Another Client"];
|
||||
console.log("Using test customer names:", customerNames.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -90,6 +90,12 @@ const handleLazyLoad = async (event) => {
|
|||
});
|
||||
}
|
||||
|
||||
// Clear cache when filters are active to ensure fresh data
|
||||
const hasActiveFilters = Object.keys(filters).length > 0;
|
||||
if (hasActiveFilters) {
|
||||
paginationStore.clearTableCache("clients");
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
const cachedData = paginationStore.getCachedPage(
|
||||
"clients",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue