From 675e3a0985eaa51b475f583ae243a9d50fec0878 Mon Sep 17 00:00:00 2001 From: jahrulezfrancis Date: Sun, 1 Mar 2026 23:36:11 +0100 Subject: [PATCH 1/2] feat: establish redux feature-based folder structure and rootReduce --- src/features/admin/adminSelectors.js | 4 +++ src/features/admin/adminSlice.js | 19 +++++++++++++ src/features/admin/adminThunks.js | 12 ++++++++ src/features/auth/authSelectors.js | 4 +++ src/features/auth/authSlice.js | 19 +++++++++++++ src/features/auth/authThunks.js | 12 ++++++++ src/features/bookmarks/bookmarksSelectors.js | 3 ++ src/features/bookmarks/bookmarksSlice.js | 18 ++++++++++++ src/features/bookmarks/bookmarksThunks.js | 12 ++++++++ src/features/campaigns/campaignsSelectors.js | 3 ++ src/features/campaigns/campaignsSlice.js | 18 ++++++++++++ src/features/campaigns/campaignsThunks.js | 12 ++++++++ src/features/dashboard/dashboardSelectors.js | 3 ++ src/features/dashboard/dashboardSlice.js | 18 ++++++++++++ src/features/dashboard/dashboardThunks.js | 12 ++++++++ src/features/donations/donationsSelectors.js | 3 ++ src/features/donations/donationsSlice.js | 18 ++++++++++++ src/features/donations/donationsThunks.js | 12 ++++++++ src/store/README.md | 30 ++++++++++++++++++++ src/store/index.js | 6 +--- src/store/rootReducer.js | 18 ++++++++++++ 21 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 src/features/admin/adminSelectors.js create mode 100644 src/features/admin/adminSlice.js create mode 100644 src/features/admin/adminThunks.js create mode 100644 src/features/auth/authSelectors.js create mode 100644 src/features/auth/authSlice.js create mode 100644 src/features/auth/authThunks.js create mode 100644 src/features/bookmarks/bookmarksSelectors.js create mode 100644 src/features/bookmarks/bookmarksSlice.js create mode 100644 src/features/bookmarks/bookmarksThunks.js create mode 100644 src/features/campaigns/campaignsSelectors.js create mode 100644 src/features/campaigns/campaignsSlice.js create mode 100644 src/features/campaigns/campaignsThunks.js create mode 100644 src/features/dashboard/dashboardSelectors.js create mode 100644 src/features/dashboard/dashboardSlice.js create mode 100644 src/features/dashboard/dashboardThunks.js create mode 100644 src/features/donations/donationsSelectors.js create mode 100644 src/features/donations/donationsSlice.js create mode 100644 src/features/donations/donationsThunks.js create mode 100644 src/store/README.md create mode 100644 src/store/rootReducer.js diff --git a/src/features/admin/adminSelectors.js b/src/features/admin/adminSelectors.js new file mode 100644 index 0000000..b5a1304 --- /dev/null +++ b/src/features/admin/adminSelectors.js @@ -0,0 +1,4 @@ +export const selectAdminUsers = (state) => state.admin.users; +export const selectAdminSettings = (state) => state.admin.settings; +export const selectAdminLoading = (state) => state.admin.loading; +export const selectAdminError = (state) => state.admin.error; diff --git a/src/features/admin/adminSlice.js b/src/features/admin/adminSlice.js new file mode 100644 index 0000000..f15d39b --- /dev/null +++ b/src/features/admin/adminSlice.js @@ -0,0 +1,19 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + users: [], + settings: {}, + loading: false, + error: null, +}; + +const adminSlice = createSlice({ + name: 'admin', + initialState, + reducers: { + // Reducer placeholders + }, +}); + +export const { } = adminSlice.actions; +export default adminSlice.reducer; diff --git a/src/features/admin/adminThunks.js b/src/features/admin/adminThunks.js new file mode 100644 index 0000000..02d7546 --- /dev/null +++ b/src/features/admin/adminThunks.js @@ -0,0 +1,12 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; + +export const fetchAdminData = createAsyncThunk( + 'admin/fetchData', + async (_, { rejectWithValue }) => { + try { + // Async logic placeholder + } catch (error) { + return rejectWithValue(error.message); + } + } +); diff --git a/src/features/auth/authSelectors.js b/src/features/auth/authSelectors.js new file mode 100644 index 0000000..51b4462 --- /dev/null +++ b/src/features/auth/authSelectors.js @@ -0,0 +1,4 @@ +export const selectUser = (state) => state.auth.user; +export const selectIsAuthenticated = (state) => state.auth.isAuthenticated; +export const selectAuthLoading = (state) => state.auth.loading; +export const selectAuthError = (state) => state.auth.error; diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js new file mode 100644 index 0000000..37da583 --- /dev/null +++ b/src/features/auth/authSlice.js @@ -0,0 +1,19 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + user: null, + isAuthenticated: false, + loading: false, + error: null, +}; + +const authSlice = createSlice({ + name: 'auth', + initialState, + reducers: { + // Reducer placeholders + }, +}); + +export const { } = authSlice.actions; +export default authSlice.reducer; diff --git a/src/features/auth/authThunks.js b/src/features/auth/authThunks.js new file mode 100644 index 0000000..4317aa5 --- /dev/null +++ b/src/features/auth/authThunks.js @@ -0,0 +1,12 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; + +export const login = createAsyncThunk( + 'auth/login', + async (credentials, { rejectWithValue }) => { + try { + // Async logic placeholder + } catch (error) { + return rejectWithValue(error.message); + } + } +); diff --git a/src/features/bookmarks/bookmarksSelectors.js b/src/features/bookmarks/bookmarksSelectors.js new file mode 100644 index 0000000..eb52ebf --- /dev/null +++ b/src/features/bookmarks/bookmarksSelectors.js @@ -0,0 +1,3 @@ +export const selectAllBookmarks = (state) => state.bookmarks.items; +export const selectBookmarksLoading = (state) => state.bookmarks.loading; +export const selectBookmarksError = (state) => state.bookmarks.error; diff --git a/src/features/bookmarks/bookmarksSlice.js b/src/features/bookmarks/bookmarksSlice.js new file mode 100644 index 0000000..1cbae7b --- /dev/null +++ b/src/features/bookmarks/bookmarksSlice.js @@ -0,0 +1,18 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + items: [], + loading: false, + error: null, +}; + +const bookmarksSlice = createSlice({ + name: 'bookmarks', + initialState, + reducers: { + // Reducer placeholders + }, +}); + +export const { } = bookmarksSlice.actions; +export default bookmarksSlice.reducer; diff --git a/src/features/bookmarks/bookmarksThunks.js b/src/features/bookmarks/bookmarksThunks.js new file mode 100644 index 0000000..f959afc --- /dev/null +++ b/src/features/bookmarks/bookmarksThunks.js @@ -0,0 +1,12 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; + +export const fetchBookmarks = createAsyncThunk( + 'bookmarks/fetchAll', + async (_, { rejectWithValue }) => { + try { + // Async logic placeholder + } catch (error) { + return rejectWithValue(error.message); + } + } +); diff --git a/src/features/campaigns/campaignsSelectors.js b/src/features/campaigns/campaignsSelectors.js new file mode 100644 index 0000000..3c7c9d9 --- /dev/null +++ b/src/features/campaigns/campaignsSelectors.js @@ -0,0 +1,3 @@ +export const selectAllCampaigns = (state) => state.campaigns.items; +export const selectCampaignsLoading = (state) => state.campaigns.loading; +export const selectCampaignsError = (state) => state.campaigns.error; diff --git a/src/features/campaigns/campaignsSlice.js b/src/features/campaigns/campaignsSlice.js new file mode 100644 index 0000000..8760122 --- /dev/null +++ b/src/features/campaigns/campaignsSlice.js @@ -0,0 +1,18 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + items: [], + loading: false, + error: null, +}; + +const campaignsSlice = createSlice({ + name: 'campaigns', + initialState, + reducers: { + // Reducer placeholders + }, +}); + +export const { } = campaignsSlice.actions; +export default campaignsSlice.reducer; diff --git a/src/features/campaigns/campaignsThunks.js b/src/features/campaigns/campaignsThunks.js new file mode 100644 index 0000000..2bfe40f --- /dev/null +++ b/src/features/campaigns/campaignsThunks.js @@ -0,0 +1,12 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; + +export const fetchCampaigns = createAsyncThunk( + 'campaigns/fetchAll', + async (_, { rejectWithValue }) => { + try { + // Async logic placeholder + } catch (error) { + return rejectWithValue(error.message); + } + } +); diff --git a/src/features/dashboard/dashboardSelectors.js b/src/features/dashboard/dashboardSelectors.js new file mode 100644 index 0000000..f36fb27 --- /dev/null +++ b/src/features/dashboard/dashboardSelectors.js @@ -0,0 +1,3 @@ +export const selectDashboardStats = (state) => state.dashboard.stats; +export const selectDashboardLoading = (state) => state.dashboard.loading; +export const selectDashboardError = (state) => state.dashboard.error; diff --git a/src/features/dashboard/dashboardSlice.js b/src/features/dashboard/dashboardSlice.js new file mode 100644 index 0000000..02a97fb --- /dev/null +++ b/src/features/dashboard/dashboardSlice.js @@ -0,0 +1,18 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + stats: {}, + loading: false, + error: null, +}; + +const dashboardSlice = createSlice({ + name: 'dashboard', + initialState, + reducers: { + // Reducer placeholders + }, +}); + +export const { } = dashboardSlice.actions; +export default dashboardSlice.reducer; diff --git a/src/features/dashboard/dashboardThunks.js b/src/features/dashboard/dashboardThunks.js new file mode 100644 index 0000000..9601ea8 --- /dev/null +++ b/src/features/dashboard/dashboardThunks.js @@ -0,0 +1,12 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; + +export const fetchDashboardStats = createAsyncThunk( + 'dashboard/fetchStats', + async (_, { rejectWithValue }) => { + try { + // Async logic placeholder + } catch (error) { + return rejectWithValue(error.message); + } + } +); diff --git a/src/features/donations/donationsSelectors.js b/src/features/donations/donationsSelectors.js new file mode 100644 index 0000000..dfc0d5a --- /dev/null +++ b/src/features/donations/donationsSelectors.js @@ -0,0 +1,3 @@ +export const selectDonationHistory = (state) => state.donations.history; +export const selectDonationsLoading = (state) => state.donations.loading; +export const selectDonationsError = (state) => state.donations.error; diff --git a/src/features/donations/donationsSlice.js b/src/features/donations/donationsSlice.js new file mode 100644 index 0000000..bad3336 --- /dev/null +++ b/src/features/donations/donationsSlice.js @@ -0,0 +1,18 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + history: [], + loading: false, + error: null, +}; + +const donationsSlice = createSlice({ + name: 'donations', + initialState, + reducers: { + // Reducer placeholders + }, +}); + +export const { } = donationsSlice.actions; +export default donationsSlice.reducer; diff --git a/src/features/donations/donationsThunks.js b/src/features/donations/donationsThunks.js new file mode 100644 index 0000000..418d418 --- /dev/null +++ b/src/features/donations/donationsThunks.js @@ -0,0 +1,12 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; + +export const fetchDonations = createAsyncThunk( + 'donations/fetchAll', + async (_, { rejectWithValue }) => { + try { + // Async logic placeholder + } catch (error) { + return rejectWithValue(error.message); + } + } +); diff --git a/src/store/README.md b/src/store/README.md new file mode 100644 index 0000000..d5fe00f --- /dev/null +++ b/src/store/README.md @@ -0,0 +1,30 @@ +# Redux Store Configuration + +This directory contains the Redux store configuration and global state management logic. + +## Folder Structure Convention + +We follow a feature-based folder structure for Redux slices. Each domain or feature should have its own directory under `src/features/`. + +### Feature Folder Contents + +Each feature folder (e.g., `src/features/auth/`) should contain: + +1. **`[feature]Slice.js`**: Defines the Redux slice using `createSlice` from `@reduxjs/toolkit`. It should export the reducer as the default export and any actions as named exports. +2. **`[feature]Thunks.js`**: Contains async thunks created using `createAsyncThunk` for handling side effects and API calls. +3. **`[feature]Selectors.js`**: Contains memoized selectors (using `createSelector` if needed) to access specific parts of the feature's state. + +## Adding a New Feature + +To add a new feature to the Redux store: + +1. Create a new directory in `src/features/` (e.g., `src/features/newFeature/`). +2. Add the slice, thunks, and selectors files as described above. +3. Import the new reducer in `src/store/rootReducer.js` and add it to the `combineReducers` call. + +## Files in this Directory + +- `index.js`: The main store configuration where the `rootReducer` is integrated. +- `rootReducer.js`: Combines all feature-level reducers into a single root reducer. +- `hooks.js`: Custom Redux hooks (`useAppDispatch`, `useAppSelector`) for better TypeScript/linting support. +- `README.md`: This documentation file. diff --git a/src/store/index.js b/src/store/index.js index 41010df..8f74c26 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,9 +1,5 @@ import { configureStore } from '@reduxjs/toolkit'; - -// A simple placeholder reducer to fulfill the requirements until actual features are added -const rootReducer = (state = {}) => { - return state; -}; +import rootReducer from './rootReducer'; export const store = configureStore({ reducer: rootReducer, diff --git a/src/store/rootReducer.js b/src/store/rootReducer.js new file mode 100644 index 0000000..daab780 --- /dev/null +++ b/src/store/rootReducer.js @@ -0,0 +1,18 @@ +import { combineReducers } from '@reduxjs/toolkit'; +import authReducer from '../features/auth/authSlice'; +import campaignsReducer from '../features/campaigns/campaignsSlice'; +import donationsReducer from '../features/donations/donationsSlice'; +import dashboardReducer from '../features/dashboard/dashboardSlice'; +import adminReducer from '../features/admin/adminSlice'; +import bookmarksReducer from '../features/bookmarks/bookmarksSlice'; + +const rootReducer = combineReducers({ + auth: authReducer, + campaigns: campaignsReducer, + donations: donationsReducer, + dashboard: dashboardReducer, + admin: adminReducer, + bookmarks: bookmarksReducer, +}); + +export default rootReducer; From 67badeb93f6eb0b23e8d2e5637fe00ea7a4bbb82 Mon Sep 17 00:00:00 2001 From: jahrulezfrancis Date: Mon, 2 Mar 2026 11:24:51 +0100 Subject: [PATCH 2/2] fixing lint error --- src/features/admin/adminSlice.js | 2 +- src/features/auth/authSlice.js | 2 +- src/features/bookmarks/bookmarksSlice.js | 2 +- src/features/campaigns/campaignsSlice.js | 2 +- src/features/dashboard/dashboardSlice.js | 2 +- src/features/donations/donationsSlice.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/features/admin/adminSlice.js b/src/features/admin/adminSlice.js index f15d39b..8121700 100644 --- a/src/features/admin/adminSlice.js +++ b/src/features/admin/adminSlice.js @@ -15,5 +15,5 @@ const adminSlice = createSlice({ }, }); -export const { } = adminSlice.actions; + export default adminSlice.reducer; diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js index 37da583..34450b2 100644 --- a/src/features/auth/authSlice.js +++ b/src/features/auth/authSlice.js @@ -15,5 +15,5 @@ const authSlice = createSlice({ }, }); -export const { } = authSlice.actions; + export default authSlice.reducer; diff --git a/src/features/bookmarks/bookmarksSlice.js b/src/features/bookmarks/bookmarksSlice.js index 1cbae7b..6767a7d 100644 --- a/src/features/bookmarks/bookmarksSlice.js +++ b/src/features/bookmarks/bookmarksSlice.js @@ -14,5 +14,5 @@ const bookmarksSlice = createSlice({ }, }); -export const { } = bookmarksSlice.actions; + export default bookmarksSlice.reducer; diff --git a/src/features/campaigns/campaignsSlice.js b/src/features/campaigns/campaignsSlice.js index 8760122..2929833 100644 --- a/src/features/campaigns/campaignsSlice.js +++ b/src/features/campaigns/campaignsSlice.js @@ -14,5 +14,5 @@ const campaignsSlice = createSlice({ }, }); -export const { } = campaignsSlice.actions; + export default campaignsSlice.reducer; diff --git a/src/features/dashboard/dashboardSlice.js b/src/features/dashboard/dashboardSlice.js index 02a97fb..54c7558 100644 --- a/src/features/dashboard/dashboardSlice.js +++ b/src/features/dashboard/dashboardSlice.js @@ -14,5 +14,5 @@ const dashboardSlice = createSlice({ }, }); -export const { } = dashboardSlice.actions; + export default dashboardSlice.reducer; diff --git a/src/features/donations/donationsSlice.js b/src/features/donations/donationsSlice.js index bad3336..347789d 100644 --- a/src/features/donations/donationsSlice.js +++ b/src/features/donations/donationsSlice.js @@ -14,5 +14,5 @@ const donationsSlice = createSlice({ }, }); -export const { } = donationsSlice.actions; + export default donationsSlice.reducer;