feat: dochazka Excel export + auto-reload + 162 filter

- Add dochazka Excel export using direct ZIP/XML manipulation of template
  (preserves styles.xml byte-for-byte to avoid Excel "repaired styles" warning)
- Calculate per-person stravné doplatek, transport (AUV), and indiv1
  (closure count + Janouš internet) per sichtovnice.py logic
- Filter exported people to TEMPLATE_NAMES (12 fixed template rows)
- Add server version polling + auto-reload on deploy
- Add FPD check modal for monthly hour validation
- Add "162" filter button to hide first 5 TKB people from view

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Docker Config Backup
2026-04-13 14:39:04 +02:00
parent 3cd05ce0e2
commit db56403f7c
13 changed files with 1079 additions and 155 deletions

View File

@@ -9,7 +9,8 @@ const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const app = express()
const PORT = 3080
const PORT = process.env.PORT || 3090
const SERVER_VERSION = Date.now().toString()
const SCHEDULES_DIR = join(__dirname, 'schedules')
const SAVED_SCHEDULE_PATH = join(__dirname, 'saved_schedule.json')
@@ -198,6 +199,55 @@ app.get('/api/files/:id/export-excel', (_req, res) => {
res.status(501).json({ error: 'Excel export not yet implemented for TKB format' })
})
// Export docházka Excel for a given month
app.get('/api/files/:id/export-dochazka', async (req, res) => {
const { id } = req.params
const month = parseInt(req.query.month) || (new Date().getMonth() + 1)
const year = parseInt(req.query.year) || new Date().getFullYear()
const filePath = join(SCHEDULES_DIR, `${id}.json`)
if (!existsSync(filePath)) return res.status(404).json({ error: 'File not found' })
let fileData
try { fileData = JSON.parse(readFileSync(filePath, 'utf8')) }
catch { return res.status(500).json({ error: 'Failed to read file' }) }
const input = JSON.stringify({ schedule: fileData.data, month, year })
const scriptPath = join(__dirname, 'export_dochazka.py')
try {
const { spawn } = await import('child_process')
await new Promise((resolve, reject) => {
const chunks = []
const errChunks = []
const proc = spawn('python3', [scriptPath])
proc.stdout.on('data', d => chunks.push(d))
proc.stderr.on('data', d => errChunks.push(d))
proc.on('close', code => {
if (code !== 0) {
const errMsg = Buffer.concat(errChunks).toString()
console.error('export_dochazka error:', errMsg)
reject(new Error(errMsg))
} else {
const xlsx = Buffer.concat(chunks)
const MONTH_NAMES = ['','Leden','Únor','Březen','Duben','Květen','Červen','Červenec','Srpen','Září','Říjen','Listopad','Prosinec']
const mm = String(month).padStart(2, '0')
const fname = `OP2416101755_TKB_${year}_${mm}_${MONTH_NAMES[month]}.xlsx`
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(fname)}`)
res.send(xlsx)
resolve()
}
})
proc.on('error', reject)
proc.stdin.write(input)
proc.stdin.end()
})
} catch (err) {
if (!res.headersSent) res.status(500).json({ error: 'Export failed', details: err.message })
}
})
// Create new file
app.post('/api/files', (req, res) => {
try {
@@ -372,6 +422,11 @@ app.get('/api/export-excel', (_req, res) => {
res.status(501).json({ error: 'Excel export not yet implemented for TKB format' })
})
// Version endpoint — clients poll this and reload when version changes
app.get('/api/version', (_req, res) => {
res.json({ version: SERVER_VERSION })
})
// Serve static files from dist/
app.use(express.static(join(__dirname, 'dist'), {
setHeaders: (res) => {