From d4e6b5c81e2e75b6c7385b7682d12894c52317eb Mon Sep 17 00:00:00 2001
From: Matthias Feyll <matthias.feyll@stud.h-da.de>
Date: Tue, 27 Aug 2024 10:43:23 +0200
Subject: [PATCH] (fix) fix race conidtion on login | refetching persist in
 store now

---
 react-ui/scripts/modify-api.sh                | 32 --------
 react-ui/scripts/template.js                  | 18 -----
 .../landingpage.scss => device/device.scss}   |  0
 .../components/view/device/device.view.tsx    | 50 +++++++++++++
 .../view/landingpage/landingpage.tsx          | 11 ---
 .../src/components/view/login/login.view.tsx  |  4 +-
 .../src/components/view/splash/splash.view.ts |  0
 .../components/view_model/device.viewmodel.ts | 48 ++++++++++++
 .../components/view_model/login.viewmodel.ts  |  1 -
 react-ui/src/index.tsx                        | 29 +-------
 react-ui/src/routes.tsx                       | 16 ++++
 react-ui/src/stores/api.store.ts              |  8 +-
 react-ui/src/stores/persist.store.ts          |  2 +
 react-ui/src/stores/reducer/device.reducer.ts | 74 +++++++++++++++++++
 .../src/stores/reducer/schedule.reducer.ts    | 50 +++++++------
 react-ui/src/stores/reducer/user.reducer.ts   | 57 +++++++-------
 react-ui/src/utils/helper/coookie.ts          | 14 ++++
 .../{auth.layout.tsx => basic.layout.tsx}     |  9 +--
 react-ui/src/utils/layouts/login.layout.tsx   |  4 +-
 .../protected.layout/protected.layout.scss    |  8 +-
 .../protected.layout/protected.layout.tsx     | 14 ++--
 react-ui/src/utils/provider/auth.provider.tsx | 16 ++--
 .../src/utils/provider/fetch.provider.tsx     | 21 ------
 react-ui/src/utils/scheduler.tsx              | 20 -----
 react-ui/tsconfig.json                        |  4 +-
 react-ui/tsconfig.node.json                   |  2 +-
 react-ui/vite.config.mjs                      |  2 +
 27 files changed, 302 insertions(+), 212 deletions(-)
 delete mode 100755 react-ui/scripts/modify-api.sh
 delete mode 100644 react-ui/scripts/template.js
 rename react-ui/src/components/view/{landingpage/landingpage.scss => device/device.scss} (100%)
 create mode 100644 react-ui/src/components/view/device/device.view.tsx
 delete mode 100644 react-ui/src/components/view/landingpage/landingpage.tsx
 delete mode 100644 react-ui/src/components/view/splash/splash.view.ts
 create mode 100644 react-ui/src/components/view_model/device.viewmodel.ts
 create mode 100644 react-ui/src/routes.tsx
 create mode 100644 react-ui/src/stores/reducer/device.reducer.ts
 create mode 100644 react-ui/src/utils/helper/coookie.ts
 rename react-ui/src/utils/layouts/{auth.layout.tsx => basic.layout.tsx} (52%)
 delete mode 100644 react-ui/src/utils/provider/fetch.provider.tsx
 delete mode 100644 react-ui/src/utils/scheduler.tsx

diff --git a/react-ui/scripts/modify-api.sh b/react-ui/scripts/modify-api.sh
deleted file mode 100755
index d5b9fdf5b..000000000
--- a/react-ui/scripts/modify-api.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env sh
-
-# Verzeichnis mit den generierten Dateien
-GENERATED_DIR="../src/utils/api/"
-
-# Template-Datei
-TEMPLATE_FILE="./template.js"
-
-# Stelle sicher, dass das Template existiert
-if [[ ! -f "$TEMPLATE_FILE" ]]; then
-  echo "Template file not found: $TEMPLATE_FILE"
-  exit 1
-fi
-
-# Füge den Inhalt des Templates in jede Query-Funktion ein
-for file in "$GENERATED_DIR"/*.ts; do
-  if grep -q 'builder.query' "$file"; then
-    echo "Processing $file..."
-
-    # Überprüfen, ob onCacheEntryAdded bereits vorhanden ist
-    if ! grep -q 'onCacheEntryAdded' "$file"; then
-      # Füge das Template in die Query-Funktion ein
-      sed -i.bak '/builder.query.*{/r '"$TEMPLATE_FILE" "$file"
-      
-      echo "Extended $file with onCacheEntryAdded."
-    else
-      echo "$file already contains onCacheEntryAdded, skipping."
-    fi
-  fi
-done
-
-echo "All applicable files processed."
\ No newline at end of file
diff --git a/react-ui/scripts/template.js b/react-ui/scripts/template.js
deleted file mode 100644
index 2fa7b2993..000000000
--- a/react-ui/scripts/template.js
+++ /dev/null
@@ -1,18 +0,0 @@
-onCacheEntryAdded: async (arg, { updateCachedData, cacheDataLoaded, cacheEntryRemoved }) => {
-    try {
-      // Warte, bis der Cache geladen ist
-      await cacheDataLoaded;
-  
-      // Beobachte kontinuierlich Änderungen am Cache
-      const unsubscribe = updateCachedData((draft) => {
-        console.log('Updated data:', draft);
-        // Hier kannst du auf die Daten zugreifen und z.B. weitere Aktionen auslösen
-      });
-  
-      // Aufräumen, wenn der Cache entfernt wird
-      await cacheEntryRemoved;
-      unsubscribe();
-    } catch (err) {
-      console.error('Error in onCacheEntryAdded:', err);
-    }
-  },
\ No newline at end of file
diff --git a/react-ui/src/components/view/landingpage/landingpage.scss b/react-ui/src/components/view/device/device.scss
similarity index 100%
rename from react-ui/src/components/view/landingpage/landingpage.scss
rename to react-ui/src/components/view/device/device.scss
diff --git a/react-ui/src/components/view/device/device.view.tsx b/react-ui/src/components/view/device/device.view.tsx
new file mode 100644
index 000000000..8a664c4d8
--- /dev/null
+++ b/react-ui/src/components/view/device/device.view.tsx
@@ -0,0 +1,50 @@
+import { useAppSelector } from '@hooks';
+import { Col, Container, Row, Table } from 'react-bootstrap';
+import './device.scss';
+import { useDeviceViewModel } from '@viewmodel/device.viewmodel';
+
+function DeviceView() {
+    const { devices } = useAppSelector(state => state.device);
+    useDeviceViewModel();
+
+
+    const getDeviceTable = () => {
+        return devices.map((device, index) => (
+            <tr key={index}>
+                <td>{device.name}</td>
+                <td>{device.id}</td>
+                <td>{device.pid}</td>
+            </tr>
+        ))
+    }
+
+    return (
+        <div className='m-4 pt-4'>
+            <Container className="bg-white rounded c-box">
+                <Row >
+                    <Col><h3>Device list</h3></Col>
+                </Row>
+
+                <Row className='mt-2'>
+                    <Col>
+                        <Table striped bordered hover className='table-primary'>
+                            <thead>
+                                <tr>
+                                    <th>Name</th>
+                                    <th>UUID</th>
+                                    <th>User</th>
+                                    <th>Last updated</th>
+                                </tr>
+                            </thead>
+                            <tbody>
+                                {getDeviceTable()}
+                            </tbody>
+                        </Table>
+                    </Col>
+                </Row>
+            </Container>
+        </div>
+    )
+}
+
+export default DeviceView
diff --git a/react-ui/src/components/view/landingpage/landingpage.tsx b/react-ui/src/components/view/landingpage/landingpage.tsx
deleted file mode 100644
index 62c358639..000000000
--- a/react-ui/src/components/view/landingpage/landingpage.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import './landingpage.scss'
-
-function Landingpage() {
-    return (
-        <div className="App">
-
-        </div>
-    )
-}
-
-export default Landingpage
diff --git a/react-ui/src/components/view/login/login.view.tsx b/react-ui/src/components/view/login/login.view.tsx
index ba8da826e..405a42f84 100644
--- a/react-ui/src/components/view/login/login.view.tsx
+++ b/react-ui/src/components/view/login/login.view.tsx
@@ -6,7 +6,7 @@ import logo from '@assets/logo.svg'
 import useLoginViewModel from '@viewmodel/login.viewmodel'
 import React, { useRef } from 'react'
 
-const LoginPage = ({ children }) => {
+const LoginView = ({ children }) => {
     const { t } = useTranslation('common')
     const { login, handleErrorMessageRendering, displayFormFieldChecks, loginLoading } = useLoginViewModel();
 
@@ -82,4 +82,4 @@ const LoginPage = ({ children }) => {
     )
 }
 
-export default LoginPage
+export default LoginView
diff --git a/react-ui/src/components/view/splash/splash.view.ts b/react-ui/src/components/view/splash/splash.view.ts
deleted file mode 100644
index e69de29bb..000000000
diff --git a/react-ui/src/components/view_model/device.viewmodel.ts b/react-ui/src/components/view_model/device.viewmodel.ts
new file mode 100644
index 000000000..7ceb78809
--- /dev/null
+++ b/react-ui/src/components/view_model/device.viewmodel.ts
@@ -0,0 +1,48 @@
+import { api, NetworkelementFlattenedManagedNetworkElement, NetworkelementGetAllFlattenedResponse, NetworkElementServiceGetAllFlattenedApiArg } from "@api/api";
+import { useAppSelector } from "@hooks";
+import { setDevices } from "@reducer/device.reducer";
+import { QueryActionCreatorResult } from "@reduxjs/toolkit/query";
+import { useEffect } from "react";
+import { useDispatch } from "react-redux";
+import { AppDispatch } from "src/stores";
+
+const FETCH_DEVICES_INTERVAL = 15000; // in ms
+
+
+export const useDeviceViewModel = () => {
+    const { user } = useAppSelector(state => state.user);
+    const [triggerFetchDevices] = api.endpoints.networkElementServiceGetAllFlattened.useLazyQuerySubscription({
+        pollingInterval: FETCH_DEVICES_INTERVAL,
+        skipPollingIfUnfocused: true
+    });
+    const dispatch = useDispatch<AppDispatch>();
+
+
+    // TODO figure out how we get the proper response type here
+    let fetchDevicesSubscription: QueryActionCreatorResult<any> | undefined;
+
+
+    useEffect(() => {
+        fetchDevices();
+
+        return () => {
+            fetchDevicesSubscription?.unsubscribe();
+        }
+    }, [])
+
+    const fetchDevices = () => {
+        const payload: NetworkElementServiceGetAllFlattenedApiArg = {
+            pid: Object.keys(user?.roles)[0],
+            timestamp: new Date().getTime().toString(),
+        }
+
+        fetchDevicesSubscription = triggerFetchDevices(payload);
+        fetchDevicesSubscription.then((response) => {
+            const { mne } = response.data as NetworkelementGetAllFlattenedResponse;
+            dispatch(setDevices(mne));
+        });
+    }
+
+    return {
+    }
+}
\ No newline at end of file
diff --git a/react-ui/src/components/view_model/login.viewmodel.ts b/react-ui/src/components/view_model/login.viewmodel.ts
index 93f4c33f2..370834a0c 100644
--- a/react-ui/src/components/view_model/login.viewmodel.ts
+++ b/react-ui/src/components/view_model/login.viewmodel.ts
@@ -9,7 +9,6 @@ export interface PageLoginState {
 export default function useLoginViewModel() {
     const {login, loginProperties} = useAuth();
     const {isLoading: loginLoading, error: loginError, reset: resetLogin} = loginProperties!;
-
     
     const [localFormState, updateLocalFormState] = useState({
         submitted: false,
diff --git a/react-ui/src/index.tsx b/react-ui/src/index.tsx
index 50f9c3509..3eb8a604f 100644
--- a/react-ui/src/index.tsx
+++ b/react-ui/src/index.tsx
@@ -1,48 +1,27 @@
 import React from 'react'
 import ReactDOM, { Container } from 'react-dom/client'
 import {
-    Route,
-    RouterProvider,
-    createBrowserRouter,
-    createRoutesFromElements
+    RouterProvider
 } from 'react-router-dom'
-import Landingpage from './components/view/landingpage/landingpage'
 import './index.scss'
 
-import { BasicLayout } from '@layout/auth.layout'
-import { LoginLayout } from '@layout/login.layout'
-import { ProtectedLayout } from '@layout/protected.layout/protected.layout'
 import i18next from 'i18next'
 import { I18nextProvider } from 'react-i18next'
 import { Provider } from 'react-redux'
 import { ToastContainer } from 'react-toastify'
 import { PersistGate } from 'redux-persist/integration/react'
 import './i18n/config'
+import { router } from './routes'
 import { persistor, store } from './stores'
 import './utils/icons/icons'
 
-
-const root = ReactDOM.createRoot(document.getElementById('root') as Container)
-
-// create a proper routing
-const router = createBrowserRouter(
-    createRoutesFromElements(
-        <Route element={<BasicLayout />}>
-            <Route path="/login" element={<LoginLayout />} />
-            <Route element={<ProtectedLayout />}>
-                <Route path="/" element={<Landingpage />} />
-            </Route>
-        </Route>
-    )
-)
-
 const installToastify = () => {
     return (
         <ToastContainer />
     )
 }
 
-root.render(
+ReactDOM.createRoot(document.getElementById("root")).render(
     <React.StrictMode>
         <Provider store={store}>
             <PersistGate loading={null} persistor={persistor}>
@@ -53,4 +32,4 @@ root.render(
             </PersistGate>
         </Provider>
     </React.StrictMode>
-)
+);
\ No newline at end of file
diff --git a/react-ui/src/routes.tsx b/react-ui/src/routes.tsx
new file mode 100644
index 000000000..28a06cbfb
--- /dev/null
+++ b/react-ui/src/routes.tsx
@@ -0,0 +1,16 @@
+import { BasicLayout } from "@layout/basic.layout"
+import { LoginLayout } from "@layout/login.layout"
+import { ProtectedLayout } from "@layout/protected.layout/protected.layout"
+import DeviceView from "@view/device/device.view"
+import { createBrowserRouter, createRoutesFromElements, Route } from "react-router-dom"
+
+export const router = createBrowserRouter(
+    createRoutesFromElements(
+        <Route element={<BasicLayout />}>
+            <Route path="/login" element={<LoginLayout />} />
+            <Route element={<ProtectedLayout />}>
+                <Route path="/" element={<DeviceView />} />
+            </Route>
+        </Route>
+    )
+)
\ No newline at end of file
diff --git a/react-ui/src/stores/api.store.ts b/react-ui/src/stores/api.store.ts
index bd36b8227..fd894e97b 100644
--- a/react-ui/src/stores/api.store.ts
+++ b/react-ui/src/stores/api.store.ts
@@ -1,11 +1,11 @@
-import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
-import { RootState } from '.'
+import { getCookieValue } from '@helper/coookie';
+import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
 
 // initialize an empty api service that we'll inject endpoints into later as needed
 export const emptySplitApi = createApi({
   baseQuery: fetchBaseQuery({
-    baseUrl: '/api', prepareHeaders: (headers, { getState }) => {
-      const token = (getState() as RootState).user.token
+    baseUrl: '/api', prepareHeaders: (headers) => {
+      const token = getCookieValue('token');
 
       if (token) {
         headers.set('authorize', `${token}`)
diff --git a/react-ui/src/stores/persist.store.ts b/react-ui/src/stores/persist.store.ts
index 806b6e2e1..feb5241a8 100644
--- a/react-ui/src/stores/persist.store.ts
+++ b/react-ui/src/stores/persist.store.ts
@@ -4,6 +4,7 @@ import { persistReducer } from "redux-persist";
 import storage from "redux-persist/es/storage";
 import { emptySplitApi } from "./api.store";
 import scheduleReducer from "@reducer/schedule.reducer";
+import deviceReducer from "@reducer/device.reducer";
 
 
 /** local storage config */
@@ -16,6 +17,7 @@ const rootPersistConfig = {
 
 const rootReducer = combineReducers({
     user: userReducer, 
+    device: deviceReducer, 
     scheduler: scheduleReducer, 
     [emptySplitApi.reducerPath]: emptySplitApi.reducer,
 })
diff --git a/react-ui/src/stores/reducer/device.reducer.ts b/react-ui/src/stores/reducer/device.reducer.ts
new file mode 100644
index 000000000..ec47ff1f8
--- /dev/null
+++ b/react-ui/src/stores/reducer/device.reducer.ts
@@ -0,0 +1,74 @@
+import { api, NetworkelementFlattenedManagedNetworkElement, NetworkElementServiceGetAllFlattenedApiArg } from '@api/api';
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import { QueryActionCreatorResult } from '@reduxjs/toolkit/query';
+import { RootState } from '..';
+import { startListening } from '../middleware/listener.middleware';
+import { setUser } from './user.reducer';
+
+type Device = NetworkelementFlattenedManagedNetworkElement;
+
+
+export interface DeviceSliceState {
+    devices: Device[],
+}
+
+const initialState: DeviceSliceState = {
+    devices: [],
+}
+
+
+
+
+const deviceSlice = createSlice({
+    name: 'device',
+    initialState,
+    reducers: {
+        setDevices: (state, action: PayloadAction<Device[]>) => { state.devices = action.payload },
+    },
+})
+
+export const { setDevices } = deviceSlice.actions
+
+export default deviceSlice.reducer
+export const deviceReducerPath = deviceSlice.reducerPath;
+
+
+
+let fetchSubscription: QueryActionCreatorResult<any>[]  = [];
+export const abortFetching = () => {
+    fetchSubscription.forEach((subscription) => {
+        subscription.unsubscribe();
+    });
+    fetchSubscription = [];
+}
+
+// continously fetch devices
+const FETCH_DEVICES_INTERVAL = 5000; // in ms
+startListening({
+    actionCreator: setUser,
+    effect: async (_, listenerApi) => {
+        const { user } = listenerApi.getState() as RootState;
+
+        const payload: NetworkElementServiceGetAllFlattenedApiArg = {
+            pid: Object.keys(user?.user.roles)[0],
+            timestamp: new Date().getTime().toString(),
+        }
+
+        const subscription = listenerApi.dispatch(api.endpoints.networkElementServiceGetAllFlattened.initiate(payload, {
+            subscriptionOptions: {
+                pollingInterval: FETCH_DEVICES_INTERVAL,
+                skipPollingIfUnfocused: true,
+            }
+        }));
+
+        fetchSubscription = [...fetchSubscription, subscription];
+    },
+})
+
+// save fetched devices
+startListening({
+    predicate: (action) => api.endpoints.networkElementServiceGetAllFlattened.matchFulfilled(action),
+    effect: async (action, listenerApi) => {
+        listenerApi.dispatch(setDevices(action.payload.mne));
+    },
+})
\ No newline at end of file
diff --git a/react-ui/src/stores/reducer/schedule.reducer.ts b/react-ui/src/stores/reducer/schedule.reducer.ts
index 345b26bf2..2e2bbe36b 100644
--- a/react-ui/src/stores/reducer/schedule.reducer.ts
+++ b/react-ui/src/stores/reducer/schedule.reducer.ts
@@ -3,18 +3,21 @@ import { startListening } from '../middleware/listener.middleware';
 
 export enum ScheduleState { INIT, RUNNING, STOPPED }
 
-type Task<T> = (options: T) => void;
-
-interface Schedule<T> {
-    f: Task<T>,
+export type Task = {
+    job: (options: object) => void,
     interval: number,
+    type: string
+};
+
+interface Schedule {
+    task: Task,
     id: number,
     state: ScheduleState,
     intervalId: NodeJS.Timeout | undefined
 }
 
 export interface ScheduleReducerState {
-    schedules: Schedule<any>[]
+    schedules: Schedule[]
 }
 
 const initialState: ScheduleReducerState = {
@@ -25,10 +28,9 @@ const ScheduleSlice = createSlice({
     name: 'schedule',
     initialState,
     reducers: {
-        addSchedule: (state, action: PayloadAction<{ task: Task<any>, interval: number }>) => {
+        registerTask: (state, action: PayloadAction<Task>) => {
             const newSchedule = {
-                f: action.payload.task,
-                interval: action.payload.interval,
+                task: action.payload.task,
                 id: state.schedules.length,
                 state: ScheduleState.INIT,
                 intervalId: undefined
@@ -36,9 +38,9 @@ const ScheduleSlice = createSlice({
 
             state.schedules = [...state.schedules, newSchedule]
         },
-        startSchedule: (state, action: PayloadAction<Schedule<any>>) => {
+        startSchedule: (state, action: PayloadAction<Schedule>) => {
             const schedule = action.payload;
-            schedule.intervalId = setInterval(schedule.f, schedule.interval);
+            schedule.intervalId = setInterval(schedule.task.job, schedule.task.interval);
             schedule.state = ScheduleState.RUNNING;
             
             state.schedules[schedule.id] = schedule; 
@@ -46,25 +48,25 @@ const ScheduleSlice = createSlice({
     },
 })
 
-export const { addSchedule } = ScheduleSlice.actions
+export const { registerTask } = ScheduleSlice.actions
 export const { startSchedule } = ScheduleSlice.actions
 
 export default ScheduleSlice.reducer
 
 
-startListening({
-    actionCreator: addSchedule,
+// startListening({
+//     actionCreator: addSchedule,
 
-    effect: (action, listenerApi) => {
-        const newState = listenerApi.getState() as ScheduleReducerState;
-        const originalState = listenerApi.getOriginalState() as ScheduleReducerState;
+//     effect: (action, listenerApi) => {
+//         const newState = listenerApi.getState() as ScheduleReducerState;
+//         const originalState = listenerApi.getOriginalState() as ScheduleReducerState;
 
-        // get the added schedule
-        const schedule = newState.schedules.filter(s => !originalState.schedules.includes(s)).at(0);
-        if (!schedule) {
-            throw new Error("Added schedule not found in store");
-        }
+//         // get the added schedule
+//         const schedule = newState.schedules.filter(s => !originalState.schedules.includes(s)).at(0);
+//         if (!schedule) {
+//             throw new Error("Added schedule not found in store");
+//         }
 
-        listenerApi.dispatch(startSchedule(schedule))
-    },
-})
\ No newline at end of file
+//         listenerApi.dispatch(startSchedule(schedule))
+//     },
+// })
\ No newline at end of file
diff --git a/react-ui/src/stores/reducer/user.reducer.ts b/react-ui/src/stores/reducer/user.reducer.ts
index b1d5f99cd..797626db0 100644
--- a/react-ui/src/stores/reducer/user.reducer.ts
+++ b/react-ui/src/stores/reducer/user.reducer.ts
@@ -1,10 +1,10 @@
 import { api, RbacUser, UserServiceGetUsersApiArg } from '@api/api';
-import { createSlice, PayloadAction } from '@reduxjs/toolkit';
-import { startListening } from '../middleware/listener.middleware';
+import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
 import { RootState } from '..';
+import { startListening } from '../middleware/listener.middleware';
+import { getCookieValue, setCookieValue } from '@helper/coookie';
 
 export interface UserSliceState {
-    token: string,
     // defined by the frontend user input. This value is getting compared with the backend response
     username: string,
     user: RbacUser | null,
@@ -12,7 +12,6 @@ export interface UserSliceState {
 
 
 const initialState: UserSliceState = {
-    token: '',
     username: '',
     user: null,
 }
@@ -22,8 +21,10 @@ const userSlice = createSlice({
     initialState,
     reducers: {
         setToken: (state, action: PayloadAction<{ token: string, username: string }>) => {
-            state.token = action.payload.token;
-            state.username = action.payload.username
+            const token = action.payload?.token || '';
+            setCookieValue('token', token);
+            
+            state.username = action.payload?.username || ''
         },
         setUser: (state, action: PayloadAction<RbacUser>) => { state.user = action.payload },
     },
@@ -36,33 +37,27 @@ export default userSlice.reducer
 export const userReducerPath = userSlice.reducerPath;
 
 
+export const fetchUser = createAsyncThunk(
+    'user/fetchUser',
+    (_, thunkAPI) => {
+    const payload: UserServiceGetUsersApiArg = {};
 
-startListening({
-    predicate: (action, { user }: any) => {
-        return setToken.match(action) && !!user.token;
-    },
-
-    effect: async (_, listenerApi) => {
-        const payload: UserServiceGetUsersApiArg = {};
+    thunkAPI.dispatch(api.endpoints.userServiceGetUsers.initiate(payload)).then((response) => {
+        if (response.error || !response.data?.user?.length) {
+            // TODO proper error handling
+            throw new Error('Fetching the pnd list after successful login failed');
+        }
 
-        listenerApi.dispatch(api.endpoints.userServiceGetUsers.initiate(payload)).then((response) => {
-            if (response.error || !response.data?.user?.length) {
-                // TODO proper error handling
-                throw new Error('Fetching the pnd list after successful login failed');
-            }
+        const { user } = thunkAPI.getState() as RootState;
 
-            const {user} = listenerApi.getState() as RootState;
+        // TODO ask if this is the correct approach
+        const matchedUser = response.data.user.find((_user) => _user.name === user.username);
 
-            // TODO ask if this is the correct approach
-            const matchedUser = response.data.user.find((_user) => _user.name === user.username);
+        if (!matchedUser) {
+            // TODO proper error handling
+            throw new Error('No user found with the provided username');
+        }
 
-            if (!matchedUser) {
-                // TODO proper error handling
-                throw new Error('No user found with the provided username');
-            }
-
-
-            listenerApi.dispatch(setUser(matchedUser));
-        });
-    },
-})
\ No newline at end of file
+        thunkAPI.dispatch(setUser(matchedUser));
+    });
+});
\ No newline at end of file
diff --git a/react-ui/src/utils/helper/coookie.ts b/react-ui/src/utils/helper/coookie.ts
new file mode 100644
index 000000000..75cba2dc8
--- /dev/null
+++ b/react-ui/src/utils/helper/coookie.ts
@@ -0,0 +1,14 @@
+export const getCookieValue = (name: string): string => {
+  const regex = new RegExp(`(^| )${name}=([^;]+)`)
+  const match = document.cookie.match(regex)
+
+  if (match) {
+    return match[2];
+  }
+
+  return '';
+}
+
+export const setCookieValue = (key: string, value: string): void => {
+  document.cookie = `${key}=${value}; Secure; SameSite=Lax`;
+}
\ No newline at end of file
diff --git a/react-ui/src/utils/layouts/auth.layout.tsx b/react-ui/src/utils/layouts/basic.layout.tsx
similarity index 52%
rename from react-ui/src/utils/layouts/auth.layout.tsx
rename to react-ui/src/utils/layouts/basic.layout.tsx
index a38da03a9..7b7f2b11c 100644
--- a/react-ui/src/utils/layouts/auth.layout.tsx
+++ b/react-ui/src/utils/layouts/basic.layout.tsx
@@ -1,19 +1,14 @@
 import { AuthProvider } from "@provider/auth.provider";
-import { useEffect } from "react";
 import { useOutlet } from "react-router-dom";
-import { ScheduleProvider } from "../scheduler";
 
 export const BasicLayout = () => {
     const outlet = useOutlet();
-    const { initSchedules } = ScheduleProvider();
 
-    useEffect(() => {
-        initSchedules();
-    }, [])
+    
 
     return (
         <AuthProvider>
-            {outlet}
+                {outlet}
         </AuthProvider>
     )
 }
\ No newline at end of file
diff --git a/react-ui/src/utils/layouts/login.layout.tsx b/react-ui/src/utils/layouts/login.layout.tsx
index 08a875f75..43e40aa30 100644
--- a/react-ui/src/utils/layouts/login.layout.tsx
+++ b/react-ui/src/utils/layouts/login.layout.tsx
@@ -1,5 +1,5 @@
 import { useAuth } from "@provider/auth.provider";
-import LoginPage from "@view/login/login.view";
+import LoginView from "@view/login/login.view";
 import { useEffect } from "react";
 import { useNavigate, useOutlet } from "react-router-dom";
 
@@ -18,6 +18,6 @@ export const LoginLayout = ({ children }) => {
     }, []);
 
     return (
-        <LoginPage>{outlet}</LoginPage>
+        <LoginView>{outlet}</LoginView>
     )
 }
\ No newline at end of file
diff --git a/react-ui/src/utils/layouts/protected.layout/protected.layout.scss b/react-ui/src/utils/layouts/protected.layout/protected.layout.scss
index eb713fa7a..47561615d 100644
--- a/react-ui/src/utils/layouts/protected.layout/protected.layout.scss
+++ b/react-ui/src/utils/layouts/protected.layout/protected.layout.scss
@@ -1,5 +1,7 @@
 @import "/src/style/colors.scss";
 
+$sidebar-width: 4.5em;
+
 .head-links {
     text-decoration: none;
     color: map-get($theme-colors, dark);
@@ -18,6 +20,10 @@
 }
 
 .sidebar {
-    width: 4.5em;
+    width: $sidebar-width;
     height: 100vh;
 }
+
+.main-content {
+    margin-left: $sidebar-width;
+}
\ No newline at end of file
diff --git a/react-ui/src/utils/layouts/protected.layout/protected.layout.tsx b/react-ui/src/utils/layouts/protected.layout/protected.layout.tsx
index 3993a0383..834b2ac5f 100644
--- a/react-ui/src/utils/layouts/protected.layout/protected.layout.tsx
+++ b/react-ui/src/utils/layouts/protected.layout/protected.layout.tsx
@@ -7,7 +7,8 @@ import { Dropdown } from "react-bootstrap";
 import { useTranslation } from "react-i18next";
 import { Link, Outlet, useNavigate } from "react-router-dom";
 import "./protected.layout.scss";
-import { useAppSelector } from '@hooks';
+import { useAppDispatch, useAppSelector } from '@hooks';
+import { fetchUser } from '@reducer/user.reducer';
 
 
 export const ProtectedLayout = () => {
@@ -15,11 +16,15 @@ export const ProtectedLayout = () => {
   const { user } = useAppSelector(state => state.user);
   const navigate = useNavigate();
   const { t } = useTranslation('common')
+  const dispatch = useAppDispatch();
 
   useEffect(() => {
     if (!isAuthenticated()) {
       navigate('/login')
+      return;
     }
+
+    dispatch(fetchUser());
   }, []);
 
   /**
@@ -27,9 +32,6 @@ export const ProtectedLayout = () => {
    */
   const handleActiveLink = (targetPath: string): string => {
     const href = window.location.href;
-    console.log(href);
-
-
     return href.includes(targetPath) ? ' active' : '';
   }
 
@@ -101,7 +103,9 @@ export const ProtectedLayout = () => {
     <div>
       {HorizontalNavbar()}
       {VerticalSidebar()}
-      <Outlet />
+      <div className='main-content'>
+        <Outlet />
+      </div>
     </div>
   )
 };
\ No newline at end of file
diff --git a/react-ui/src/utils/provider/auth.provider.tsx b/react-ui/src/utils/provider/auth.provider.tsx
index d583d4116..638d9b408 100644
--- a/react-ui/src/utils/provider/auth.provider.tsx
+++ b/react-ui/src/utils/provider/auth.provider.tsx
@@ -1,5 +1,7 @@
 import { AuthServiceLoginApiArg, AuthServiceLoginApiResponse, useAuthServiceLoginMutation } from "@api/api";
+import { getCookieValue } from "@helper/coookie";
 import { useAppDispatch, useAppSelector } from "@hooks";
+import { abortFetching } from "@reducer/device.reducer";
 import { setToken } from "@reducer/user.reducer";
 import { jwtDecode } from "jwt-decode";
 import { createContext, useContext, useEffect, useMemo } from "react";
@@ -30,17 +32,17 @@ const AuthContext = createContext<AuthProviderType>({
 export const AuthProvider = ({ children }) => {
     const dispatch = useAppDispatch();;
     const navigate = useNavigate();
-    const { token } = useAppSelector(state => state.user);
+    const { username } = useAppSelector(state => state.user);
 
     useEffect(() => {
-        console.log('auth provider init');
-
-        if (isAuthenticated()) {
+        const token = getCookieValue('token');
+        
+        if (token) {
             navigate('/')
         } else {
             navigate('/login')
         }
-    }, []);
+    }, [username]);
 
     const [
         sendLogin,
@@ -80,6 +82,7 @@ export const AuthProvider = ({ children }) => {
     }
 
     const isAuthenticated = () => {
+        const token = getCookieValue('token');
         if (!token) {
             return false;
         }
@@ -96,7 +99,8 @@ export const AuthProvider = ({ children }) => {
     }
 
     const logout = () => {
-        dispatch(setToken(""));
+        abortFetching();
+        dispatch(setToken(null));
         // TODO: purge other information
     }
 
diff --git a/react-ui/src/utils/provider/fetch.provider.tsx b/react-ui/src/utils/provider/fetch.provider.tsx
deleted file mode 100644
index 9db4e121c..000000000
--- a/react-ui/src/utils/provider/fetch.provider.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { useAppSelector } from "@hooks";
-import { useAuth } from "./auth.provider"
-
-
-// seconds before token expire to trigger a refetch
-const REFETCH_TOKEN_TIME = 43200;
-
-const FetchProvider = () => {
-    const { isAuthenticated: isAuthed } = useAuth();
-    const { token } = useAppSelector((root) => root.user);
-
-
-    const checkFetch = (): boolean => {
-        const { exp } = jwtDecode(token);
-        const currentTime = new Date().getTime() / 1000;
-
-        return exp 
-    }
-
-    return
-}
\ No newline at end of file
diff --git a/react-ui/src/utils/scheduler.tsx b/react-ui/src/utils/scheduler.tsx
deleted file mode 100644
index 51bf94de3..000000000
--- a/react-ui/src/utils/scheduler.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { useAppSelector } from "@hooks";
-import { startSchedule } from "@reducer/schedule.reducer";
-import { useDispatch } from "react-redux";
-import { AppDispatch } from "src/stores";
-
-export const ScheduleProvider = () => {
-    const dispatch = useDispatch<AppDispatch>();
-    const { schedules } = useAppSelector(state => state.scheduler);
-
-
-    const initSchedules = () => {
-        schedules.forEach(schedule => {
-            dispatch(startSchedule(schedule));
-        })
-    }
-
-    return {
-        initSchedules
-    }
-}
diff --git a/react-ui/tsconfig.json b/react-ui/tsconfig.json
index b8c8c0625..cc5d30818 100644
--- a/react-ui/tsconfig.json
+++ b/react-ui/tsconfig.json
@@ -15,7 +15,7 @@
       "jsx": "react-jsx",
   
       /* Linting */
-      "strict": true,
+      "strict": false,
       "noUnusedLocals": true,
       "noUnusedParameters": true,
       "noFallthroughCasesInSwitch": true,
@@ -30,6 +30,8 @@
           "@provider/*": ["src/utils/provider/*"],
           "@layout/*": ["src/utils/layouts/*"],
           "@hooks": ["src/hooks"],
+          "@task/*": ["src/utils/tasks/*"],
+          "@helper/*": ["src/utils/helper/*"],
       }
     },
     "include": [
diff --git a/react-ui/tsconfig.node.json b/react-ui/tsconfig.node.json
index 9357c44f3..3a29b0309 100644
--- a/react-ui/tsconfig.node.json
+++ b/react-ui/tsconfig.node.json
@@ -5,7 +5,7 @@
     "module": "ES2020",
     "moduleResolution": "bundler",
     "allowSyntheticDefaultImports": true,
-    "strict": true
+    "strict": false
   },
   "include": [
     "vite.config.ts"
diff --git a/react-ui/vite.config.mjs b/react-ui/vite.config.mjs
index c52ee47ca..dee436d2a 100644
--- a/react-ui/vite.config.mjs
+++ b/react-ui/vite.config.mjs
@@ -36,6 +36,8 @@ export default defineConfig({
       "@provider": "/src/utils/provider",
       "@layout": "/src/utils/layouts",
       "@hooks": "/src/hooks.ts",
+      "@task": "/src/utils/tasks",
+      "@helper": "/src/utils/helper",
     },
   },
 
-- 
GitLab