initial
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/login', (req, res) => {
|
||||||
|
if (req.session.authenticated) return res.redirect('/');
|
||||||
|
res.render('login', { error: null, title: 'Anmelden' });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/login', (req, res) => {
|
||||||
|
const { username, password } = req.body;
|
||||||
|
const validUser = process.env.APP_USERNAME || 'admin';
|
||||||
|
const validPass = process.env.APP_PASSWORD || 'changeme';
|
||||||
|
|
||||||
|
if (username === validUser && password === validPass) {
|
||||||
|
req.session.authenticated = true;
|
||||||
|
req.session.username = username;
|
||||||
|
return res.redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
res.render('login', {
|
||||||
|
error: 'Benutzername oder Passwort ist falsch.',
|
||||||
|
title: 'Anmelden',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/logout', (req, res) => {
|
||||||
|
req.session.destroy(() => {
|
||||||
|
res.redirect('/login');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const { requireAuth } = require('../middleware/auth');
|
||||||
|
const TimeEntry = require('../models/TimeEntry');
|
||||||
|
const Settings = require('../models/Settings');
|
||||||
|
const {
|
||||||
|
computeWorkedMinutes,
|
||||||
|
computeBalanceMinutes,
|
||||||
|
formatBalance,
|
||||||
|
minutesToHM,
|
||||||
|
formatDateDisplay,
|
||||||
|
formatMonthDisplay,
|
||||||
|
} = require('../utils/time');
|
||||||
|
|
||||||
|
router.get('/', requireAuth, async (req, res) => {
|
||||||
|
const settings = await Settings.getSingleton();
|
||||||
|
const entries = await TimeEntry.find().sort({ date: -1 }).limit(90).lean();
|
||||||
|
|
||||||
|
const totalAgg = await TimeEntry.aggregate([
|
||||||
|
{ $group: { _id: null, total: { $sum: '$balanceMinutes' } } },
|
||||||
|
]);
|
||||||
|
const totalBalanceMinutes = settings.startingBalanceMinutes + (totalAgg[0]?.total || 0);
|
||||||
|
|
||||||
|
const monthlyAgg = await TimeEntry.aggregate([
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: { $substrCP: ['$date', 0, 7] },
|
||||||
|
balance: { $sum: '$balanceMinutes' },
|
||||||
|
worked: { $sum: '$workedMinutes' },
|
||||||
|
days: { $sum: 1 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ $sort: { _id: -1 } },
|
||||||
|
{ $limit: 12 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
let editEntry = null;
|
||||||
|
if (req.query.edit) {
|
||||||
|
editEntry = await TimeEntry.findOne({ date: req.query.edit }).lean();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.render('dashboard', {
|
||||||
|
title: 'Übersicht',
|
||||||
|
entries,
|
||||||
|
settings,
|
||||||
|
totalBalanceMinutes,
|
||||||
|
monthlyAgg,
|
||||||
|
editEntry,
|
||||||
|
formatBalance,
|
||||||
|
minutesToHM,
|
||||||
|
formatDateDisplay,
|
||||||
|
formatMonthDisplay,
|
||||||
|
error: req.query.error || null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/entries', requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { date, startTime, endTime, breakMinutes, note } = req.body;
|
||||||
|
|
||||||
|
if (!date || !startTime || !endTime) {
|
||||||
|
return res.redirect('/?error=Bitte+Datum%2C+Start-+und+Endzeit+angeben.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const workedMinutes = computeWorkedMinutes(startTime, endTime, breakMinutes);
|
||||||
|
|
||||||
|
if (workedMinutes < 0) {
|
||||||
|
return res.redirect('/?error=Die+Endzeit+muss+nach+der+Startzeit+liegen.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = await Settings.getSingleton();
|
||||||
|
const balanceMinutes = computeBalanceMinutes(workedMinutes, date, settings);
|
||||||
|
|
||||||
|
await TimeEntry.findOneAndUpdate(
|
||||||
|
{ date },
|
||||||
|
{
|
||||||
|
date,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
breakMinutes: Number(breakMinutes || 0),
|
||||||
|
note: note || '',
|
||||||
|
workedMinutes,
|
||||||
|
balanceMinutes,
|
||||||
|
},
|
||||||
|
{ upsert: true, new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.redirect('/');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.redirect('/?error=Beim+Speichern+ist+ein+Fehler+aufgetreten.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/entries/:id/delete', requireAuth, async (req, res) => {
|
||||||
|
await TimeEntry.findByIdAndDelete(req.params.id);
|
||||||
|
res.redirect('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const { requireAuth } = require('../middleware/auth');
|
||||||
|
const Settings = require('../models/Settings');
|
||||||
|
const { WEEKDAY_NAMES } = require('../utils/time');
|
||||||
|
|
||||||
|
router.get('/settings', requireAuth, async (req, res) => {
|
||||||
|
const settings = await Settings.getSingleton();
|
||||||
|
res.render('settings', {
|
||||||
|
title: 'Einstellungen',
|
||||||
|
settings,
|
||||||
|
weekdayNames: WEEKDAY_NAMES,
|
||||||
|
saved: req.query.saved === '1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/settings', requireAuth, async (req, res) => {
|
||||||
|
const { targetHoursPerDay, startingBalanceHours, workDays } = req.body;
|
||||||
|
|
||||||
|
const days = Array.isArray(workDays)
|
||||||
|
? workDays.map(Number)
|
||||||
|
: workDays
|
||||||
|
? [Number(workDays)]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const settings = await Settings.getSingleton();
|
||||||
|
settings.targetHoursPerDay = Number(targetHoursPerDay) || 0;
|
||||||
|
settings.workDays = days;
|
||||||
|
settings.startingBalanceMinutes = Math.round(Number(startingBalanceHours || 0) * 60);
|
||||||
|
await settings.save();
|
||||||
|
|
||||||
|
res.redirect('/settings?saved=1');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
Reference in New Issue
Block a user