feat: initialize learning platform project with React, Vite, and baseline application structure

This commit is contained in:
RaymondVerhoef
2026-05-10 10:30:30 +02:00
commit 2fb50a19c9
23 changed files with 4304 additions and 0 deletions

116
src/store/AppContext.jsx Normal file
View File

@@ -0,0 +1,116 @@
import React, { createContext, useContext, useReducer, useEffect } from 'react';
import { storage } from '../lib/storage';
const AppContext = createContext();
const initialState = {
currentUser: null, // will hold user object if logged in
users: [], // array of registered users
weekNumber: 1, // current learning week
isLoading: true
};
function appReducer(state, action) {
switch (action.type) {
case 'INIT_APP':
return {
...state,
users: action.payload.users,
weekNumber: action.payload.weekNumber,
isLoading: false
};
case 'LOGIN':
return {
...state,
currentUser: action.payload
};
case 'LOGOUT':
return {
...state,
currentUser: null
};
case 'REGISTER_USER':
return {
...state,
users: [...state.users, action.payload]
};
case 'ADVANCE_WEEK':
return {
...state,
weekNumber: state.weekNumber + 1
};
default:
return state;
}
}
export function AppProvider({ children }) {
const [state, dispatch] = useReducer(appReducer, initialState);
// Initialize app state from storage
useEffect(() => {
const loadState = () => {
let users = storage.get('users:registry');
// Seed first admin if no users exist
if (!users || users.length === 0) {
const initialAdmin = {
id: 'u_1',
name: 'Admin',
role: 'admin',
pin: '0000',
registeredAt: new Date().toISOString()
};
users = [initialAdmin];
storage.set('users:registry', users);
// Also seed initial empty leaderboard
storage.set('leaderboard:current', []);
}
const storedWeek = storage.get('admin:current_week') || 1;
// Automatically login if we saved session (optional, simpler to require login per session)
const sessionUserId = sessionStorage.getItem('respellion_session');
if (sessionUserId) {
const user = users.find(u => u.id === sessionUserId);
if (user) {
dispatch({ type: 'LOGIN', payload: user });
}
}
dispatch({
type: 'INIT_APP',
payload: { users, weekNumber: storedWeek }
});
};
loadState();
}, []);
// Expose dispatch actions as convenient methods
const login = (userId, pin) => {
const user = state.users.find(u => u.id === userId && u.pin === pin);
if (user) {
sessionStorage.setItem('respellion_session', user.id);
dispatch({ type: 'LOGIN', payload: user });
return true;
}
return false;
};
const logout = () => {
sessionStorage.removeItem('respellion_session');
dispatch({ type: 'LOGOUT' });
};
return (
<AppContext.Provider value={{ state, dispatch, login, logout }}>
{children}
</AppContext.Provider>
);
}
export function useApp() {
return useContext(AppContext);
}