Files
yourpart3/backend/controllers/navigationController.js
Torsten Schulz (local) 83597d9e02 Add Vocab Trainer feature with routing, database schema, and translations
- Introduced Vocab Trainer functionality, including new routes for managing languages and chapters.
- Implemented database schema for vocab-related tables to ensure data integrity.
- Updated navigation and UI components to include Vocab Trainer in the social network menu.
- Added translations for Vocab Trainer in both German and English locales, enhancing user accessibility.
2025-12-30 18:34:32 +01:00

416 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import User from '../models/community/user.js';
import UserParam from '../models/community/user_param.js';
import UserRight from '../models/community/user_right.js';
import UserRightType from '../models/type/user_right.js';
import UserParamType from '../models/type/user_param.js';
import FalukantUser from '../models/falukant/data/user.js';
import VocabService from '../services/vocabService.js';
const menuStructure = {
home: {
visible: ["all"],
children: {},
path: "/",
icon: "logo_mono.png"
},
friends: {
visible: ["all"],
children: {
manageFriends: {
visible: ["all"],
path: "/friends",
icon: "friends24.png"
}
},
showLoggedinFriends: 1,
icon: "friends24.png"
},
socialnetwork: {
visible: ["all"],
icon: "socialnetwork.png",
children: {
guestbook: {
visible: ["all"],
path: "/socialnetwork/guestbook"
},
blog: {
visible: ["all"],
path: "/blogs"
},
usersearch: {
visible: ["all"],
path: "/socialnetwork/search"
},
forum: {
visible: ["all"],
path: "/socialnetwork/forum",
showForums: 1
},
gallery: {
visible: ["all"],
path: "/socialnetwork/gallery"
},
vocabtrainer: {
visible: ["all"],
path: "/socialnetwork/vocab",
children: {}
},
blockedUsers: {
visible: ["all"],
path: "/socialnetwork/blocked"
},
oneTimeInvitation: {
visible: ["all"],
path: "/socialnetwork/onetimeinvitation"
},
diary: {
visible: ["all"],
path: "/socialnetwork/diary"
},
erotic: {
visible: ["over18"],
children: {
pictures: {
visible: ["over18"],
path: "/socialnetwork/erotic/pictures"
},
videos: {
visible: ["over18"],
path: "/socialnetwork/erotic/videos"
}
}
}
}
},
chats: {
visible: ["over12"],
icon: "chat.png",
children: {
multiChat: {
visible: ["over12"],
action: "openMultiChat",
view: "window",
class: "multiChatDialog",
icon: "multichat24.png"
},
randomChat: {
visible: ["over12"],
action: "openRanomChat",
view: "window",
class: "randomChatDialog"
},
eroticChat: {
visible: ["over18"],
action: "openEroticChat",
view: "window",
class: "eroticChatWindow"
}
}
},
falukant: {
visible: ["all"],
icon: "falukant16.png",
children: {
create: {
visible: ["nofalukantaccount"],
path: "/falukant/create"
},
overview: {
visible: ["hasfalukantaccount"],
path: "/falukant/home"
},
towns: {
visible: ["hasfalukantaccount"],
path: "/falukant/branch"
},
family: {
visible: ["hasfalukantaccount"],
path: "/falukant/family"
},
house: {
visible: ["hasfalukantaccount"],
path: "/falukant/house"
},
nobility: {
visible: ["hasfalukantaccount"],
path: "/falukant/nobility"
},
church: {
visible: ["hasfalukantaccount"],
path: "/falukant/church"
},
politics: {
visible: ["hasfalukantaccount"],
path: "/falukant/politics"
},
education: {
visible: ["hasfalukantaccount"],
path: "/falukant/education"
},
health: {
visible: ["hasfalukantaccount"],
path: "/falukant/health"
},
bank: {
visible: ["hasfalukantaccount"],
path: "/falukant/bank"
},
darknet: {
visible: ["hasfalukantaccount"],
path: "/falukant/darknet"
},
reputation: {
visible: ["hasfalukantaccount"],
path: "/falukant/reputation"
},
moneyhistory: {
visible: ["hasfalukantaccount"],
path: "/falukant/moneyhistory"
}
}
},
minigames: {
visible: ["all"],
icon: "minigames16.png",
children: {
match3: {
visible: ["all"],
path: "/minigames/match3"
},
taxi: {
visible: ["all"],
path: "/minigames/taxi"
}
}
},
settings: {
visible: ["all"],
icon: "settings16.png",
children: {
homepage: {
visible: ["all"],
path: "/settings/homepage"
},
account: {
visible: ["all"],
path: "/settings/account"
},
personal: {
visible: ["all"],
path: "/settings/personal"
},
view: {
visible: ["all"],
path: "/settings/view"
},
interests: {
visible: ["all"],
path: "/settings/interests"
},
flirt: {
visible: ["over14"],
path: "/settings/flirt"
},
sexuality: {
visible: ["over14"],
path: "/settings/sexuality"
},
notifications: {
visible: ["all"],
path: "/settings/notifications"
}
}
},
administration: {
visible: ["anyadmin"],
children: {
contactrequests: {
visible: ["mainadmin", "contactrequests"],
path: "/admin/contacts"
},
users: {
visible: ["mainadmin", "useradministration"],
children: {
userlist: {
visible: ["mainadmin", "useradministration"],
path: "/admin/users"
},
userstatistics: {
visible: ["mainadmin"],
path: "/admin/users/statistics"
},
userrights: {
visible: ["mainadmin", "rights"],
path: "/admin/rights"
}
}
},
forum: {
visible: ["mainadmin", "forum"],
path: "/admin/forum"
},
chatrooms: {
visible: ["mainadmin", "chatrooms"],
path: "/admin/chatrooms"
},
servicesStatus: {
visible: ["mainadmin"],
path: "/admin/services/status"
},
interests: {
visible: ["mainadmin", "interests"],
path: "/admin/interests"
},
falukant: {
visible: ["mainadmin", "falukant"],
children: {
logentries: {
visible: ["mainadmin", "falukant"],
path: "/admin/falukant/logentries"
},
edituser: {
visible: ["mainadmin", "falukant"],
path: "/admin/falukant/edituser"
},
database: {
visible: ["mainadmin", "falukant"],
path: "/admin/falukant/database"
},
mapEditor: {
visible: ["mainadmin", "falukant"],
path: "/admin/falukant/map"
},
}
},
minigames: {
visible: ["mainadmin", "match3"],
children: {
match3: {
visible: ["mainadmin", "match3"],
path: "/admin/minigames/match3"
},
taxiTools: {
visible: ["mainadmin", "taxi"],
path: "/admin/minigames/taxi-tools"
}
}
}
}
}
};
class NavigationController {
constructor() {
this.menu = this.menu.bind(this);
this.vocabService = new VocabService();
}
calculateAge(birthDate) {
const today = new Date();
const birthDateObj = new Date(birthDate);
let age = today.getFullYear() - birthDateObj.getFullYear();
const monthDiff = today.getMonth() - birthDateObj.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDateObj.getDate())) {
age--;
}
return age;
}
async filterMenu(menu, rights, age, userId) {
const filteredMenu = {};
try {
const hasFalukantAccount = await this.hasFalukantAccount(userId);
for (const [key, value] of Object.entries(menu)) {
if (value.visible.includes("all")
|| value.visible.some(v => rights.includes(v)) || (value.visible.includes("anyadmin") && rights.length > 0)
|| (value.visible.includes("over14") && age >= 14)
|| (value.visible.includes("over12") && age >= 12)
|| (value.visible.includes("over18") && age >= 18)
|| (value.visible.includes('nofalukantaccount') && !hasFalukantAccount)
|| (value.visible.includes('hasfalukantaccount') && hasFalukantAccount)) {
const { visible, ...itemWithoutVisible } = value;
filteredMenu[key] = { ...itemWithoutVisible };
if (value.children) {
filteredMenu[key].children = await this.filterMenu(value.children, rights, age, userId);
}
}
}
} catch (error) {
console.error('Error filtering menu:', error);
// Return empty menu if filtering fails
return {};
}
return filteredMenu;
}
async menu(req, res) {
try {
const { userid } = req.params;
const user = await User.findOne({ where: { hashedId: userid } });
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const userRights = await UserRight.findAll({
where: { userId: user.id },
include: [{
model: UserRightType,
as: 'rightType',
required: false
}]
});
const userBirthdateParams = await UserParam.findAll({
where: { userId: user.id },
include: [
{
model: UserParamType,
as: 'paramType',
where: { description: 'birthdate' }
}
]
});
const birthDate = userBirthdateParams.length > 0 ? userBirthdateParams[0].value : (new Date()).toDateString();
const age = this.calculateAge(birthDate);
const rights = userRights.map(ur => ur.rightType?.title).filter(Boolean);
const filteredMenu = await this.filterMenu(menuStructure, rights, age, user.id);
// Dynamisches Submenü: Treffpunkt → Vokabeltrainer → (Neue Sprache + abonnierte/angelegte)
// Wichtig: "Neue Sprache" soll IMMER sichtbar sein auch wenn die DB-Abfrage (noch) fehlschlägt.
if (filteredMenu?.socialnetwork?.children?.vocabtrainer) {
const children = {
newLanguage: { path: '/socialnetwork/vocab/new' },
};
try {
const langs = await this.vocabService.listLanguagesForMenu(user.id);
for (const l of langs) {
children[`lang_${l.id}`] = { path: `/socialnetwork/vocab/${l.id}`, label: l.name };
}
} catch (e) {
console.warn('[menu] Konnte Vokabeltrainer-Sprachen nicht laden:', e?.message || e);
}
filteredMenu.socialnetwork.children.vocabtrainer.children = children;
}
res.status(200).json(filteredMenu);
} catch (error) {
console.error('Error fetching menu:', error);
// Return a minimal working menu instead of 500 error
res.status(200).json({
home: { path: "/", icon: "logo_mono.png" },
settings: { path: "/settings", icon: "settings16.png", children: {} }
});
}
}
async hasFalukantAccount(userId) {
try {
const falukantUser = await FalukantUser.findOne({ where: { userId: userId } });
return falukantUser !== null;
} catch (error) {
console.error('Error checking falukant account:', error);
return false; // Fallback: assume no falukant account if error occurs
}
}
}
export default NavigationController;