bulk-email/src/redux/slices/slotSlice.ts

330 lines
8.6 KiB
TypeScript

import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import http from "../../lib/https"; // Assuming you have a custom HTTP library for requests
import { toast } from "sonner";
import dayjs from "dayjs";
// Define TypeScript types
interface Slot {
id: string;
stationId: string;
date?: string;
startTime: string;
startingDate?: string;
endingDate?: string;
endTime: string;
duration: number;
isAvailable: boolean;
stationName: string;
ChargingStation: { name: string };
}
interface SlotState {
slots: Slot[];
availableSlots: Slot[];
loading: boolean;
error: string | null;
}
// Initial state
const initialState: SlotState = {
slots: [],
availableSlots: [], // <-- Initialize this as an empty array
loading: false,
error: null,
};
export const fetchAvailableSlots = createAsyncThunk<
Slot[],
void,
{ rejectValue: string }
>("fetchVehicles", async (_, { rejectWithValue }) => {
try {
const token = localStorage?.getItem("authToken");
if (!token) throw new Error("No token found");
const response = await http.get("/available");
if (!response.data?.data) throw new Error("Invalid API response");
return response.data.data;
} catch (error: any) {
toast.error("Error Fetching Profile" + error);
return rejectWithValue(
error?.response?.data?.message || "An error occurred"
);
}
});
export const fetchManagersSlots = createAsyncThunk<
Slot[],
void,
{ rejectValue: string }
>("fetchManagersSlots", async (_, { rejectWithValue }) => {
try {
const token = localStorage?.getItem("authToken");
if (!token) throw new Error("No token found");
const response = await http.get("/manager-slots");
if (!response.data?.data) throw new Error("Invalid API response");
return response.data.data;
} catch (error: any) {
toast.error("Error Fetching Profile" + error);
return rejectWithValue(
error?.response?.data?.message || "An error occurred"
);
}
});
export const createSlot = createAsyncThunk<
Slot[], // <-- updated from Slot to Slot[]
{
date?: string;
startingDate?: string;
endingDate?: string;
startHour: string;
endHour: string;
isAvailable: boolean;
duration: number;
stationId: number;
},
{ rejectValue: string }
>("slots/createSlot", async (payload, { rejectWithValue }) => {
try {
const token = localStorage?.getItem("authToken");
if (!token) throw new Error("No token found");
// Make the API call to create the slots
const response = await http.post("create-slot", payload);
toast.success("Slot(s) created successfully");
return response.data.data; // Return the array of created slots
} catch (error: any) {
toast.error("Error creating slot: " + error?.message);
return rejectWithValue(
error.response?.data?.message || "An error occurred"
);
}
});
// Update Slot details
export const updateSlot = createAsyncThunk<
Slot,
{
id: string;
startTime: string;
endTime: string;
},
{ rejectValue: string }
>("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => {
try {
const response = await http.patch(`/update-availability/${id}`, {
...slotData,
// Ensure data matches exactly what backend expects
startHour: slotData.startTime,
endHour: slotData.endTime,
});
toast.success("Slot updated successfully");
return response.data.data;
} catch (error: any) {
toast.error("Error updating the slot: " + error?.message);
return rejectWithValue(
error.response?.data?.message || "An error occurred"
);
}
});
export const toggleStatus = createAsyncThunk<
any,
{ id: string; isAvailable: boolean },
{ rejectValue: string }
>("slot/toggleStatus", async ({ id, isAvailable }, { rejectWithValue }) => {
try {
const response = await http.patch(`/update-availability/${id}`, {
isAvailable,
});
if (response.data.statusCode === 200) {
toast.success(
response.data.message || "Status updated successfully"
);
return {
responseData: response.data,
id,
isAvailable,
};
} else {
throw new Error(response.data.message || "Failed to update status");
}
} catch (error: any) {
toast.error(
"Error updating status: " + (error.message || "Unknown error")
);
return rejectWithValue(
error.response?.data?.message ||
error.message ||
"An error occurred"
);
}
});
export const deleteSlot = createAsyncThunk<
string, // Return type (id of deleted slot)
string,
{ rejectValue: string }
>("slots/deleteSlot", async (id, { rejectWithValue }) => {
try {
const response = await http.delete(`/delete-slot/${id}`);
toast.success("Slot deleted successfully");
return id; // Return the id of the deleted slot
} catch (error: any) {
toast.error("Error deleting the slot: " + error?.message);
return rejectWithValue(
error.response?.data?.message || "An error occurred"
);
}
});
const slotSlice = createSlice({
name: "slots",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchAvailableSlots.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(
fetchAvailableSlots.fulfilled,
(state, action: PayloadAction<Slot[]>) => {
state.loading = false;
state.availableSlots = action.payload;
}
)
.addCase(fetchAvailableSlots.rejected, (state, action) => {
state.loading = false;
state.error =
action.payload || "Failed to fetch available slots";
})
.addCase(fetchManagersSlots.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(
fetchManagersSlots.fulfilled,
(state, action: PayloadAction<Slot[]>) => {
state.loading = false;
state.availableSlots = action.payload;
}
)
.addCase(fetchManagersSlots.rejected, (state, action) => {
state.loading = false;
state.error =
action.payload || "Failed to fetch available slots";
})
.addCase(createSlot.pending, (state) => {
state.loading = true;
})
.addCase(
createSlot.fulfilled,
(state, action: PayloadAction<Slot[]>) => {
state.loading = false;
const normalizedSlots = action.payload.map((slot) => {
const combinedStart = `${slot.date} ${slot.startTime}`; // Keep raw for now
const combinedEnd = `${slot.date} ${slot.endTime}`;
return {
...slot,
startTime: combinedStart,
endTime: combinedEnd,
};
});
console.log("Normalized Slots →", normalizedSlots); // Check this in console
state.slots.push(...normalizedSlots);
state.availableSlots.push(...normalizedSlots);
}
)
.addCase(createSlot.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "Failed to create slot";
})
.addCase(updateSlot.pending, (state) => {
state.loading = true;
})
.addCase(
updateSlot.fulfilled,
(state, action: PayloadAction<Slot>) => {
state.loading = false;
// Update the slot in the state with the updated data
const index = state.availableSlots.findIndex(
(slot) => slot.id === action.payload.id
);
if (index !== -1) {
state.availableSlots[index] = action.payload;
}
}
)
.addCase(updateSlot.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "Failed to update slot";
})
.addCase(deleteSlot.pending, (state) => {
state.loading = true;
})
.addCase(toggleStatus.pending, (state) => {
state.loading = true;
})
.addCase(
toggleStatus.fulfilled,
(state, action: PayloadAction<any>) => {
state.loading = false;
const { id, isAvailable } = action.payload;
const stationIndex = state.availableSlots.findIndex(
(slot) => slot.id === id
);
if (stationIndex !== -1) {
state.availableSlots[stationIndex] = {
...state.availableSlots[stationIndex],
isAvailable: isAvailable,
};
}
}
)
.addCase(
toggleStatus.rejected,
(state, action: PayloadAction<string | undefined>) => {
state.loading = false;
state.error =
action.payload || "Failed to toggle station status";
}
)
.addCase(
deleteSlot.fulfilled,
(state, action: PayloadAction<string>) => {
state.loading = false;
// Ensure we're filtering the correct array (availableSlots)
state.availableSlots = state.availableSlots.filter(
(slot) => String(slot.id) !== String(action.payload)
);
// Also update slots array if it exists
if (state.slots) {
state.slots = state.slots.filter(
(slot) => String(slot.id) !== String(action.payload)
);
}
}
)
.addCase(deleteSlot.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "Failed to delete slot";
});
},
});
export default slotSlice.reducer;