create form and modal components, update datatable persistant filtering
This commit is contained in:
parent
b70e08026d
commit
8d9bb81fe2
23 changed files with 3502 additions and 74 deletions
165
frontend/src/components/common/DataTable.vue
Normal file
165
frontend/src/components/common/DataTable.vue
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<template lang="html">
|
||||
<DataTable
|
||||
:value="data"
|
||||
:rowsPerPageOptions="[5, 10, 20, 50]"
|
||||
:paginator="true"
|
||||
:rows="10"
|
||||
sortMode="multiple"
|
||||
removableSort
|
||||
filterDisplay="row"
|
||||
v-model:filters="filterRef"
|
||||
scrollable
|
||||
scrollHeight="70vh"
|
||||
v-model:selection="selectedRows"
|
||||
selectionMode="multiple"
|
||||
metaKeySelection="true"
|
||||
dataKey="id"
|
||||
>
|
||||
<Column
|
||||
v-for="col in columns"
|
||||
:key="col.fieldName"
|
||||
:field="col.fieldName"
|
||||
:header="col.label"
|
||||
:sortable="col.sortable"
|
||||
>
|
||||
<template v-if="col.filterable === true" #filter="{ filterModel, filterCallback }">
|
||||
<InputText
|
||||
v-model="filterModel.value"
|
||||
type="text"
|
||||
@input="handleFilterInput(col.fieldName, filterModel.value, filterCallback)"
|
||||
:placeholder="`Search ${col.label}...`"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="col.type === 'status'" #body="slotProps">
|
||||
<Tag
|
||||
:value="slotProps.data[col.fieldName]"
|
||||
:severity="getBadgeColor(slotProps.data[col.fieldName])"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="col.type === 'button'" #body="slotProps">
|
||||
<Button
|
||||
:label="slotProps.data[col.fieldName]"
|
||||
size="small"
|
||||
severity="info"
|
||||
@click="$emit('rowClick', slotProps)"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</template>
|
||||
<script setup>
|
||||
import { defineProps, computed, onMounted, watch } from "vue";
|
||||
import DataTable from "primevue/datatable";
|
||||
import Column from "primevue/column";
|
||||
import Tag from "primevue/tag";
|
||||
import Button from "primevue/button";
|
||||
import InputText from "primevue/inputtext";
|
||||
import { ref } from "vue";
|
||||
import { FilterMatchMode } from "@primevue/core";
|
||||
import { useFiltersStore } from "../../stores/filters";
|
||||
|
||||
const filtersStore = useFiltersStore();
|
||||
|
||||
const props = defineProps({
|
||||
columns: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
filters: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
|
||||
}),
|
||||
},
|
||||
tableName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["rowClick"]);
|
||||
|
||||
// Initialize filters in store when component mounts
|
||||
onMounted(() => {
|
||||
filtersStore.initializeTableFilters(props.tableName, props.columns);
|
||||
});
|
||||
|
||||
// Get filters from store, with fallback to props.filters
|
||||
const filterRef = computed({
|
||||
get() {
|
||||
const storeFilters = filtersStore.getTableFilters(props.tableName);
|
||||
// Merge store filters with any additional filters from props
|
||||
return { ...props.filters, ...storeFilters };
|
||||
},
|
||||
set(newFilters) {
|
||||
// Update store when filters change
|
||||
Object.keys(newFilters).forEach(key => {
|
||||
if (key !== 'global' && newFilters[key]) {
|
||||
const filter = newFilters[key];
|
||||
filtersStore.updateTableFilter(
|
||||
props.tableName,
|
||||
key,
|
||||
filter.value,
|
||||
filter.matchMode
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for filter changes to sync match mode changes
|
||||
watch(filterRef, (newFilters) => {
|
||||
Object.keys(newFilters).forEach(key => {
|
||||
if (key !== 'global' && newFilters[key]) {
|
||||
const filter = newFilters[key];
|
||||
const storeFilter = filtersStore.getTableFilters(props.tableName)[key];
|
||||
|
||||
// Only update if the match mode has actually changed
|
||||
if (storeFilter && storeFilter.matchMode !== filter.matchMode) {
|
||||
filtersStore.updateTableFilter(
|
||||
props.tableName,
|
||||
key,
|
||||
filter.value,
|
||||
filter.matchMode
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, { deep: true });
|
||||
|
||||
const selectedRows = ref();
|
||||
|
||||
// Handle filter input changes
|
||||
const handleFilterInput = (fieldName, value, filterCallback) => {
|
||||
// Get the current filter to preserve the match mode
|
||||
const currentFilter = filterRef.value[fieldName];
|
||||
const matchMode = currentFilter?.matchMode || FilterMatchMode.CONTAINS;
|
||||
|
||||
// Update the store with both value and match mode
|
||||
filtersStore.updateTableFilter(props.tableName, fieldName, value, matchMode);
|
||||
// Call the PrimeVue filter callback
|
||||
filterCallback();
|
||||
};
|
||||
|
||||
const getBadgeColor = (status) => {
|
||||
console.log("DEBUG: - getBadgeColor status", status);
|
||||
switch (status?.toLowerCase()) {
|
||||
case "completed":
|
||||
return "success"; // green
|
||||
case "in progress":
|
||||
return "warn";
|
||||
case "not started":
|
||||
return "danger"; // red
|
||||
default:
|
||||
return "info"; // blue fallback
|
||||
}
|
||||
};
|
||||
console.log("DEBUG: - DataTable props.columns", props.columns);
|
||||
console.log("DEBUG: - DataTable props.data", props.data);
|
||||
</script>
|
||||
<style lang="">
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue