import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"; export interface SidebarItem { id?: string; title: string; path: string; icon?: string; // icon name from backend; map client-side if needed permissions?: string[]; // required permissions for visibility children?: SidebarItem[]; } export interface AppMetadata { groups?: string[]; job_titles?: string[]; merchants?: string[]; message?: string; sidebar?: SidebarItem[]; success: boolean; } interface MetadataState { data: AppMetadata | null; status: "idle" | "loading" | "succeeded" | "failed"; error: string | null; lastFetchedAt: number | null; } const initialState: MetadataState = { data: null, status: "idle", error: null, lastFetchedAt: null, }; export const fetchMetadata = createAsyncThunk< AppMetadata, void, { rejectValue: string } >("metadata/fetch", async (_, { rejectWithValue }) => { try { const res = await fetch("/api/metadata", { method: "GET", cache: "no-store", }); const data = await res.json(); if (!res.ok) { return rejectWithValue(data?.message || "Failed to fetch metadata"); } return data as AppMetadata; } catch (err) { return rejectWithValue((err as Error)?.message || "Network error"); } }); const metadataSlice = createSlice({ name: "metadata", initialState, reducers: { clearMetadata(state) { state.data = null; state.error = null; state.status = "idle"; state.lastFetchedAt = null; }, }, extraReducers: builder => { builder .addCase(fetchMetadata.pending, state => { state.status = "loading"; state.error = null; }) .addCase( fetchMetadata.fulfilled, (state, action: PayloadAction) => { state.status = "succeeded"; state.data = action.payload; state.lastFetchedAt = Date.now(); } ) .addCase(fetchMetadata.rejected, (state, action) => { state.status = "failed"; state.error = (action.payload as string) || "Unknown error"; }); }, }); export const { clearMetadata } = metadataSlice.actions; export default metadataSlice.reducer;