Add dashboard functionality: Integrate dashboardRouter and UserDashboard model, enabling user-specific dashboard configurations. Update LoggedInView to support dynamic widget management, including adding, removing, and saving widget configurations, enhancing user experience and interactivity.
This commit is contained in:
@@ -20,6 +20,7 @@ import taxiMapRouter from './routers/taxiMapRouter.js';
|
||||
import taxiHighscoreRouter from './routers/taxiHighscoreRouter.js';
|
||||
import termineRouter from './routers/termineRouter.js';
|
||||
import vocabRouter from './routers/vocabRouter.js';
|
||||
import dashboardRouter from './routers/dashboardRouter.js';
|
||||
import cors from 'cors';
|
||||
import './jobs/sessionCleanup.js';
|
||||
|
||||
@@ -78,6 +79,7 @@ app.use('/api/friendships', friendshipRouter);
|
||||
app.use('/api/models', modelsProxyRouter);
|
||||
app.use('/api/blog', blogRouter);
|
||||
app.use('/api/termine', termineRouter);
|
||||
app.use('/api/dashboard', dashboardRouter);
|
||||
|
||||
// Serve frontend SPA for non-API routes to support history mode clean URLs
|
||||
// /models/* nicht statisch ausliefern – nur über /api/models (Proxy mit Komprimierung)
|
||||
|
||||
39
backend/controllers/dashboardController.js
Normal file
39
backend/controllers/dashboardController.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import dashboardService from '../services/dashboardService.js';
|
||||
|
||||
function getHashedUserId(req) {
|
||||
return req.headers?.userid;
|
||||
}
|
||||
|
||||
export default {
|
||||
async getConfig(req, res) {
|
||||
const hashedUserId = getHashedUserId(req);
|
||||
if (!hashedUserId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
try {
|
||||
const config = await dashboardService.getConfig(hashedUserId);
|
||||
res.json(config);
|
||||
} catch (error) {
|
||||
console.error('Dashboard getConfig:', error);
|
||||
res.status(500).json({ error: error.message || 'Internal server error' });
|
||||
}
|
||||
},
|
||||
|
||||
async setConfig(req, res) {
|
||||
const hashedUserId = getHashedUserId(req);
|
||||
if (!hashedUserId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
const config = req.body;
|
||||
if (!config || typeof config !== 'object') {
|
||||
return res.status(400).json({ error: 'Invalid config' });
|
||||
}
|
||||
try {
|
||||
const result = await dashboardService.setConfig(hashedUserId, config);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
console.error('Dashboard setConfig:', error);
|
||||
res.status(500).json({ error: error.message || 'Internal server error' });
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -5,6 +5,7 @@ import ChatUser from './chat/user.js';
|
||||
import Room from './chat/room.js';
|
||||
import User from './community/user.js';
|
||||
import UserParam from './community/user_param.js';
|
||||
import UserDashboard from './community/user_dashboard.js';
|
||||
import UserParamType from './type/user_param.js';
|
||||
import UserRightType from './type/user_right.js';
|
||||
import UserRight from './community/user_right.js';
|
||||
@@ -166,6 +167,9 @@ export default function setupAssociations() {
|
||||
User.hasMany(UserParam, { foreignKey: 'userId', as: 'user_params' });
|
||||
UserParam.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||
|
||||
User.hasOne(UserDashboard, { foreignKey: 'userId', as: 'dashboard' });
|
||||
UserDashboard.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||
|
||||
UserParamValue.belongsTo(UserParamType, { foreignKey: 'userParamTypeId', as: 'user_param_value_type' });
|
||||
UserParamType.hasMany(UserParamValue, { foreignKey: 'userParamTypeId', as: 'user_param_type_value' });
|
||||
|
||||
|
||||
24
backend/models/community/user_dashboard.js
Normal file
24
backend/models/community/user_dashboard.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { sequelize } from '../../utils/sequelize.js';
|
||||
import { DataTypes } from 'sequelize';
|
||||
import User from './user.js';
|
||||
|
||||
const UserDashboard = sequelize.define('user_dashboard', {
|
||||
userId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
references: { model: User, key: 'id' }
|
||||
},
|
||||
config: {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: false,
|
||||
defaultValue: { widgets: [] }
|
||||
}
|
||||
}, {
|
||||
tableName: 'user_dashboard',
|
||||
schema: 'community',
|
||||
underscored: true,
|
||||
timestamps: false
|
||||
});
|
||||
|
||||
export default UserDashboard;
|
||||
@@ -6,6 +6,7 @@ import UserParamType from './type/user_param.js';
|
||||
import UserRightType from './type/user_right.js';
|
||||
import User from './community/user.js';
|
||||
import UserParam from './community/user_param.js';
|
||||
import UserDashboard from './community/user_dashboard.js';
|
||||
import Login from './logs/login.js';
|
||||
import UserRight from './community/user_right.js';
|
||||
import InterestType from './type/interest.js';
|
||||
@@ -152,6 +153,7 @@ const models = {
|
||||
UserRightType,
|
||||
User,
|
||||
UserParam,
|
||||
UserDashboard,
|
||||
Login,
|
||||
UserRight,
|
||||
InterestType,
|
||||
|
||||
10
backend/routers/dashboardRouter.js
Normal file
10
backend/routers/dashboardRouter.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Router } from 'express';
|
||||
import { authenticate } from '../middleware/authMiddleware.js';
|
||||
import dashboardController from '../controllers/dashboardController.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/config', authenticate, dashboardController.getConfig.bind(dashboardController));
|
||||
router.put('/config', authenticate, dashboardController.setConfig.bind(dashboardController));
|
||||
|
||||
export default router;
|
||||
37
backend/services/dashboardService.js
Normal file
37
backend/services/dashboardService.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import BaseService from './BaseService.js';
|
||||
import UserDashboard from '../models/community/user_dashboard.js';
|
||||
|
||||
class DashboardService extends BaseService {
|
||||
/**
|
||||
* @param {string} hashedUserId
|
||||
* @returns {Promise<{ widgets: Array<{ id: string, title: string, endpoint: string }> }>}
|
||||
*/
|
||||
async getConfig(hashedUserId) {
|
||||
const user = await this.getUserByHashedId(hashedUserId);
|
||||
const row = await UserDashboard.findOne({ where: { userId: user.id } });
|
||||
const config = row?.config ?? { widgets: [] };
|
||||
if (!Array.isArray(config.widgets)) config.widgets = [];
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} hashedUserId
|
||||
* @param {{ widgets: Array<{ id: string, title: string, endpoint: string }> }} config
|
||||
*/
|
||||
async setConfig(hashedUserId, config) {
|
||||
const user = await this.getUserByHashedId(hashedUserId);
|
||||
const widgets = Array.isArray(config?.widgets) ? config.widgets : [];
|
||||
const sanitized = widgets.map(w => ({
|
||||
id: String(w?.id ?? ''),
|
||||
title: String(w?.title ?? ''),
|
||||
endpoint: String(w?.endpoint ?? '')
|
||||
})).filter(w => w.id && (w.title || w.endpoint));
|
||||
await UserDashboard.upsert({
|
||||
userId: user.id,
|
||||
config: { widgets: sanitized }
|
||||
}, { conflictFields: ['userId'] });
|
||||
return { widgets: sanitized };
|
||||
}
|
||||
}
|
||||
|
||||
export default new DashboardService();
|
||||
Reference in New Issue
Block a user