import { BASE_URL } from "@/constants/config"; import api from "@/services/axiosClient"; import { toCamel } from "@/utils/Payments"; import { createSlice, PayloadAction, createAsyncThunk } from "@reduxjs/toolkit"; import { AxiosError } from "axios"; type LastEmiStatus = "pending" | "completed"; export interface MyPlan { noOfEmi: number | null; totalAmount: number | null; downPayment: number | null; emiAmount: number | null; totalEmi: number | null; installmentPaid: number | null; currentAmount: number | null; } export interface EmiDetails { dueAmount: number | null; totalAmountPaidInCurrentCycle: number | null; dueDate: string | null; status: LastEmiStatus | null; advanceBalance: number | null; pendingCycles: number | null; totalPendingInstallments: number | null; myPlan: MyPlan; } type LoadingState = "idle" | "pending" | "succeeded" | "failed"; export interface EmiDetailsState { item?: EmiDetails; loading: LoadingState; error?: string; lastFetchedAt?: number; } const initialState: EmiDetailsState = { item: undefined, loading: "idle", error: undefined, lastFetchedAt: undefined, }; export interface MyPlanApi { no_of_emi: number | null; total_amount: number | null; down_payment: number | null; emi_amount: number | null; total_emi: number | null; installment_paid: number | null; current_amount: number | null; } export interface EmiDetailsApi { due_amount: number | null; total_amount_paid_in_current_cycle: number | null; due_date: string | null; status: LastEmiStatus | null; advance_balance: number | null; pending_cycles: number | null; total_pending_installments: number | null; myPlain: MyPlanApi; } export interface EmiDetailsResponse { success: boolean; data: EmiDetailsApi[]; } const mapEmiDetailsApiToModel = (apiObj: EmiDetailsApi): EmiDetails => ({ dueAmount: apiObj.due_amount, totalAmountPaidInCurrentCycle: apiObj.total_amount_paid_in_current_cycle, dueDate: apiObj.due_date, status: apiObj.status, advanceBalance: apiObj.advance_balance, pendingCycles: apiObj.pending_cycles, totalPendingInstallments: apiObj.total_pending_installments, myPlan: { noOfEmi: apiObj.myPlain.no_of_emi, totalAmount: apiObj.myPlain.total_amount, downPayment: apiObj.myPlain.down_payment, emiAmount: apiObj.myPlain.emi_amount, totalEmi: apiObj.myPlain.total_emi, installmentPaid: apiObj.myPlain.installment_paid, currentAmount: apiObj.myPlain.current_amount, }, }); export const fetchEmiDetails = createAsyncThunk< EmiDetails, void, { rejectValue: string } >("emiDetails/fetch", async (_: void, { rejectWithValue }) => { try { const res = await api.get(`${BASE_URL}}/api/v1/emi-details`, { method: "GET", headers: { "Content-Type": "application/json" }, }); const json = res.data; const first = json?.data?.[0]; if (!json?.success || !first) { return rejectWithValue("No EMI details found"); } return mapEmiDetailsApiToModel(first); } catch (e: any) { const err = e as AxiosError; if (err.code === "ERR_CANCELED") return rejectWithValue("Request aborted"); const msg = err.response?.data?.message || err.message || "Something went wrong"; return rejectWithValue(msg); } }); const emiDetailsSlice = createSlice({ name: "emiDetails", initialState, reducers: { clearEmiDetails(state) { state.item = undefined; state.error = undefined; state.loading = "idle"; state.lastFetchedAt = undefined; }, }, extraReducers: (builder) => { builder .addCase(fetchEmiDetails.pending, (state) => { state.loading = "pending"; state.error = undefined; }) .addCase( fetchEmiDetails.fulfilled, (state, action: PayloadAction) => { state.loading = "succeeded"; state.item = action.payload; state.lastFetchedAt = Date.now(); } ) .addCase(fetchEmiDetails.rejected, (state, action) => { state.loading = "failed"; state.error = action.payload as string | undefined; }); }, }); export const { clearEmiDetails } = emiDetailsSlice.actions; export default emiDetailsSlice.reducer;