From 172927e0698b27fdba8b7e0237455b4839919783 Mon Sep 17 00:00:00 2001 From: Casey Wittrock Date: Wed, 12 Nov 2025 15:26:39 -0600 Subject: [PATCH] add changes --- custom_ui/api/db/warranties.py | 146 +++++++------- frontend/src/api.js | 5 +- frontend/src/stores/notifications.js | 272 --------------------------- 3 files changed, 79 insertions(+), 344 deletions(-) delete mode 100644 frontend/src/stores/notifications.js diff --git a/custom_ui/api/db/warranties.py b/custom_ui/api/db/warranties.py index ec4c4b7..d313ebc 100644 --- a/custom_ui/api/db/warranties.py +++ b/custom_ui/api/db/warranties.py @@ -1,6 +1,6 @@ import frappe, json, re from datetime import datetime, date -from custom_ui.db_utils import process_query_conditions, build_datatable_response, get_count_or_filters +from custom_ui.db_utils import build_error_response, build_success_response, process_query_conditions, build_datatable_dict, get_count_or_filters # =============================================================================== # WARRANTY MANAGEMENT API METHODS @@ -9,78 +9,84 @@ from custom_ui.db_utils import process_query_conditions, build_datatable_respons @frappe.whitelist() def get_warranty_claims(filters={}, sortings=[], page=1, page_size=10): """Get paginated warranty claims table data with filtering and sorting support.""" - print("DEBUG: Raw warranty options received:", filters, sortings, page, page_size) - - processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size) - - # Handle count with proper OR filter support - if is_or: - count = frappe.db.sql(*get_count_or_filters("Warranty Claim", processed_filters))[0][0] - else: - count = frappe.db.count("Warranty Claim", filters=processed_filters) - - warranty_claims = frappe.db.get_all( - "Warranty Claim", - fields=["*"], - filters=processed_filters if not is_or else None, - or_filters=processed_filters if is_or else None, - limit=page_size, - start=(page - 1) * page_size, - order_by=processed_sortings - ) - - tableRows = [] - for warranty in warranty_claims: - tableRow = {} - tableRow["id"] = warranty["name"] - tableRow["warrantyId"] = warranty["name"] - tableRow["customer"] = warranty.get("customer_name", "") - tableRow["serviceAddress"] = warranty.get("service_address", warranty.get("address_display", "")) - - # Extract a brief description from the complaint HTML - complaint_text = warranty.get("complaint", "") - if complaint_text: - # Simple HTML stripping for display - take first 100 chars - clean_text = re.sub('<.*?>', '', complaint_text) - clean_text = clean_text.strip() - if len(clean_text) > 100: - clean_text = clean_text[:100] + "..." - tableRow["issueDescription"] = clean_text + try: + print("DEBUG: Raw warranty options received:", filters, sortings, page, page_size) + + processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size) + + # Handle count with proper OR filter support + if is_or: + count = frappe.db.sql(*get_count_or_filters("Warranty Claim", processed_filters))[0][0] else: - tableRow["issueDescription"] = "" + count = frappe.db.count("Warranty Claim", filters=processed_filters) + + warranty_claims = frappe.db.get_all( + "Warranty Claim", + fields=["*"], + filters=processed_filters if not is_or else None, + or_filters=processed_filters if is_or else None, + limit=page_size, + start=(page - 1) * page_size, + order_by=processed_sortings + ) + + tableRows = [] + for warranty in warranty_claims: + tableRow = {} + tableRow["id"] = warranty["name"] + tableRow["warrantyId"] = warranty["name"] + tableRow["customer"] = warranty.get("customer_name", "") + tableRow["serviceAddress"] = warranty.get("service_address", warranty.get("address_display", "")) - tableRow["status"] = warranty.get("status", "") - tableRow["complaintDate"] = warranty.get("complaint_date", "") - tableRow["complaintRaisedBy"] = warranty.get("complaint_raised_by", "") - tableRow["fromCompany"] = warranty.get("from_company", "") - tableRow["territory"] = warranty.get("territory", "") - tableRow["resolutionDate"] = warranty.get("resolution_date", "") - tableRow["warrantyStatus"] = warranty.get("warranty_amc_status", "") - - # Add priority based on status and date (can be customized) - if warranty.get("status") == "Open": - # Calculate priority based on complaint date - if warranty.get("complaint_date"): - complaint_date = warranty.get("complaint_date") - if isinstance(complaint_date, str): - complaint_date = datetime.strptime(complaint_date, "%Y-%m-%d").date() - elif isinstance(complaint_date, datetime): - complaint_date = complaint_date.date() - - days_old = (date.today() - complaint_date).days - if days_old > 7: - tableRow["priority"] = "High" - elif days_old > 3: - tableRow["priority"] = "Medium" - else: - tableRow["priority"] = "Low" + # Extract a brief description from the complaint HTML + complaint_text = warranty.get("complaint", "") + if complaint_text: + # Simple HTML stripping for display - take first 100 chars + clean_text = re.sub('<.*?>', '', complaint_text) + clean_text = clean_text.strip() + if len(clean_text) > 100: + clean_text = clean_text[:100] + "..." + tableRow["issueDescription"] = clean_text else: - tableRow["priority"] = "Medium" - else: - tableRow["priority"] = "Low" - tableRows.append(tableRow) - - return build_datatable_response(data=tableRows, count=count, page=page, page_size=page_size) + tableRow["issueDescription"] = "" + + tableRow["status"] = warranty.get("status", "") + tableRow["complaintDate"] = warranty.get("complaint_date", "") + tableRow["complaintRaisedBy"] = warranty.get("complaint_raised_by", "") + tableRow["fromCompany"] = warranty.get("from_company", "") + tableRow["territory"] = warranty.get("territory", "") + tableRow["resolutionDate"] = warranty.get("resolution_date", "") + tableRow["warrantyStatus"] = warranty.get("warranty_amc_status", "") + + # Add priority based on status and date (can be customized) + if warranty.get("status") == "Open": + # Calculate priority based on complaint date + if warranty.get("complaint_date"): + complaint_date = warranty.get("complaint_date") + if isinstance(complaint_date, str): + complaint_date = datetime.strptime(complaint_date, "%Y-%m-%d").date() + elif isinstance(complaint_date, datetime): + complaint_date = complaint_date.date() + + days_old = (date.today() - complaint_date).days + if days_old > 7: + tableRow["priority"] = "High" + elif days_old > 3: + tableRow["priority"] = "Medium" + else: + tableRow["priority"] = "Low" + else: + tableRow["priority"] = "Medium" + else: + tableRow["priority"] = "Low" + tableRows.append(tableRow) + + tableDataDict = build_datatable_dict(data=tableRows, count=count, page=page, page_size=page_size) + return build_success_response(tableDataDict) + except frappe.ValidationError as ve: + return build_error_response(str(ve), 400) + except Exception as e: + return build_error_response(str(e), 500) @frappe.whitelist() diff --git a/frontend/src/api.js b/frontend/src/api.js index 215d0fa..8dc3451 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -6,6 +6,7 @@ const FRAPPE_UPSERT_ESTIMATE_METHOD = "custom_ui.api.db.estimates.upsert_estimat const FRAPPE_GET_JOBS_METHOD = "custom_ui.api.db.get_jobs"; const FRAPPE_UPSERT_JOB_METHOD = "custom_ui.api.db.jobs.upsert_job"; const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice"; +const FRAPPE_GET_WARRANTY_CLAIMS_METHOD = "custom_ui.api.db.warranties.get_warranty_claims"; const FRAPPE_UPSERT_CLIENT_METHOD = "custom_ui.api.db.clients.upsert_client"; const FRAPPE_GET_CLIENT_STATUS_COUNTS_METHOD = "custom_ui.api.db.clients.get_client_status_counts"; const FRAPPE_GET_CLIENT_TABLE_DATA_METHOD = "custom_ui.api.db.clients.get_clients_table_data"; @@ -73,7 +74,7 @@ class Api { } static async getWarrantyData() { - const data = DataUtils.dummyWarrantyData; + const data = await this.request(FRAPPE_GET_WARRANTY_CLAIMS_METHOD); console.log("DEBUG: API - getWarrantyData result: ", data); return data; } @@ -180,7 +181,7 @@ class Api { console.log("DEBUG: API - Sending warranty options to backend:", options); - const result = await this.request("custom_ui.api.db.get_warranty_claims", { options }); + const result = await this.request(FRAPPE_GET_WARRANTY_CLAIMS_METHOD, { options }); return result; } diff --git a/frontend/src/stores/notifications.js b/frontend/src/stores/notifications.js deleted file mode 100644 index d2fd1af..0000000 --- a/frontend/src/stores/notifications.js +++ /dev/null @@ -1,272 +0,0 @@ -import { defineStore } from "pinia"; - -// Global toast instance - will be set during app initialization -let toastInstance = null; - -export const useNotificationStore = defineStore("notifications", { - state: () => ({ - // Configuration for PrimeVue Toast - defaultLife: 4000, - position: "top-right", - }), - - getters: { - // Helper to check if toast is available - isToastAvailable: () => !!toastInstance, - }, - - actions: { - // Add a new notification - addNotification(notification) { - const newNotification = { - id: this.nextId++, - type: notification.type || "info", // info, success, warning, error - title: notification.title || "", - message: notification.message || "", - duration: notification.duration ?? this.defaultDuration, - persistent: notification.persistent || false, // If true, won't auto-dismiss - actions: notification.actions || [], // Array of action buttons - data: notification.data || null, // Any additional data - timestamp: new Date().toISOString(), - dismissed: false, - seen: false, - }; - - // Add to beginning of array (newest first) - this.notifications.unshift(newNotification); - - // Trim notifications if we exceed max count - if (this.notifications.length > this.maxNotifications * 2) { - // Keep twice the max to maintain some history - this.notifications = this.notifications.slice(0, this.maxNotifications * 2); - } - - // Auto-dismiss if not persistent - if (!newNotification.persistent && newNotification.duration > 0) { - setTimeout(() => { - this.dismissNotification(newNotification.id); - }, newNotification.duration); - } - - return newNotification.id; - }, - - // Convenience methods for different types of notifications - addSuccess(message, title = "Success", options = {}) { - return this.addNotification({ - type: "success", - title, - message, - ...options, - }); - }, - - addError(message, title = "Error", options = {}) { - return this.addNotification({ - type: "error", - title, - message, - duration: options.duration ?? 6000, // Errors stay longer by default - ...options, - }); - }, - - addWarning(message, title = "Warning", options = {}) { - return this.addNotification({ - type: "warning", - title, - message, - ...options, - }); - }, - - addInfo(message, title = "Info", options = {}) { - return this.addNotification({ - type: "info", - title, - message, - ...options, - }); - }, - - // Dismiss a specific notification - dismissNotification(id) { - const notification = this.notifications.find((n) => n.id === id); - if (notification) { - notification.dismissed = true; - } - }, - - // Remove a notification completely - removeNotification(id) { - const index = this.notifications.findIndex((n) => n.id === id); - if (index !== -1) { - this.notifications.splice(index, 1); - } - }, - - // Mark notification as seen - markAsSeen(id) { - const notification = this.notifications.find((n) => n.id === id); - if (notification) { - notification.seen = true; - } - }, - - // Clear all notifications of a specific type - clearType(type) { - this.notifications = this.notifications.filter((n) => n.type !== type); - }, - - // Clear all notifications - clearAll() { - this.notifications = []; - }, - - // Clear all dismissed notifications - clearDismissed() { - this.notifications = this.notifications.filter((n) => !n.dismissed); - }, - - // Update notification content - updateNotification(id, updates) { - const notification = this.notifications.find((n) => n.id === id); - if (notification) { - Object.assign(notification, updates); - } - }, - - // Show a loading notification that can be updated - showLoadingNotification(message, title = "Loading...") { - return this.addNotification({ - type: "info", - title, - message, - persistent: true, - actions: [], - }); - }, - - // Update a loading notification to success - updateToSuccess(id, message, title = "Success") { - this.updateNotification(id, { - type: "success", - title, - message, - persistent: false, - duration: this.defaultDuration, - }); - - // Auto-dismiss after updating - setTimeout(() => { - this.dismissNotification(id); - }, this.defaultDuration); - }, - - // Update a loading notification to error - updateToError(id, message, title = "Error") { - this.updateNotification(id, { - type: "error", - title, - message, - persistent: false, - duration: 6000, - }); - - // Auto-dismiss after updating - setTimeout(() => { - this.dismissNotification(id); - }, 6000); - }, - - // Show API operation notifications - showApiSuccess(operation, message = null) { - const defaultMessages = { - create: "Item created successfully", - update: "Item updated successfully", - delete: "Item deleted successfully", - fetch: "Data loaded successfully", - }; - - return this.addSuccess( - message || defaultMessages[operation] || "Operation completed successfully", - "Success", - ); - }, - - showApiError(operation, error, message = null) { - const defaultMessages = { - create: "Failed to create item", - update: "Failed to update item", - delete: "Failed to delete item", - fetch: "Failed to load data", - }; - - let errorMessage = message; - if (!errorMessage) { - if (typeof error === "string") { - errorMessage = error; - } else if (error?.response?.data?.message) { - errorMessage = error.response.data.message; - } else if (error?.message) { - errorMessage = error.message; - } else { - errorMessage = defaultMessages[operation] || "Operation failed"; - } - } - - return this.addError(errorMessage, "Operation Failed"); - }, - - // Configuration methods - setPosition(position) { - this.position = position; - }, - - setDefaultDuration(duration) { - this.defaultDuration = duration; - }, - - setMaxNotifications(max) { - this.maxNotifications = max; - }, - - // Utility method for handling async operations with notifications - async withNotifications(operation, asyncFunction, options = {}) { - const { - loadingMessage = "Processing...", - successMessage = null, - errorMessage = null, - showLoading = true, - } = options; - - let loadingId = null; - - try { - if (showLoading) { - loadingId = this.showLoadingNotification(loadingMessage); - } - - const result = await asyncFunction(); - - if (loadingId) { - this.updateToSuccess( - loadingId, - successMessage || `${operation} completed successfully`, - ); - } else if (successMessage !== false) { - this.showApiSuccess(operation, successMessage); - } - - return result; - } catch (error) { - if (loadingId) { - this.updateToError(loadingId, errorMessage || error.message); - } else { - this.showApiError(operation, error, errorMessage); - } - throw error; - } - }, - }, -});