Added a Select component to the Actions section of the Datatable, and API methods to load the available status-es from the Task doctype to populate the select for the tasks list.

This commit is contained in:
rocketdebris 2026-01-13 16:39:52 -05:00
parent adb7bc5930
commit 0c52f3fc23
4 changed files with 88 additions and 7 deletions

View file

@ -13,6 +13,23 @@ def get_job_task_list(job_id=""):
return build_error_response(str(e), 500) return build_error_response(str(e), 500)
@frappe.whitelist()
def get_task_status_options():
print("DEBUG: Getting task status options")
try:
task_doctype = frappe.get_doc("DocType", "Task")
status_index = 0
for i, field in enumerate(task_doctype.fields):
if field.fieldname == "status":
status_index = i
break
options = task_doctype.fields[status_index].options.split()
print("DEBUG: Task Status options retreived", options)
return build_success_response(options)
except Exception as e:
return build_error_response(str(e), 500)
@frappe.whitelist() @frappe.whitelist()
def get_tasks_table_data(filters={}, sortings=[], page=1, page_size=10): def get_tasks_table_data(filters={}, sortings=[], page=1, page_size=10):
"""Get paginated task table data with filtering and sorting support.""" """Get paginated task table data with filtering and sorting support."""

View file

@ -23,6 +23,7 @@ const FRAPPE_GET_INSTALL_PROJECTS_METHOD = "custom_ui.api.db.jobs.get_install_pr
const FRAPPE_GET_JOB_TEMPLATES_METHOD = "custom_ui.api.db.jobs.get_job_templates"; const FRAPPE_GET_JOB_TEMPLATES_METHOD = "custom_ui.api.db.jobs.get_job_templates";
// Task methods // Task methods
const FRAPPE_GET_TASKS_METHOD = "custom_ui.api.db.tasks.get_tasks_table_data"; const FRAPPE_GET_TASKS_METHOD = "custom_ui.api.db.tasks.get_tasks_table_data";
const FRAPPE_GET_TASKS_STATUS_OPTIONS = "custom_ui.api.db.tasks.get_task_status_options";
// Invoice methods // Invoice methods
const FRAPPE_GET_INVOICES_METHOD = "custom_ui.api.db.invoices.get_invoice_table_data"; const FRAPPE_GET_INVOICES_METHOD = "custom_ui.api.db.invoices.get_invoice_table_data";
const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice"; const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice";
@ -384,6 +385,12 @@ class Api {
return result; return result;
} }
static async getTaskStatusOptions() {
console.log("DEBUG: API - Loading Task Status options form the backend.");
const result = await this.request(FRAPPE_GET_TASKS_STATUS_OPTIONS, {});
return result
}
// ============================================================================ // ============================================================================
// INVOICE / PAYMENT METHODS // INVOICE / PAYMENT METHODS
// ============================================================================ // ============================================================================

View file

@ -376,6 +376,18 @@
/> />
<!-- Dropdown menu would go here - could be implemented with PrimeVue Menu component --> <!-- Dropdown menu would go here - could be implemented with PrimeVue Menu component -->
</div> </div>
<!-- Select menu for a single action with multiple options -->
<div
v-if="rowActionsGrouped.select.length > 0"
class="dt-row-actions-select"
>
<Select
v-for="action in rowActionsGrouped.select"
:options="action.statusOptions"
:placeholder="action.label"
@change="handleSelectChange(action, slotProps.data, $event)"
/>
</div>
</div> </div>
</template> </template>
</Column> </Column>
@ -387,6 +399,7 @@ import DataTable from "primevue/datatable";
import Column from "primevue/column"; import Column from "primevue/column";
import Tag from "primevue/tag"; import Tag from "primevue/tag";
import Button from "primevue/button"; import Button from "primevue/button";
import Select from "primevue/select";
import InputText from "primevue/inputtext"; import InputText from "primevue/inputtext";
import { FilterMatchMode } from "@primevue/core"; import { FilterMatchMode } from "@primevue/core";
import { useFiltersStore } from "../../stores/filters"; import { useFiltersStore } from "../../stores/filters";
@ -701,6 +714,7 @@ const rowActionsGrouped = computed(() => {
), ),
secondary: rowActions.value.filter((action) => action.layout?.priority === "secondary"), secondary: rowActions.value.filter((action) => action.layout?.priority === "secondary"),
dropdown: rowActions.value.filter((action) => action.layout?.priority === "dropdown"), dropdown: rowActions.value.filter((action) => action.layout?.priority === "dropdown"),
select: rowActions.value.filter((action) => action.layout?.priority === "select"),
}; };
return groups; return groups;
}); });
@ -1018,6 +1032,22 @@ const handleActionClick = (action, rowData = null) => {
} }
}; };
const handleSelectChange = (action, rowData, event) => {
try {
if (typeof action.action === "function") {
if (rowData) {
// Row-specific action - pass row data
action.action(rowData, event.value);
} else {
// Global action - no row data needed
action.action();
}
}
} catch (error) {
console.error("Error executing action:", error);
}
}
const handleGlobalAction = (action) => { const handleGlobalAction = (action) => {
handleActionClick(action); handleActionClick(action);
}; };

View file

@ -36,6 +36,8 @@ const notifications = useNotificationStore();
const tableData = ref([]); const tableData = ref([]);
const totalRecords = ref(0); const totalRecords = ref(0);
const isLoading = ref(false); const isLoading = ref(false);
const showCompleted = ref(false);
const statusOptions = ref([]);
// Computed property to get current filters for the chart // Computed property to get current filters for the chart
const currentFilters = computed(() => { const currentFilters = computed(() => {
@ -63,6 +65,29 @@ const filterBy = (filter) => {
console.log("DEBUG: Tasks filterBy not implemented yet."); console.log("DEBUG: Tasks filterBy not implemented yet.");
}; };
const tableActions = [
{
label: "Show Completed",
action: () => {
showCompleted = !showCompleted
},
type: "button",
style: "info",
},
{
label: "Set Status",
action: (rowData, newValue) => {
console.log("DEBUG: Row Data:", rowData);
console.log("DEBUG: New Value:", newValue);
},
rowAction: true,
layout: {
priority: "select"
},
statusOptions: statusOptions.value
},
];
// Handle lazy loading events from DataTable // Handle lazy loading events from DataTable
const handleLazyLoad = async (event) => { const handleLazyLoad = async (event) => {
console.log("Tasks page - handling lazy load:", event); console.log("Tasks page - handling lazy load:", event);
@ -154,13 +179,11 @@ const handlePropertyClick = (link, rowData) => {
// router.push(`/task?taskId=${rowData.name}`); // router.push(`/task?taskId=${rowData.name}`);
//} //}
watch( watch(showCompleted, () => {
() => filtersStore.getTableFilters("clients"), if (showCompleted) {
async () => { // TODO: Logic for filtering on "Completed" goes here
await refreshStatusCounts(); }
}, });
{ deep: true },
);
// Load initial data // Load initial data
onMounted(async () => { onMounted(async () => {
@ -175,6 +198,10 @@ onMounted(async () => {
const initialFilters = filtersStore.getTableFilters("tasks"); const initialFilters = filtersStore.getTableFilters("tasks");
const initialSorting = filtersStore.getTableSorting("tasks"); const initialSorting = filtersStore.getTableSorting("tasks");
const optionsResult = await Api.getTaskStatusOptions();
statusOptions.value = optionsResult;
console.log("DEBUG: Loaded Status options: ", statusOptions.value)
await handleLazyLoad({ await handleLazyLoad({
page: initialPagination.page, page: initialPagination.page,
rows: initialPagination.rows, rows: initialPagination.rows,