The store is configured in src/redux/store/configureStore.tsx.
Current reducer composition includes:
loginapi(RTK Query reducer path)
redux-persist is enabled with:
- storage: web local storage in the browser
- noop storage fallback for non-browser execution
- persist key:
session-portal - whitelist:
login - transform filter: only
sessionis persisted from the login slice
src/redux/store/provider.tsx wraps the app with:
- Redux
Provider PersistGate
src/redux/baseApi.tsx defines a shared RTK Query API instance:
- reducer path:
api - base query:
customBaseQuery - tag types: currently an empty tuple with a TODO comment
Authentication operations use Next.js server actions for security:
- Located in
src/app/login/actions.ts loginAndSetCookie: Validates Google OAuth token, sets HttpOnly cookie, returns session data for ReduxlogoutAndClearCookie: Clears authentication cookie and redirects to login
These actions complement client-side Redux state management by handling secure server-side operations.
src/redux/customBaseQuery.ts adds shared request behavior:
| Behavior | Description |
|---|---|
| Base host | ${NEXT_PUBLIC_BASE_URL}/api/v1 |
| Authorization | Adds Bearer <token> when an access token exists |
| Unauthorized handling | Dispatches login/logout on 401 |
| Error normalization | Uses parseError |
| User feedback | Shows toast notifications by default |
src/redux/login/slice.ts stores:
{
session: {
refresh,
access,
user_info: {
avatar,
first_name,
full_name,
last_name
}
},
error,
isLoading
}src/redux/login/selectors.ts exposes:
selectAccessTokenselectRefreshTokenselectUserInfoselectIsLoading
src/redux/login/apiSlice.ts injects a mutation:
| Hook | Method | Endpoint |
|---|---|---|
useLoginMutation |
POST |
/users/login |
Request body:
{
"auth_token": "<google-access-token>"
}Example response shape:
{
"access": "<jwt-or-access-token>",
"refresh": "<refresh-token>",
"user_info": {
"avatar": "https://example.com/avatar.png",
"first_name": "Alex",
"full_name": "Alex Example",
"last_name": "Example"
}
}- Google OAuth is initialized in
src/app/layout.tsxthroughGoogleOAuthProvider. - The frontend sends the Google access token to
POST /users/loginasauth_token. customBaseQueryattachesAuthorization: Bearer <token>when an access token is present.- A
401response triggers alogin/logoutdispatch.
src/redux/events/apiSlice.ts injects event-related queries.
| Hook | Endpoint | Purpose |
|---|---|---|
useEventDetailQuery |
/events/videoasset/{id}/ |
Single event/video asset detail |
useEventTagsQuery |
/events/tags/?linked_to_events=True |
Tag list |
useGetEventsQuery / lazy variant |
/events/all/ |
Paginated event listing |
useEventTypesQuery |
/events/event_types/ |
Event type list |
useRecommendationQuery |
/events/recommendations/{id}/ |
Recommendations for a video |
usePlaylistsQuery |
/events/playlists/?linked_to_events=True |
Playlist list |
Example event detail request:
GET /events/videoasset/{id}/
Example recommendation request:
GET /events/recommendations/{id}/?page=1&page_size=10
Observed custom behavior includes:
- custom
serializeQueryArgs - result merging for infinite scrolling
- duplicate prevention by event ID
forceRefetchusinglodash/isEqual
src/redux/parseError.ts normalizes error payloads from different shapes:
- server errors
- strings
- arrays
- nested objects
- null/undefined object values
The result format is:
type ErrorType = {
statusCode: number | string;
message: string;
details?: Record<string, string>;
}- RTK Query tags are not currently configured, so tag-based invalidation is not documented.
- Only frontend-visible API paths are documented here; backend response contracts may be broader than the inferred model types.
- The repository does not include canonical backend error response examples, so exact non-
401error payload shapes should be confirmed from the backend service.