From f38b305ba703f3606f80d83d240d4b49d3acb319 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 12 Feb 2026 18:07:48 +0000 Subject: [PATCH 1/2] ui slice to TS --- client/src/Features/UI/uiSlice.js | 98 ---------------------- client/src/Features/UI/uiSlice.ts | 133 ++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 98 deletions(-) delete mode 100644 client/src/Features/UI/uiSlice.js create mode 100644 client/src/Features/UI/uiSlice.ts diff --git a/client/src/Features/UI/uiSlice.js b/client/src/Features/UI/uiSlice.js deleted file mode 100644 index 0e8ca8608..000000000 --- a/client/src/Features/UI/uiSlice.js +++ /dev/null @@ -1,98 +0,0 @@ -import { createSlice } from "@reduxjs/toolkit"; - -const initialMode = window?.matchMedia?.("(prefers-color-scheme: dark)")?.matches - ? "dark" - : "light"; - -const initialState = { - monitors: { - rowsPerPage: 10, - }, - team: { - rowsPerPage: 5, - }, - maintenance: { - rowsPerPage: 5, - }, - infrastructure: { - rowsPerPage: 5, - }, - logs: { - rowsPerPage: 15, - }, - sidebar: { - collapsed: false, - }, - mode: initialMode, - showURL: false, - greeting: { index: 0, lastUpdate: null }, - timezone: "America/Toronto", - distributedUptimeEnabled: false, - language: "en", - starPromptOpen: true, - chartType: "histogram", -}; - -const uiSlice = createSlice({ - name: "ui", - initialState, - reducers: { - setDistributedUptimeEnabled: (state, action) => { - state.distributedUptimeEnabled = action.payload; - }, - setRowsPerPage: (state, action) => { - const { table, value } = action.payload; - if (!state[table]) { - state[table] = {}; - } - state[table].rowsPerPage = value; - }, - toggleSidebar: (state) => { - state.sidebar.collapsed = !state.sidebar.collapsed; - }, - setCollapsed: (state, action) => { - const { collapsed } = action.payload; - state.sidebar.collapsed = collapsed; - }, - setMode: (state, action) => { - state.mode = action.payload; - }, - setShowURL: (state, action) => { - state.showURL = action.payload; - }, - setGreeting(state, action) { - if (!state.greeting) { - state.greeting = { index: 0, lastUpdate: null }; - } - state.greeting.index = action.payload.index; - state.greeting.lastUpdate = action.payload.lastUpdate; - }, - setTimezone(state, action) { - state.timezone = action.payload.timezone; - }, - setLanguage: (state, action) => { - state.language = action.payload; - }, - setStarPromptOpen: (state, action) => { - state.starPromptOpen = action.payload; - }, - setChartType: (state, action) => { - state.chartType = action.payload; - }, - }, -}); - -export default uiSlice.reducer; -export const { - setRowsPerPage, - toggleSidebar, - setCollapsed, - setMode, - setShowURL, - setGreeting, - setTimezone, - setDistributedUptimeEnabled, - setLanguage, - setStarPromptOpen, - setChartType, -} = uiSlice.actions; diff --git a/client/src/Features/UI/uiSlice.ts b/client/src/Features/UI/uiSlice.ts new file mode 100644 index 000000000..0675784b9 --- /dev/null +++ b/client/src/Features/UI/uiSlice.ts @@ -0,0 +1,133 @@ +import { createSlice, type PayloadAction } from "@reduxjs/toolkit"; + +type ThemeMode = "light" | "dark"; +type ChartType = "histogram" | "line"; +type TableName = "monitors" | "team" | "maintenance" | "infrastructure" | "logs"; + +interface TableState { + rowsPerPage: number; +} + +interface SidebarState { + collapsed: boolean; +} + +interface GreetingState { + index: number; + lastUpdate: string | null; +} + +interface UIState { + monitors: TableState; + team: TableState; + maintenance: TableState; + infrastructure: TableState; + logs: TableState; + sidebar: SidebarState; + mode: ThemeMode; + showURL: boolean; + greeting: GreetingState; + timezone: string; + distributedUptimeEnabled: boolean; + language: string; + starPromptOpen: boolean; + chartType: ChartType; +} + +const initialMode: ThemeMode = window?.matchMedia?.("(prefers-color-scheme: dark)") + ?.matches + ? "dark" + : "light"; + +const initialState: UIState = { + monitors: { + rowsPerPage: 10, + }, + team: { + rowsPerPage: 5, + }, + maintenance: { + rowsPerPage: 5, + }, + infrastructure: { + rowsPerPage: 5, + }, + logs: { + rowsPerPage: 15, + }, + sidebar: { + collapsed: false, + }, + mode: initialMode, + showURL: false, + greeting: { index: 0, lastUpdate: null }, + timezone: "America/Toronto", + distributedUptimeEnabled: false, + language: "en", + starPromptOpen: true, + chartType: "histogram", +}; + +const uiSlice = createSlice({ + name: "ui", + initialState, + reducers: { + setDistributedUptimeEnabled: (state, action: PayloadAction) => { + state.distributedUptimeEnabled = action.payload; + }, + setRowsPerPage: ( + state, + action: PayloadAction<{ table: TableName; value: number }> + ) => { + const { table, value } = action.payload; + state[table].rowsPerPage = value; + }, + toggleSidebar: (state) => { + state.sidebar.collapsed = !state.sidebar.collapsed; + }, + setCollapsed: (state, action: PayloadAction<{ collapsed: boolean }>) => { + state.sidebar.collapsed = action.payload.collapsed; + }, + setMode: (state, action: PayloadAction) => { + state.mode = action.payload; + }, + setShowURL: (state, action: PayloadAction) => { + state.showURL = action.payload; + }, + setGreeting: ( + state, + action: PayloadAction<{ index: number; lastUpdate: string | null }> + ) => { + state.greeting.index = action.payload.index; + state.greeting.lastUpdate = action.payload.lastUpdate; + }, + setTimezone: (state, action: PayloadAction<{ timezone: string }>) => { + state.timezone = action.payload.timezone; + }, + setLanguage: (state, action: PayloadAction) => { + state.language = action.payload; + }, + setStarPromptOpen: (state, action: PayloadAction) => { + state.starPromptOpen = action.payload; + }, + setChartType: (state, action: PayloadAction) => { + state.chartType = action.payload; + }, + }, +}); + +export type { UIState, ThemeMode, ChartType, TableName }; +export default uiSlice.reducer; +export const { + setRowsPerPage, + toggleSidebar, + setCollapsed, + setMode, + setShowURL, + setGreeting, + setTimezone, + setDistributedUptimeEnabled, + setLanguage, + setStarPromptOpen, + setChartType, +} = uiSlice.actions; From 85dbe34e009e0b796635b07b8bd747b46ecce24e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 12 Feb 2026 18:30:15 +0000 Subject: [PATCH 2/2] store to ts --- client/src/main.tsx | 2 +- client/src/{store.js => store.ts} | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) rename client/src/{store.js => store.ts} (59%) diff --git a/client/src/main.tsx b/client/src/main.tsx index 813a839bd..1e339f87b 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -3,7 +3,7 @@ import App from "./App.jsx"; import "./index.css"; import { BrowserRouter as Router } from "react-router-dom"; import { Provider } from "react-redux"; -import { persistor, store } from "./store.js"; +import { persistor, store } from "@/store.js"; import { PersistGate } from "redux-persist/integration/react"; import I18nLoader from "./Components/v1/I18nLoader/index.jsx"; import { initApiClient } from "./Utils/ApiClient.js"; diff --git a/client/src/store.js b/client/src/store.ts similarity index 59% rename from client/src/store.js rename to client/src/store.ts index 69aa3d50b..9e064b55f 100644 --- a/client/src/store.js +++ b/client/src/store.ts @@ -1,18 +1,21 @@ import { configureStore, combineReducers } from "@reduxjs/toolkit"; - -import authReducer from "./Features/Auth/authSlice"; -import uiReducer from "./Features/UI/uiSlice"; +import authReducer from "@/Features/Auth/authSlice"; +import uiReducer from "@/Features/UI/uiSlice"; import storage from "redux-persist/lib/storage"; -import { persistReducer, persistStore, createTransform } from "redux-persist"; +import { + persistReducer, + persistStore, + createTransform, + PERSIST, + REHYDRATE, +} from "redux-persist"; const authTransform = createTransform( - (inboundState) => { + (inboundState: Record) => { const { profileImage, ...rest } = inboundState; return rest; }, - // No transformation on rehydration - null, - // Only applies to auth + undefined, { whitelist: ["auth"] } ); @@ -28,6 +31,7 @@ const rootReducer = combineReducers({ ui: uiReducer, }); +// @ts-expect-error - redux-persist types don't align perfectly with redux-toolkit const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ @@ -35,10 +39,13 @@ export const store = configureStore({ middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { - ignoredActions: ["persist/PERSIST", "persist/REHYDRATE", "persist/REGISTER"], + ignoredActions: [PERSIST, REHYDRATE, "persist/REGISTER"], }, }), }); +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; + export const persistor = persistStore(store); export default store;