Funktionen: Terminplaner als zweite Funktion (Team-Route, Kundenansicht)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-03 08:17:43 +00:00
1 parent ae1886c030
commit 9e8fd33713
12 files changed
+434 -33

No files matched your search

+29 -12
View File
@@ -2,10 +2,19 @@ import { Router } from 'express'
import { config } from '../../config.js'
import { getDocument } from '../../services/appwriteAdmin.js'
import { withLocks } from '../../services/keyedLock.js'
import { adminActivate, assertOwnProject, disable, getFeatureDoc, listCustomerFeatures } from '../../services/siteFeatures.js'
import {
FEATURE_KEYS,
FEATURE_LABELS,
adminActivate,
assertOwnProject,
disable,
getFeatureDoc,
listCustomerFeatureDocs,
listCustomerFeatures,
} from '../../services/siteFeatures.js'
/**
* Funktionen (News-Pinnwand) je Kundenprojekt im Ticketsystem (Kunden-Reiter "Funktionen").
* Funktionen (News-Pinnwand, Terminplaner) je Kundenprojekt im Ticketsystem (Kunden-Reiter "Funktionen").
* Eingehaengt in routes/team/index.js unter /api/team/funktionen, dort hinter requireStaffJwt.
*/
export const funktionenAdminRouter = Router()
@@ -37,13 +46,17 @@ async function ticketExists(ticketId) {
}
}
// Die Funktionsdokumente des Kunden werden einmal gelesen und fuer alle Projekte und Funktionen verwendet
async function adminView(customerId) {
const projects = await listCustomerFeatures(customerId)
const docs = await listCustomerFeatureDocs(customerId)
const projects = await listCustomerFeatures(customerId, { docs })
for (const p of projects) {
const news = p.features.news
const doc = await getFeatureDoc(customerId, p.projectId, 'news')
news.adminNote = doc?.adminNote || ''
news.ticketMissing = news.state === 'setup' && !(doc?.ticketId && (await ticketExists(doc.ticketId)))
for (const key of FEATURE_KEYS) {
const view = p.features[key]
const doc = docs.find((d) => d.projectId === p.projectId && d.featureKey === key)
view.adminNote = doc?.adminNote || ''
view.ticketMissing = view.state === 'setup' && !(doc?.ticketId && (await ticketExists(doc.ticketId)))
}
}
return projects
}
@@ -56,9 +69,11 @@ funktionenAdminRouter.get('/:customerId', async (req, res) => {
}
})
funktionenAdminRouter.post('/:customerId/news', async (req, res) => {
// Aktivieren (ohne Zahlung) und Abschalten je Funktion: /news wie bisher, /termine neu; jeder andere Schluessel -> 404
funktionenAdminRouter.post('/:customerId/:key', async (req, res) => {
try {
const { customerId } = req.params
const { customerId, key } = req.params
if (!FEATURE_KEYS.includes(key)) return res.status(404).json({ error: 'Unbekannte Funktion' })
const { projectId, action } = req.body || {}
const note = typeof req.body?.note === 'string' ? req.body.note.trim().slice(0, 120) : ''
if (action !== 'activate' && action !== 'disable') {
@@ -70,12 +85,14 @@ funktionenAdminRouter.post('/:customerId/news', async (req, res) => {
await assertOwnProject(customerId, projectId)
const by = adminName(req)
await withLocks([`feature-admin:${projectId}`], async () => {
const doc = await getFeatureDoc(customerId, projectId, 'news')
const doc = await getFeatureDoc(customerId, projectId, key)
const adminNote = composeAdminNote(doc?.adminNote, { by, action, note })
if (action === 'activate') {
await adminActivate(customerId, projectId, 'news', { by, adminNote })
await adminActivate(customerId, projectId, key, { by, adminNote })
} else {
if (!doc) throw Object.assign(new Error('Für dieses Projekt gibt es noch keine News-Pinnwand'), { status: 409 })
if (!doc) {
throw Object.assign(new Error(`Für dieses Projekt gibt es noch ${FEATURE_LABELS[key].keine}`), { status: 409 })
}
await disable(doc.$id, { by, adminNote })
}
})