feat(vocab): add dashboard learning summary and related endpoints
All checks were successful
Deploy to production / deploy (push) Successful in 2m52s

- Introduced `getDashboardLearningSummary` method in `VocabService` to provide a compact overview of enrolled courses and current lessons for users.
- Updated `vocabController` to include a new route for the dashboard widget, allowing users to access their learning summary.
- Enhanced `vocabRouter` to route requests for the new dashboard widget endpoint.
- Added localization support for the new dashboard features across multiple languages, improving user engagement and accessibility.
- Updated UI components to integrate the new dashboard widget, ensuring a seamless user experience.
This commit is contained in:
Torsten Schulz (local)
2026-04-02 15:06:50 +02:00
parent 77e6f8d3e8
commit 5fcd55be43
28 changed files with 1095 additions and 39 deletions

View File

@@ -20,6 +20,7 @@ import PoliticalOfficeType from "../../models/falukant/type/political_office_typ
import ChurchOfficeType from "../../models/falukant/type/church_office_type.js";
import ChurchOfficeRequirement from "../../models/falukant/predefine/church_office_requirement.js";
import PoliticalOfficeBenefitType from "../../models/falukant/type/political_office_benefit_type.js";
import PoliticalOfficeBenefit from "../../models/falukant/predefine/political_office_benefit.js";
import PoliticalOfficePrerequisite from "../../models/falukant/predefine/political_office_prerequisite.js";
import UndergroundType from "../../models/falukant/type/underground.js";
import WeatherType from "../../models/falukant/type/weather.js";
@@ -51,6 +52,7 @@ export const initializeFalukantTypes = async () => {
await initializePoliticalOfficeBenefitTypes();
await initializePoliticalOfficeTypes();
await initializePoliticalOfficePrerequisites();
await initializePoliticalOfficeBenefits();
await initializeChurchOfficeTypes();
await initializeChurchOfficePrerequisites();
await initializeUndergroundTypes();
@@ -352,6 +354,7 @@ const vehicleTypes = [
const politicalOfficeBenefitTypes = [
{ tr: 'salary' },
{ tr: 'daily_salary' },
{ tr: 'reputation' },
{ tr: 'influence' },
{ tr: 'access_level' },
@@ -1038,6 +1041,62 @@ export const initializePoliticalOfficePrerequisites = async () => {
console.log(`[Falukant] OfficePrereq neu=${created} exist=${existing}${skipped?` skip=${skipped}`:''}`);
};
/** Amtsvorteile für Politik-UI und Tageshonorar (Testsysteme / frische DBs). Produktion alternativ: backend/sql/falukant_political_office_benefits.sql */
export const initializePoliticalOfficeBenefits = async () => {
const taxType = await PoliticalOfficeBenefitType.findOne({ where: { tr: 'tax_exemption' } });
const dailyType = await PoliticalOfficeBenefitType.findOne({ where: { tr: 'daily_salary' } });
if (!dailyType) {
if (falukantDebug) console.warn('[Falukant] daily_salary Benefit-Typ fehlt, überspringe Office-Benefits');
return;
}
const taxByOffice = [
{ officeTr: 'council', regions: ['city'] },
{ officeTr: 'taxman', regions: ['city', 'county'] },
{ officeTr: 'treasurer', regions: ['city', 'county', 'shire'] },
{
officeTr: 'super-state-administrator',
regions: ['city', 'county', 'shire', 'markgrave', 'duchy']
},
{ officeTr: 'chancellor', regions: ['*'] }
];
let taxCreated = 0;
if (taxType) {
for (const { officeTr, regions } of taxByOffice) {
const office = await PoliticalOfficeType.findOne({ where: { name: officeTr } });
if (!office) continue;
const [, wasCreated] = await PoliticalOfficeBenefit.findOrCreate({
where: { officeTypeId: office.id, benefitTypeId: taxType.id },
defaults: {
officeTypeId: office.id,
benefitTypeId: taxType.id,
value: { regions }
}
});
if (wasCreated) taxCreated += 1;
}
}
const allOffices = await PoliticalOfficeType.findAll({ attributes: ['id'] });
let dailyCreated = 0;
for (const office of allOffices) {
const [, wasCreated] = await PoliticalOfficeBenefit.findOrCreate({
where: { officeTypeId: office.id, benefitTypeId: dailyType.id },
defaults: {
officeTypeId: office.id,
benefitTypeId: dailyType.id,
value: { base: 4, perRank: 11 }
}
});
if (wasCreated) dailyCreated += 1;
}
console.log(
`[Falukant] PoliticalOfficeBenefits: Steuer neu=${taxCreated}, Tageslohn neu=${dailyCreated} (gesamt Ämter=${allOffices.length})`
);
};
// — Church Offices —
const churchOffices = [

View File

@@ -10,7 +10,8 @@ const DEFAULT_WIDGET_TYPES = [
{ label: 'News', endpoint: '/api/news?language=de&category=top', description: 'Nachrichten (newsdata.io), Counter für Pagination', orderId: 3 },
{ label: 'Geburtstage', endpoint: '/api/calendar/widget/birthdays', description: 'Nächste Geburtstage von Freunden', orderId: 4 },
{ label: 'Nächste Termine', endpoint: '/api/calendar/widget/upcoming', description: 'Anstehende Kalendertermine', orderId: 5 },
{ label: 'Kalender', endpoint: '/api/calendar/widget/mini', description: 'Mini-Kalenderansicht', orderId: 6 }
{ label: 'Kalender', endpoint: '/api/calendar/widget/mini', description: 'Mini-Kalenderansicht', orderId: 6 },
{ label: 'Sprachkurse', endpoint: '/api/vocab/dashboard-widget', description: 'Vokabelkurse, aktuelle Lektion, Sprung zur Lektion', orderId: 7 }
];
/**