From 76cbca94e34aa6a32857a7e2061bf7075a2ebea4 Mon Sep 17 00:00:00 2001 From: Edwin Eames Date: Tue, 26 Aug 2025 17:19:41 -0400 Subject: [PATCH] Added price for service --- src/layouts/sidebar/sidebar.vue | 5 +- src/pages/service/ServiceCalendar.vue | 85 +++--- src/pages/service/ServiceEditModal.vue | 95 ++++--- src/pages/service/ServiceHome.vue | 2 + src/pages/service/ServicePast.vue | 246 ++++++++++++++++++ .../service/calender/CalendarCustomer.vue | 3 +- src/pages/service/routes.ts | 7 +- 7 files changed, 350 insertions(+), 93 deletions(-) create mode 100644 src/pages/service/ServicePast.vue diff --git a/src/layouts/sidebar/sidebar.vue b/src/layouts/sidebar/sidebar.vue index a0093e1..ba05a7d 100755 --- a/src/layouts/sidebar/sidebar.vue +++ b/src/layouts/sidebar/sidebar.vue @@ -81,11 +81,14 @@
-
Services
+
Service Upcomming
({{ upcoming_service_count }})
Services
+
+ +
Past Service
diff --git a/src/pages/service/ServiceCalendar.vue b/src/pages/service/ServiceCalendar.vue index 3514362..05969a4 100644 --- a/src/pages/service/ServiceCalendar.vue +++ b/src/pages/service/ServiceCalendar.vue @@ -19,6 +19,7 @@
+
@@ -58,6 +59,7 @@ interface ServiceCall { customer_town: string; type_service_call: number; description: string; + service_cost: string; } export default defineComponent({ @@ -71,77 +73,80 @@ export default defineComponent({ plugins: [dayGridPlugin, interactionPlugin], initialView: 'dayGridMonth', weekends: true, - events: [] as any[], + // Instead of a static array, we use a function source. + // This is the standard way FullCalendar fetches events. + events: `${import.meta.env.VITE_BASE_URL}/service/all`, eventClick: this.handleEventClick, + // Add headers for authentication if needed by your API + eventSourceSuccess: (content, response) => { + // This is where you could transform data if needed + return content; + }, + eventSourceFailure: (error) => { + console.error("Failed to fetch calendar events:", error); + } } as CalendarOptions, }; }, created() { this.userStatus(); - this.fetchEvents(); + // We no longer need to call fetchEvents() here because FullCalendar does it automatically. }, methods: { - async fetchEvents(): Promise { - try { - const path = `${import.meta.env.VITE_BASE_URL}/service/all`; - const response = await axios.get(path, { headers: authHeader(), withCredentials: true }); - this.calendarOptions.events = response.data; - } catch (error) { - console.error("Error fetching all calendar events:", error); - } - }, + // We can remove the fetchEvents method as FullCalendar now handles it. + // async fetchEvents(): Promise { ... } - // --- THIS IS THE FIX --- handleEventClick(clickInfo: EventClickArg): void { - const events = (this.calendarOptions.events as any[]) || []; - const originalEvent = events.find(e => e.id == clickInfo.event.id); - - if (originalEvent) { - // We "flatten" the nested object from the calendar into the simple, - // flat structure that the modal expects. - this.selectedServiceForEdit = { - id: originalEvent.id, - scheduled_date: originalEvent.start, - customer_id: originalEvent.customer_id, - // Extract the customer name from the title - customer_name: originalEvent.title.split(': ')[1] || 'Unknown Customer', - // Pull the properties out of the nested extendedProps - type_service_call: originalEvent.extendedProps.type_service_call, - description: originalEvent.extendedProps.description, - - // Add dummy values for other fields the ServiceCall interface expects - customer_address: '', - customer_town: '', - }; - } + // This logic remains the same, as it correctly pulls data from extendedProps + this.selectedServiceForEdit = { + id: parseInt(clickInfo.event.id), + scheduled_date: clickInfo.event.startStr, + customer_name: clickInfo.event.title.split(': ')[1] || 'Unknown Customer', + customer_id: clickInfo.event.extendedProps.customer_id, + type_service_call: clickInfo.event.extendedProps.type_service_call, + description: clickInfo.event.extendedProps.description, + service_cost: clickInfo.event.extendedProps.service_cost, + }; }, closeEditModal() { this.selectedServiceForEdit = null; }, + // =================== THIS IS THE CORRECTED SECTION =================== async handleSaveChanges(updatedService: ServiceCall) { try { const path = `${import.meta.env.VITE_BASE_URL}/service/update/${updatedService.id}`; await axios.put(path, updatedService, { headers: authHeader(), withCredentials: true }); - await this.fetchEvents(); + + // Get the FullCalendar component instance from the ref + const calendarApi = (this.$refs.fullCalendar as any).getApi(); + if (calendarApi) { + // Tell FullCalendar to re-fetch its events from the source. + // This is the most reliable way to refresh the view immediately. + calendarApi.refetchEvents(); + } + this.closeEditModal(); } catch (error) { console.error("Failed to save changes:", error); alert("An error occurred while saving. Please check the console."); } }, + // =================== END OF CORRECTED SECTION =================== async handleDeleteService(serviceId: number) { try { const path = `${import.meta.env.VITE_BASE_URL}/service/delete/${serviceId}`; - const response = await axios.delete(path, { withCredentials: true, headers: authHeader() }); - if (response.data.ok === true) { - await this.fetchEvents(); - this.closeEditModal(); - } else { - console.error("Failed to delete event:", response.data.error); + await axios.delete(path, { withCredentials: true, headers: authHeader() }); + + // Also refresh the calendar after a delete + const calendarApi = (this.$refs.fullCalendar as any).getApi(); + if (calendarApi) { + calendarApi.refetchEvents(); } + + this.closeEditModal(); } catch (error) { console.error("Error deleting event:", error); } diff --git a/src/pages/service/ServiceEditModal.vue b/src/pages/service/ServiceEditModal.vue index 6abd281..d5bcfc3 100644 --- a/src/pages/service/ServiceEditModal.vue +++ b/src/pages/service/ServiceEditModal.vue @@ -2,7 +2,8 @@
-
+ +
@@ -16,9 +17,8 @@ - -
- + +
{{ customer.account_number }}
{{ customer.customer_first_name }} {{ customer.customer_last_name }}
@@ -26,66 +26,63 @@
{{ customer.customer_town }}, {{ customer.customer_state }} {{ customer.customer_zip }}
{{ customer.customer_phone_number }}
-
-

Loading customer details...

-
- - - - +

Loading customer details...

Service Parts on File

-
- -

{{ serviceParts.oil_filter || 'N/A' }}

-
-
- -

{{ serviceParts.oil_filter_2 || 'N/A' }}

-
-
- -

{{ serviceParts.oil_nozzle || 'N/A' }}

-
-
- -

{{ serviceParts.oil_nozzle_2 || 'N/A' }}

-
+

{{ serviceParts.oil_filter || 'N/A' }}

+

{{ serviceParts.oil_filter_2 || 'N/A' }}

+

{{ serviceParts.oil_nozzle || 'N/A' }}

+

{{ serviceParts.oil_nozzle_2 || 'N/A' }}

-
-

Loading service parts...

-
- - - +

Loading service parts...

- + + +
-
-
+ +
+ + +
-
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
-
- - -
-
- - -
+ +
@@ -105,7 +102,7 @@ import axios from 'axios'; import authHeader from '../../services/auth.header'; // --- Interfaces --- -interface ServiceCall { id: number; scheduled_date: string; customer_id: number; customer_name: string; customer_address: string; customer_town: string; type_service_call: number; description: string; } +interface ServiceCall { id: number; scheduled_date: string; customer_id: number; customer_name: string; customer_address: string; customer_town: string; type_service_call: number; description: string; service_cost: string } interface EditableService extends Omit { date: string; time: number; } interface Customer { account_number: string; customer_first_name: string; customer_last_name: string; customer_address: string; customer_town: string; customer_state: number; customer_zip: string; customer_phone_number: string; } interface ServiceParts { customer_id: number; oil_filter: string; oil_filter_2: string; oil_nozzle: string; oil_nozzle_2: string; } @@ -157,9 +154,7 @@ export default defineComponent({ .catch((error: any) => { console.error("Failed to fetch service parts:", error); }) .finally(() => { this.isLoadingParts = false; }); }, - // --- UPDATED: Simplified saveChanges method --- async saveChanges() { - // This method now only saves the service call itself. const date = this.editableService.date; const time = this.editableService.time || 0; const combinedDateTime = dayjs(`${date} ${time}:00`).format('YYYY-MM-DDTHH:mm:ss'); diff --git a/src/pages/service/ServiceHome.vue b/src/pages/service/ServiceHome.vue index cf75004..418a067 100644 --- a/src/pages/service/ServiceHome.vue +++ b/src/pages/service/ServiceHome.vue @@ -55,6 +55,7 @@ {{ getServiceTypeName(service.type_service_call) }} {{ service.description }} + {{ service.service_cost }} @@ -93,6 +94,7 @@ interface ServiceCall { customer_town: string; type_service_call: number; description: string; + service_cost: string; } export default defineComponent({ diff --git a/src/pages/service/ServicePast.vue b/src/pages/service/ServicePast.vue new file mode 100644 index 0000000..a58e315 --- /dev/null +++ b/src/pages/service/ServicePast.vue @@ -0,0 +1,246 @@ + + + \ No newline at end of file diff --git a/src/pages/service/calender/CalendarCustomer.vue b/src/pages/service/calender/CalendarCustomer.vue index 7e35974..c6fae53 100644 --- a/src/pages/service/calender/CalendarCustomer.vue +++ b/src/pages/service/calender/CalendarCustomer.vue @@ -48,7 +48,7 @@ import ServiceEditModal from '../../service/ServiceEditModal.vue'; import axios from 'axios'; import authHeader from '../../../services/auth.header'; -interface ServiceCall { id: number; scheduled_date: string; customer_id: number; customer_name: string; customer_address: string; customer_town: string; type_service_call: number; description: string; } +interface ServiceCall { id: number; scheduled_date: string; customer_id: number; customer_name: string; customer_address: string; customer_town: string; type_service_call: number; description: string; service_cost: string;} interface Customer { id: number; customer_last_name: string; customer_first_name: string; customer_town: string; customer_state: number; customer_zip: string; customer_phone_number: string; customer_address: string; customer_home_type: number; customer_apt: string; } export default defineComponent({ @@ -96,6 +96,7 @@ export default defineComponent({ customer_name: originalEvent.title.split(': ')[1] || 'Unknown Customer', type_service_call: originalEvent.extendedProps.type_service_call, description: originalEvent.extendedProps.description, + service_cost: originalEvent.extendedProps.description, customer_address: '', customer_town: '', }; diff --git a/src/pages/service/routes.ts b/src/pages/service/routes.ts index 351f84b..a8af3d8 100644 --- a/src/pages/service/routes.ts +++ b/src/pages/service/routes.ts @@ -1,5 +1,6 @@ // Import the new component at the top import ServiceHome from './ServiceHome.vue' +import ServicePast from './ServicePast.vue' import CalendarCustomer from './calender/CalendarCustomer.vue' import ServiceCalendar from './ServiceCalendar.vue' @@ -9,7 +10,11 @@ const serviceRoutes = [ name: 'ServiceHome', component: ServiceHome }, - + { + path: '/service/past', + name: 'ServicePast', + component: ServicePast + }, // --- NEW ROUTE FOR THE MASTER CALENDAR --- { path: '/service/calendar', // Note: No '/:id' parameter