fix: service calendar edit modal saves and displays correct time

- Force FullCalendar remount after save/delete via calendarKey ref
- Fix datetime construction to use proper ISO 8601 format (T separator, zero-padded)
- Add console.log for save debugging
- Root cause was TIMESTAMPTZ column with PDT server timezone causing +3h offset for EDT users; fixed by converting column to TIMESTAMP WITHOUT TIME ZONE via raw SQL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 14:39:16 -04:00
parent afdb9eb4e0
commit 3134ef0264
2 changed files with 32 additions and 35 deletions
+27 -24
View File
@@ -104,7 +104,7 @@
<!-- FullCalendar -->
<div class="calendar-body p-6">
<FullCalendar ref="fullCalendar" :options="calendarOptions" />
<FullCalendar ref="fullCalendar" :key="calendarKey" :options="calendarOptions" />
</div>
</div>
</div>
@@ -122,6 +122,7 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import dayjs from 'dayjs';
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
@@ -131,11 +132,13 @@ import { serviceService } from '../../services/serviceService';
import { authService } from '../../services/authService';
import { AxiosResponse, AxiosError, ServiceCall } from '../../types/models';
import { getFederalHolidays, type Holiday } from '../../utils/holidays';
import { notify } from '@kyvg/vue3-notification';
// Reactive data
const user = ref(null)
const selectedServiceForEdit = ref(null as Partial<ServiceCall> | null)
const fullCalendar = ref()
const calendarKey = ref(0)
const currentView = ref('dayGridMonth')
const holidays = ref<Holiday[]>([])
const currentDate = ref(new Date())
@@ -163,9 +166,10 @@ const isHolidayDate = (dateStr: string): Holiday | undefined => {
// Event handlers
const handleEventClick = (clickInfo: EventClickArg): void => {
const start = clickInfo.event.start;
selectedServiceForEdit.value = {
id: parseInt(clickInfo.event.id),
scheduled_date: clickInfo.event.startStr,
scheduled_date: start ? dayjs(start).format('YYYY-MM-DDTHH:mm:ss') : '',
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,
@@ -182,12 +186,8 @@ const TYPE_CLASS: Record<number, string> = {
4: 'event-other',
}
// Fetch events function for FullCalendar
const fetchCalendarEvents = async (
fetchInfo: { startStr: string; endStr: string },
successCallback: (events: any[]) => void,
failureCallback: (error: Error) => void
) => {
// Fetch and render all events imperatively
const fetchCalendarEvents = async () => {
try {
const response = await serviceService.getAll();
const rawEvents: any[] = response.data?.events || [];
@@ -206,14 +206,19 @@ const fetchCalendarEvents = async (
start: holiday.date,
allDay: true,
display: 'background',
classNames: ['holiday-event']
classNames: ['holiday-event'],
}));
successCallback([...serviceEvents, ...holidayEvents]);
const allEvents = [...serviceEvents, ...holidayEvents];
calendarOptions.value.events = allEvents as any;
const api = (fullCalendar.value as any)?.getApi();
if (api) {
api.removeAllEvents();
allEvents.forEach((e: any) => api.addEvent(e));
}
} catch (err: unknown) {
const error = err as AxiosError;
console.error("Failed to fetch calendar events:", error);
failureCallback(error as Error);
console.error("Failed to fetch calendar events:", err);
}
};
@@ -280,7 +285,7 @@ const calendarOptions = ref({
headerToolbar: false,
weekends: true,
height: 'auto',
events: fetchCalendarEvents,
events: [],
eventClick: handleEventClick,
dayCellClassNames: getDayCellClassNames,
eventDisplay: 'block',
@@ -297,24 +302,21 @@ const closeEditModal = () => {
const handleSaveChanges = async (updatedService: ServiceCall) => {
try {
await serviceService.update(updatedService.id, updatedService);
const calendarApi = (fullCalendar.value as any).getApi();
if (calendarApi) {
calendarApi.refetchEvents();
}
await fetchCalendarEvents();
calendarKey.value++;
closeEditModal();
notify({ title: 'Saved', text: 'Service call updated.', type: 'success' });
} catch (error) {
console.error("Failed to save changes:", error);
alert("An error occurred while saving. Please check the console.");
notify({ title: 'Error', text: 'Failed to save service call.', type: 'error' });
}
}
const handleDeleteService = async (serviceId: number) => {
try {
await serviceService.delete(serviceId);
const calendarApi = (fullCalendar.value as any).getApi();
if (calendarApi) {
calendarApi.refetchEvents();
}
await fetchCalendarEvents();
calendarKey.value++;
closeEditModal();
} catch (error) {
console.error("Error deleting event:", error);
@@ -333,9 +335,10 @@ const userStatus = () => {
}
// Lifecycle
onMounted(() => {
onMounted(async () => {
userStatus();
loadHolidays();
await fetchCalendarEvents();
})
</script>
+5 -11
View File
@@ -163,19 +163,13 @@ const getServiceParts = (customerId: number) => {
.finally(() => { isLoadingParts.value = false; });
}
const saveChanges = async () => {
const saveChanges = () => {
const date = editableService.value.date;
const time = editableService.value.time || 0;
const combinedDateTime = dayjs(`${date} ${time}:00`).format('YYYY-MM-DDTHH:mm:ss');
const time = editableService.value.time ?? 0;
const combinedDateTime = dayjs(`${date}T${String(time).padStart(2, '0')}:00:00`).format('YYYY-MM-DDTHH:mm:ss');
console.log('[ServiceEditModal] saving - date:', date, 'time:', time, 'combinedDateTime:', combinedDateTime);
const finalPayload = { ...props.service, ...editableService.value, scheduled_date: combinedDateTime };
try {
await serviceService.update(finalPayload.id!, finalPayload);
emit('save-changes', finalPayload as ServiceCall);
} catch (error) {
console.error("Failed to save changes:", error);
alert("An error occurred while saving. Please check the console.");
}
emit('save-changes', finalPayload as ServiceCall);
}
const confirmDelete = () => {