mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 19:44:12 +00:00
feat: add Workflows Collection for complex process management
Hybrid solution for process visualization: ## Timeline Collection (Simple Processes) Enhanced with process-specific fields for simple linear flows: - Step number, duration, responsible person - Action required indicator (customer/internal/both/automatic) - Deliverables/documents per step Ideal for: Onboarding, Bewerbungsprozess, simple customer journeys ## Workflows Collection (Complex Processes) New dedicated collection for multi-phase workflows with: **Phases:** - Named sections with icons, colors, estimated duration - Responsible person/role assignment - Phase-level deliverables **Steps:** - Multiple types: task, decision, milestone, approval, wait, automatic - Priority levels: critical, high, normal, low, optional - Dependencies between steps (blocking, parallel) - Conditions/branches for decision steps - Checklists with required/optional items - Resources (documents, templates, links, tools) - Outputs per step **Properties:** - Workflow types: project, business, approval, onboarding, support, development, marketing - Complexity levels, iterative flag, parallel phases flag - Display options: vertical, horizontal, flowchart, kanban, gantt layouts **API Features:** - Public endpoint at /api/workflows with tenant isolation - Filter by type, complexity - Statistics: phase count, step count, checklist count, step type breakdown Database: 20 new tables (18 for workflows, 2 for timeline process fields) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3f61050fb3
commit
5df2139bbd
8 changed files with 25902 additions and 3 deletions
364
src/app/(frontend)/api/workflows/route.ts
Normal file
364
src/app/(frontend)/api/workflows/route.ts
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
// src/app/(frontend)/api/workflows/route.ts
|
||||
// Dedizierte Workflows-API für Frontend-Anwendungen
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getPayload } from 'payload'
|
||||
import config from '@payload-config'
|
||||
import type { Media } from '@/payload-types'
|
||||
import {
|
||||
searchLimiter,
|
||||
rateLimitHeaders,
|
||||
getClientIpFromRequest,
|
||||
isIpBlocked,
|
||||
} from '@/lib/security'
|
||||
|
||||
// Validation constants
|
||||
const WORKFLOW_RATE_LIMIT = 30
|
||||
|
||||
// Valid workflow types
|
||||
const WORKFLOW_TYPES = [
|
||||
'project',
|
||||
'business',
|
||||
'approval',
|
||||
'onboarding',
|
||||
'support',
|
||||
'development',
|
||||
'marketing',
|
||||
'other',
|
||||
] as const
|
||||
type WorkflowType = (typeof WORKFLOW_TYPES)[number]
|
||||
|
||||
// Step types for filtering
|
||||
const STEP_TYPES = ['task', 'decision', 'milestone', 'approval', 'wait', 'automatic'] as const
|
||||
|
||||
interface WorkflowStep {
|
||||
name: string
|
||||
description?: unknown
|
||||
shortDescription?: string
|
||||
stepType?: string
|
||||
priority?: string
|
||||
estimatedDuration?: string
|
||||
responsible?: string
|
||||
icon?: string
|
||||
dependencies?: {
|
||||
dependsOnSteps?: string
|
||||
canRunParallel?: boolean
|
||||
isBlocking?: boolean
|
||||
}
|
||||
conditions?: Array<{
|
||||
condition: string
|
||||
nextStep?: string
|
||||
color?: string
|
||||
}>
|
||||
checklist?: Array<{
|
||||
item: string
|
||||
isRequired?: boolean
|
||||
}>
|
||||
resources?: Array<{
|
||||
name: string
|
||||
type?: string
|
||||
file?: Media | number
|
||||
url?: string
|
||||
description?: string
|
||||
}>
|
||||
outputs?: Array<{
|
||||
name: string
|
||||
description?: string
|
||||
}>
|
||||
metadata?: unknown
|
||||
}
|
||||
|
||||
interface WorkflowPhase {
|
||||
name: string
|
||||
description?: string
|
||||
icon?: string
|
||||
color?: string
|
||||
estimatedDuration?: string
|
||||
responsible?: string
|
||||
steps: WorkflowStep[]
|
||||
deliverables?: Array<{
|
||||
name: string
|
||||
description?: string
|
||||
}>
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// IP-Blocklist prüfen
|
||||
const ip = getClientIpFromRequest(request)
|
||||
if (isIpBlocked(ip)) {
|
||||
return NextResponse.json({ error: 'Access denied' }, { status: 403 })
|
||||
}
|
||||
|
||||
// Rate limiting
|
||||
const rateLimit = await searchLimiter.check(ip)
|
||||
if (!rateLimit.allowed) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Too many requests. Please try again later.' },
|
||||
{ status: 429, headers: rateLimitHeaders(rateLimit, WORKFLOW_RATE_LIMIT) }
|
||||
)
|
||||
}
|
||||
|
||||
// Parse query parameters
|
||||
const { searchParams } = new URL(request.url)
|
||||
|
||||
const tenantParam = searchParams.get('tenant')
|
||||
const slugParam = searchParams.get('slug')?.trim()
|
||||
const typeParam = searchParams.get('type')?.trim()
|
||||
const localeParam = searchParams.get('locale')?.trim()
|
||||
const complexityParam = searchParams.get('complexity')?.trim()
|
||||
|
||||
// Validate tenant - REQUIRED for tenant isolation
|
||||
if (!tenantParam) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Tenant ID is required. Use ?tenant=<id> to specify the tenant.' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
const tenantId = parseInt(tenantParam, 10)
|
||||
if (isNaN(tenantId) || tenantId < 1) {
|
||||
return NextResponse.json({ error: 'Invalid tenant ID' }, { status: 400 })
|
||||
}
|
||||
|
||||
// Validate locale
|
||||
const validLocales = ['de', 'en']
|
||||
const locale = localeParam && validLocales.includes(localeParam) ? localeParam : 'de'
|
||||
|
||||
// Validate type if provided
|
||||
if (typeParam && !WORKFLOW_TYPES.includes(typeParam as WorkflowType)) {
|
||||
return NextResponse.json(
|
||||
{ error: `Invalid type. Must be one of: ${WORKFLOW_TYPES.join(', ')}` },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Build where clause
|
||||
const where: Record<string, unknown> = {
|
||||
status: { equals: 'published' },
|
||||
tenant: { equals: tenantId },
|
||||
}
|
||||
|
||||
// Filter by slug (single workflow)
|
||||
if (slugParam) {
|
||||
where.slug = { equals: slugParam }
|
||||
}
|
||||
|
||||
// Filter by type
|
||||
if (typeParam) {
|
||||
where.type = { equals: typeParam }
|
||||
}
|
||||
|
||||
// Get payload instance
|
||||
const payload = await getPayload({ config })
|
||||
|
||||
// Execute query
|
||||
const result = await payload.find({
|
||||
collection: 'workflows',
|
||||
where,
|
||||
sort: '-updatedAt',
|
||||
limit: slugParam ? 1 : 100, // Single or list
|
||||
locale,
|
||||
depth: 2, // Load media relations
|
||||
})
|
||||
|
||||
if (slugParam && result.docs.length === 0) {
|
||||
return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
// Transform workflows
|
||||
const transformedDocs = result.docs.map((workflow) => {
|
||||
const phases = (workflow.phases || []) as WorkflowPhase[]
|
||||
|
||||
// Filter by complexity if specified
|
||||
if (
|
||||
complexityParam &&
|
||||
(workflow.properties as { complexity?: string })?.complexity !== complexityParam
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Calculate statistics
|
||||
let totalSteps = 0
|
||||
let totalChecklists = 0
|
||||
const stepTypes: Record<string, number> = {}
|
||||
|
||||
// Transform phases
|
||||
const transformedPhases = phases.map((phase, phaseIndex) => {
|
||||
const steps = (phase.steps || []) as WorkflowStep[]
|
||||
|
||||
const transformedSteps = steps.map((step, stepIndex) => {
|
||||
totalSteps++
|
||||
|
||||
// Count step types
|
||||
const stepType = step.stepType || 'task'
|
||||
stepTypes[stepType] = (stepTypes[stepType] || 0) + 1
|
||||
|
||||
// Count checklists
|
||||
totalChecklists += (step.checklist || []).length
|
||||
|
||||
// Transform resources
|
||||
const resources = (step.resources || []).map((resource) => {
|
||||
const file = resource.file as Media | null
|
||||
return {
|
||||
name: resource.name,
|
||||
type: resource.type || 'document',
|
||||
fileUrl: file?.url || null,
|
||||
url: resource.url || null,
|
||||
description: resource.description || null,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
index: stepIndex + 1,
|
||||
globalIndex: totalSteps,
|
||||
name: step.name,
|
||||
description: step.description || null,
|
||||
shortDescription: step.shortDescription || null,
|
||||
stepType: step.stepType || 'task',
|
||||
priority: step.priority || 'normal',
|
||||
estimatedDuration: step.estimatedDuration || null,
|
||||
responsible: step.responsible || null,
|
||||
icon: step.icon || null,
|
||||
dependencies: step.dependencies
|
||||
? {
|
||||
dependsOnSteps: step.dependencies.dependsOnSteps || null,
|
||||
canRunParallel: step.dependencies.canRunParallel || false,
|
||||
isBlocking: step.dependencies.isBlocking !== false,
|
||||
}
|
||||
: null,
|
||||
conditions:
|
||||
step.stepType === 'decision'
|
||||
? (step.conditions || []).map((c) => ({
|
||||
condition: c.condition,
|
||||
nextStep: c.nextStep || null,
|
||||
color: c.color || null,
|
||||
}))
|
||||
: null,
|
||||
checklist: (step.checklist || []).map((item) => ({
|
||||
item: item.item,
|
||||
isRequired: item.isRequired !== false,
|
||||
})),
|
||||
resources: resources.length > 0 ? resources : null,
|
||||
outputs: (step.outputs || []).map((o) => ({
|
||||
name: o.name,
|
||||
description: o.description || null,
|
||||
})),
|
||||
metadata: step.metadata || null,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
index: phaseIndex + 1,
|
||||
name: phase.name,
|
||||
description: phase.description || null,
|
||||
icon: phase.icon || null,
|
||||
color: phase.color || null,
|
||||
estimatedDuration: phase.estimatedDuration || null,
|
||||
responsible: phase.responsible || null,
|
||||
stepCount: transformedSteps.length,
|
||||
steps: transformedSteps,
|
||||
deliverables: (phase.deliverables || []).map((d) => ({
|
||||
name: d.name,
|
||||
description: d.description || null,
|
||||
})),
|
||||
}
|
||||
})
|
||||
|
||||
// Transform global resources
|
||||
const globalResources = (
|
||||
workflow.globalResources as Array<{
|
||||
name: string
|
||||
type?: string
|
||||
file?: Media | number
|
||||
url?: string
|
||||
description?: string
|
||||
}>
|
||||
)?.map((resource) => {
|
||||
const file = resource.file as Media | null
|
||||
return {
|
||||
name: resource.name,
|
||||
type: resource.type || 'document',
|
||||
fileUrl: file?.url || null,
|
||||
url: resource.url || null,
|
||||
description: resource.description || null,
|
||||
}
|
||||
})
|
||||
|
||||
const image = workflow.image as Media | null
|
||||
|
||||
return {
|
||||
id: workflow.id,
|
||||
name: workflow.name,
|
||||
slug: workflow.slug,
|
||||
description: workflow.description || null,
|
||||
shortDescription: workflow.shortDescription || null,
|
||||
type: workflow.type,
|
||||
image: image
|
||||
? {
|
||||
url: image.url,
|
||||
alt: image.alt || workflow.name,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
}
|
||||
: null,
|
||||
properties: workflow.properties || null,
|
||||
displayOptions: workflow.displayOptions || null,
|
||||
phases: transformedPhases,
|
||||
globalResources: globalResources?.length ? globalResources : null,
|
||||
statistics: {
|
||||
phaseCount: transformedPhases.length,
|
||||
totalSteps,
|
||||
totalChecklists,
|
||||
stepTypes,
|
||||
},
|
||||
seo: workflow.seo || null,
|
||||
}
|
||||
})
|
||||
|
||||
// Filter out null entries (from complexity filter)
|
||||
const filteredDocs = transformedDocs.filter(Boolean)
|
||||
|
||||
// Single workflow response
|
||||
if (slugParam) {
|
||||
if (filteredDocs.length === 0) {
|
||||
return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
workflow: filteredDocs[0],
|
||||
locale,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
...rateLimitHeaders(rateLimit, WORKFLOW_RATE_LIMIT),
|
||||
'Cache-Control': 'public, max-age=60, s-maxage=300',
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// List response
|
||||
return NextResponse.json(
|
||||
{
|
||||
docs: filteredDocs,
|
||||
total: filteredDocs.length,
|
||||
filters: {
|
||||
tenant: tenantId,
|
||||
type: typeParam || null,
|
||||
complexity: complexityParam || null,
|
||||
locale,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
...rateLimitHeaders(rateLimit, WORKFLOW_RATE_LIMIT),
|
||||
'Cache-Control': 'public, max-age=60, s-maxage=120',
|
||||
},
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('[Workflow API] Error:', error)
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
|
@ -398,6 +398,81 @@ export const Timelines: CollectionConfig = {
|
|||
},
|
||||
},
|
||||
|
||||
// Prozess-spezifische Felder (für einfache Prozesse wie Onboarding, Bewerbung)
|
||||
{
|
||||
name: 'stepNumber',
|
||||
type: 'number',
|
||||
label: 'Schritt-Nummer',
|
||||
admin: {
|
||||
description: 'Explizite Nummerierung (optional, sonst wird Reihenfolge verwendet)',
|
||||
condition: (data) => data?.type === 'process',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
type: 'text',
|
||||
label: 'Dauer',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'z.B. "2-3 Tage", "1 Woche", "30 Minuten"',
|
||||
condition: (data) => data?.type === 'process',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'responsible',
|
||||
type: 'text',
|
||||
label: 'Verantwortlich',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'Person oder Rolle (z.B. "HR-Team", "Projektleiter")',
|
||||
condition: (data) => data?.type === 'process',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'actionRequired',
|
||||
type: 'select',
|
||||
label: 'Aktion erforderlich von',
|
||||
admin: {
|
||||
description: 'Wer muss in diesem Schritt aktiv werden?',
|
||||
condition: (data) => data?.type === 'process',
|
||||
},
|
||||
options: [
|
||||
{ label: 'Kunde/Bewerber', value: 'customer' },
|
||||
{ label: 'Internes Team', value: 'internal' },
|
||||
{ label: 'Beide Seiten', value: 'both' },
|
||||
{ label: 'Automatisch', value: 'automatic' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'deliverables',
|
||||
type: 'array',
|
||||
label: 'Ergebnisse/Dokumente',
|
||||
admin: {
|
||||
description: 'Was wird in diesem Schritt erstellt oder benötigt?',
|
||||
condition: (data) => data?.type === 'process',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Name',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
defaultValue: 'output',
|
||||
label: 'Typ',
|
||||
options: [
|
||||
{ label: 'Ergebnis (Output)', value: 'output' },
|
||||
{ label: 'Benötigt (Input)', value: 'input' },
|
||||
{ label: 'Dokument', value: 'document' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Icon & Styling
|
||||
{
|
||||
name: 'icon',
|
||||
|
|
|
|||
683
src/collections/Workflows.ts
Normal file
683
src/collections/Workflows.ts
Normal file
|
|
@ -0,0 +1,683 @@
|
|||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
/**
|
||||
* Workflows Collection
|
||||
*
|
||||
* Komplexe Prozess- und Workflow-Darstellungen mit:
|
||||
* - Phasen und Schritten
|
||||
* - Abhängigkeiten zwischen Schritten
|
||||
* - Verzweigungen und Bedingungen
|
||||
* - Status-Tracking
|
||||
* - Checklisten und Dokumente
|
||||
* - Verantwortlichkeiten und Zeitschätzungen
|
||||
*
|
||||
* Ideal für:
|
||||
* - Projektabläufe
|
||||
* - Komplexe Geschäftsprozesse
|
||||
* - Genehmigungs-Workflows
|
||||
* - Produktentwicklungs-Pipelines
|
||||
*
|
||||
* Multi-Tenant-fähig
|
||||
*/
|
||||
export const Workflows: CollectionConfig = {
|
||||
slug: 'workflows',
|
||||
labels: {
|
||||
singular: 'Workflow',
|
||||
plural: 'Workflows',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'name',
|
||||
group: 'Inhalte',
|
||||
defaultColumns: ['name', 'type', 'status', 'updatedAt'],
|
||||
description: 'Komplexe Prozesse und Workflows mit Phasen, Abhängigkeiten und Status-Tracking',
|
||||
},
|
||||
access: {
|
||||
read: () => true, // Öffentlich lesbar
|
||||
},
|
||||
fields: [
|
||||
// Workflow-Metadaten
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Workflow-Name',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'Name des Workflows (z.B. "Projektablauf Webentwicklung")',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
unique: true,
|
||||
label: 'Slug',
|
||||
admin: {
|
||||
description: 'URL-freundlicher Identifier',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'richText',
|
||||
label: 'Beschreibung',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'Ausführliche Beschreibung des Workflows',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'shortDescription',
|
||||
type: 'textarea',
|
||||
label: 'Kurzbeschreibung',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'Kurze Zusammenfassung für Übersichten',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
required: true,
|
||||
defaultValue: 'project',
|
||||
label: 'Workflow-Typ',
|
||||
options: [
|
||||
{ label: 'Projektablauf', value: 'project' },
|
||||
{ label: 'Geschäftsprozess', value: 'business' },
|
||||
{ label: 'Genehmigung', value: 'approval' },
|
||||
{ label: 'Onboarding', value: 'onboarding' },
|
||||
{ label: 'Support/Service', value: 'support' },
|
||||
{ label: 'Entwicklung', value: 'development' },
|
||||
{ label: 'Marketing', value: 'marketing' },
|
||||
{ label: 'Sonstiges', value: 'other' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
type: 'select',
|
||||
required: true,
|
||||
defaultValue: 'draft',
|
||||
label: 'Status',
|
||||
options: [
|
||||
{ label: 'Entwurf', value: 'draft' },
|
||||
{ label: 'Veröffentlicht', value: 'published' },
|
||||
{ label: 'Archiviert', value: 'archived' },
|
||||
],
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
label: 'Vorschaubild',
|
||||
admin: {
|
||||
description: 'Optionales Bild für den Workflow',
|
||||
},
|
||||
},
|
||||
|
||||
// Workflow-Eigenschaften
|
||||
{
|
||||
name: 'properties',
|
||||
type: 'group',
|
||||
label: 'Workflow-Eigenschaften',
|
||||
fields: [
|
||||
{
|
||||
name: 'estimatedDuration',
|
||||
type: 'text',
|
||||
label: 'Geschätzte Gesamtdauer',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'z.B. "4-6 Wochen", "3 Monate"',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'complexity',
|
||||
type: 'select',
|
||||
label: 'Komplexität',
|
||||
options: [
|
||||
{ label: 'Einfach', value: 'simple' },
|
||||
{ label: 'Mittel', value: 'medium' },
|
||||
{ label: 'Komplex', value: 'complex' },
|
||||
{ label: 'Sehr komplex', value: 'very_complex' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'isIterative',
|
||||
type: 'checkbox',
|
||||
label: 'Iterativer Prozess',
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: 'Kann der Workflow wiederholt durchlaufen werden?',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'allowParallelPhases',
|
||||
type: 'checkbox',
|
||||
label: 'Parallele Phasen erlaubt',
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: 'Können mehrere Phasen gleichzeitig aktiv sein?',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Display-Optionen
|
||||
{
|
||||
name: 'displayOptions',
|
||||
type: 'group',
|
||||
label: 'Darstellungsoptionen',
|
||||
fields: [
|
||||
{
|
||||
name: 'layout',
|
||||
type: 'select',
|
||||
defaultValue: 'vertical',
|
||||
label: 'Layout',
|
||||
options: [
|
||||
{ label: 'Vertikal (Schritte untereinander)', value: 'vertical' },
|
||||
{ label: 'Horizontal (Schritte nebeneinander)', value: 'horizontal' },
|
||||
{ label: 'Flowchart (mit Verbindungen)', value: 'flowchart' },
|
||||
{ label: 'Kanban (Spalten)', value: 'kanban' },
|
||||
{ label: 'Gantt (Zeitstrahl)', value: 'gantt' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'showPhaseNumbers',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Phasen-Nummern anzeigen',
|
||||
},
|
||||
{
|
||||
name: 'showStepNumbers',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Schritt-Nummern anzeigen',
|
||||
},
|
||||
{
|
||||
name: 'showDurations',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Zeitangaben anzeigen',
|
||||
},
|
||||
{
|
||||
name: 'showResponsible',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Verantwortliche anzeigen',
|
||||
},
|
||||
{
|
||||
name: 'showProgress',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
label: 'Fortschrittsanzeige',
|
||||
admin: {
|
||||
description: 'Zeigt Fortschrittsbalken basierend auf abgeschlossenen Schritten',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'colorScheme',
|
||||
type: 'select',
|
||||
defaultValue: 'phase',
|
||||
label: 'Farbschema',
|
||||
options: [
|
||||
{ label: 'Nach Phase', value: 'phase' },
|
||||
{ label: 'Nach Status', value: 'status' },
|
||||
{ label: 'Nach Priorität', value: 'priority' },
|
||||
{ label: 'Einheitlich (Brand)', value: 'brand' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Phasen (Hauptabschnitte des Workflows)
|
||||
{
|
||||
name: 'phases',
|
||||
type: 'array',
|
||||
required: true,
|
||||
minRows: 1,
|
||||
label: 'Phasen',
|
||||
admin: {
|
||||
description: 'Hauptabschnitte des Workflows',
|
||||
initCollapsed: false,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Phasen-Name',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
label: 'Beschreibung',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'icon',
|
||||
type: 'text',
|
||||
label: 'Icon',
|
||||
admin: {
|
||||
description: 'Lucide Icon-Name (z.B. "rocket", "settings", "check-circle")',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
type: 'text',
|
||||
label: 'Farbe',
|
||||
admin: {
|
||||
description: 'HEX-Farbe (z.B. "#3B82F6") oder Tailwind-Klasse',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'estimatedDuration',
|
||||
type: 'text',
|
||||
label: 'Geschätzte Dauer',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'z.B. "1 Woche", "2-3 Tage"',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'responsible',
|
||||
type: 'text',
|
||||
label: 'Verantwortlich',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'Rolle oder Person (z.B. "Projektleiter", "Design-Team")',
|
||||
},
|
||||
},
|
||||
|
||||
// Schritte innerhalb der Phase
|
||||
{
|
||||
name: 'steps',
|
||||
type: 'array',
|
||||
required: true,
|
||||
minRows: 1,
|
||||
label: 'Schritte',
|
||||
admin: {
|
||||
description: 'Einzelne Schritte in dieser Phase',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Schritt-Name',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'richText',
|
||||
label: 'Beschreibung',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'shortDescription',
|
||||
type: 'textarea',
|
||||
label: 'Kurzbeschreibung',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'Für kompakte Ansichten',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'stepType',
|
||||
type: 'select',
|
||||
defaultValue: 'task',
|
||||
label: 'Schritt-Typ',
|
||||
options: [
|
||||
{ label: 'Aufgabe', value: 'task' },
|
||||
{ label: 'Entscheidung', value: 'decision' },
|
||||
{ label: 'Meilenstein', value: 'milestone' },
|
||||
{ label: 'Genehmigung', value: 'approval' },
|
||||
{ label: 'Warten/Pause', value: 'wait' },
|
||||
{ label: 'Automatisch', value: 'automatic' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'priority',
|
||||
type: 'select',
|
||||
defaultValue: 'normal',
|
||||
label: 'Priorität',
|
||||
options: [
|
||||
{ label: 'Kritisch', value: 'critical' },
|
||||
{ label: 'Hoch', value: 'high' },
|
||||
{ label: 'Normal', value: 'normal' },
|
||||
{ label: 'Niedrig', value: 'low' },
|
||||
{ label: 'Optional', value: 'optional' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'estimatedDuration',
|
||||
type: 'text',
|
||||
label: 'Geschätzte Dauer',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'z.B. "2 Stunden", "1 Tag"',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'responsible',
|
||||
type: 'text',
|
||||
label: 'Verantwortlich',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'icon',
|
||||
type: 'text',
|
||||
label: 'Icon',
|
||||
},
|
||||
|
||||
// Abhängigkeiten
|
||||
{
|
||||
name: 'dependencies',
|
||||
type: 'group',
|
||||
label: 'Abhängigkeiten',
|
||||
admin: {
|
||||
description: 'Welche Schritte müssen vorher abgeschlossen sein?',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'dependsOnSteps',
|
||||
type: 'text',
|
||||
label: 'Abhängig von Schritten',
|
||||
admin: {
|
||||
description:
|
||||
'IDs oder Namen der Vorgänger-Schritte (komma-separiert). Leer = vorheriger Schritt',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'canRunParallel',
|
||||
type: 'checkbox',
|
||||
label: 'Kann parallel laufen',
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
name: 'isBlocking',
|
||||
type: 'checkbox',
|
||||
label: 'Blockierend',
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description: 'Muss abgeschlossen sein, bevor der nächste Schritt beginnt',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Bedingungen (für Entscheidungen)
|
||||
{
|
||||
name: 'conditions',
|
||||
type: 'array',
|
||||
label: 'Bedingungen/Verzweigungen',
|
||||
admin: {
|
||||
description: 'Für Entscheidungsschritte: Welche Wege gibt es?',
|
||||
condition: (_, siblingData) => siblingData?.stepType === 'decision',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'condition',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Bedingung',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'z.B. "Wenn genehmigt", "Bei Budget > 10.000€"',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'nextStep',
|
||||
type: 'text',
|
||||
label: 'Nächster Schritt',
|
||||
admin: {
|
||||
description: 'ID oder Name des nächsten Schritts',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
type: 'text',
|
||||
label: 'Farbe',
|
||||
admin: {
|
||||
description: 'z.B. "green" für positiv, "red" für negativ',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Checkliste
|
||||
{
|
||||
name: 'checklist',
|
||||
type: 'array',
|
||||
label: 'Checkliste',
|
||||
admin: {
|
||||
description: 'To-Do-Punkte für diesen Schritt',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'item',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Aufgabe',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'isRequired',
|
||||
type: 'checkbox',
|
||||
label: 'Pflicht',
|
||||
defaultValue: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Dokumente und Ressourcen
|
||||
{
|
||||
name: 'resources',
|
||||
type: 'array',
|
||||
label: 'Ressourcen & Dokumente',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Name',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
defaultValue: 'document',
|
||||
label: 'Typ',
|
||||
options: [
|
||||
{ label: 'Dokument', value: 'document' },
|
||||
{ label: 'Template', value: 'template' },
|
||||
{ label: 'Link', value: 'link' },
|
||||
{ label: 'Tool', value: 'tool' },
|
||||
{ label: 'Video', value: 'video' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'file',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
label: 'Datei',
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
['document', 'template'].includes(siblingData?.type),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
type: 'text',
|
||||
label: 'URL',
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
['link', 'tool', 'video'].includes(siblingData?.type),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
label: 'Beschreibung',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Outputs/Ergebnisse
|
||||
{
|
||||
name: 'outputs',
|
||||
type: 'array',
|
||||
label: 'Ergebnisse/Outputs',
|
||||
admin: {
|
||||
description: 'Was wird in diesem Schritt produziert?',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Output',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
label: 'Beschreibung',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Zusätzliche Daten
|
||||
{
|
||||
name: 'metadata',
|
||||
type: 'json',
|
||||
label: 'Zusätzliche Daten (JSON)',
|
||||
admin: {
|
||||
description: 'Für spezielle Anwendungsfälle',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Phase-Level Outputs
|
||||
{
|
||||
name: 'deliverables',
|
||||
type: 'array',
|
||||
label: 'Liefergegenstände der Phase',
|
||||
admin: {
|
||||
description: 'Was wird am Ende dieser Phase abgeliefert?',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Deliverable',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
label: 'Beschreibung',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Globale Ressourcen
|
||||
{
|
||||
name: 'globalResources',
|
||||
type: 'array',
|
||||
label: 'Globale Ressourcen',
|
||||
admin: {
|
||||
description: 'Ressourcen, die für den gesamten Workflow relevant sind',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: 'Name',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
defaultValue: 'document',
|
||||
label: 'Typ',
|
||||
options: [
|
||||
{ label: 'Dokument', value: 'document' },
|
||||
{ label: 'Template', value: 'template' },
|
||||
{ label: 'Checkliste', value: 'checklist' },
|
||||
{ label: 'Link', value: 'link' },
|
||||
{ label: 'Tool', value: 'tool' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'file',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
label: 'Datei',
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
type: 'text',
|
||||
label: 'URL',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
label: 'Beschreibung',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// SEO
|
||||
{
|
||||
name: 'seo',
|
||||
type: 'group',
|
||||
label: 'SEO',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'metaTitle',
|
||||
type: 'text',
|
||||
label: 'Meta-Titel',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'metaDescription',
|
||||
type: 'textarea',
|
||||
label: 'Meta-Beschreibung',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
({ data }) => {
|
||||
// Auto-generate slug from name if not provided
|
||||
if (data && !data.slug && data.name) {
|
||||
data.slug = data.name
|
||||
.toLowerCase()
|
||||
.replace(/[äöüß]/g, (match: string) => {
|
||||
const map: Record<string, string> = { ä: 'ae', ö: 'oe', ü: 'ue', ß: 'ss' }
|
||||
return map[match] || match
|
||||
})
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-|-$/g, '')
|
||||
}
|
||||
return data
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,324 @@
|
|||
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||
|
||||
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
CREATE TYPE "public"."enum_timelines_events_deliverables_type" AS ENUM('output', 'input', 'document');
|
||||
CREATE TYPE "public"."enum_timelines_events_action_required" AS ENUM('customer', 'internal', 'both', 'automatic');
|
||||
CREATE TYPE "public"."enum_workflows_phases_steps_resources_type" AS ENUM('document', 'template', 'link', 'tool', 'video');
|
||||
CREATE TYPE "public"."enum_workflows_phases_steps_step_type" AS ENUM('task', 'decision', 'milestone', 'approval', 'wait', 'automatic');
|
||||
CREATE TYPE "public"."enum_workflows_phases_steps_priority" AS ENUM('critical', 'high', 'normal', 'low', 'optional');
|
||||
CREATE TYPE "public"."enum_workflows_global_resources_type" AS ENUM('document', 'template', 'checklist', 'link', 'tool');
|
||||
CREATE TYPE "public"."enum_workflows_type" AS ENUM('project', 'business', 'approval', 'onboarding', 'support', 'development', 'marketing', 'other');
|
||||
CREATE TYPE "public"."enum_workflows_status" AS ENUM('draft', 'published', 'archived');
|
||||
CREATE TYPE "public"."enum_workflows_properties_complexity" AS ENUM('simple', 'medium', 'complex', 'very_complex');
|
||||
CREATE TYPE "public"."enum_workflows_display_options_layout" AS ENUM('vertical', 'horizontal', 'flowchart', 'kanban', 'gantt');
|
||||
CREATE TYPE "public"."enum_workflows_display_options_color_scheme" AS ENUM('phase', 'status', 'priority', 'brand');
|
||||
CREATE TABLE "timelines_events_deliverables" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" varchar NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"type" "enum_timelines_events_deliverables_type" DEFAULT 'output'
|
||||
);
|
||||
|
||||
CREATE TABLE "timelines_events_deliverables_locales" (
|
||||
"name" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_conditions" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" varchar NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"next_step" varchar,
|
||||
"color" varchar
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_conditions_locales" (
|
||||
"condition" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_checklist" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" varchar NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"is_required" boolean DEFAULT true
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_checklist_locales" (
|
||||
"item" varchar NOT NULL,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_resources" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" varchar NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"type" "enum_workflows_phases_steps_resources_type" DEFAULT 'document',
|
||||
"file_id" integer,
|
||||
"url" varchar
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_resources_locales" (
|
||||
"name" varchar NOT NULL,
|
||||
"description" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_outputs" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" varchar NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_outputs_locales" (
|
||||
"name" varchar NOT NULL,
|
||||
"description" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" varchar NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"step_type" "enum_workflows_phases_steps_step_type" DEFAULT 'task',
|
||||
"priority" "enum_workflows_phases_steps_priority" DEFAULT 'normal',
|
||||
"icon" varchar,
|
||||
"dependencies_depends_on_steps" varchar,
|
||||
"dependencies_can_run_parallel" boolean DEFAULT false,
|
||||
"dependencies_is_blocking" boolean DEFAULT true,
|
||||
"metadata" jsonb
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_steps_locales" (
|
||||
"name" varchar NOT NULL,
|
||||
"description" jsonb,
|
||||
"short_description" varchar,
|
||||
"estimated_duration" varchar,
|
||||
"responsible" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_deliverables" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" varchar NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_deliverables_locales" (
|
||||
"name" varchar NOT NULL,
|
||||
"description" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" integer NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"icon" varchar,
|
||||
"color" varchar
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_phases_locales" (
|
||||
"name" varchar NOT NULL,
|
||||
"description" varchar,
|
||||
"estimated_duration" varchar,
|
||||
"responsible" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_global_resources" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" integer NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"type" "enum_workflows_global_resources_type" DEFAULT 'document',
|
||||
"file_id" integer,
|
||||
"url" varchar
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_global_resources_locales" (
|
||||
"name" varchar NOT NULL,
|
||||
"description" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" varchar NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"tenant_id" integer,
|
||||
"slug" varchar NOT NULL,
|
||||
"type" "enum_workflows_type" DEFAULT 'project' NOT NULL,
|
||||
"status" "enum_workflows_status" DEFAULT 'draft' NOT NULL,
|
||||
"image_id" integer,
|
||||
"properties_complexity" "enum_workflows_properties_complexity",
|
||||
"properties_is_iterative" boolean DEFAULT false,
|
||||
"properties_allow_parallel_phases" boolean DEFAULT false,
|
||||
"display_options_layout" "enum_workflows_display_options_layout" DEFAULT 'vertical',
|
||||
"display_options_show_phase_numbers" boolean DEFAULT true,
|
||||
"display_options_show_step_numbers" boolean DEFAULT true,
|
||||
"display_options_show_durations" boolean DEFAULT true,
|
||||
"display_options_show_responsible" boolean DEFAULT true,
|
||||
"display_options_show_progress" boolean DEFAULT false,
|
||||
"display_options_color_scheme" "enum_workflows_display_options_color_scheme" DEFAULT 'phase',
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "workflows_locales" (
|
||||
"name" varchar NOT NULL,
|
||||
"description" jsonb,
|
||||
"short_description" varchar,
|
||||
"properties_estimated_duration" varchar,
|
||||
"seo_meta_title" varchar,
|
||||
"seo_meta_description" varchar,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "_locales" NOT NULL,
|
||||
"_parent_id" integer NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE "timelines_events" ADD COLUMN "step_number" numeric;
|
||||
ALTER TABLE "timelines_events" ADD COLUMN "action_required" "enum_timelines_events_action_required";
|
||||
ALTER TABLE "timelines_events_locales" ADD COLUMN "duration" varchar;
|
||||
ALTER TABLE "timelines_events_locales" ADD COLUMN "responsible" varchar;
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD COLUMN "workflows_id" integer;
|
||||
ALTER TABLE "timelines_events_deliverables" ADD CONSTRAINT "timelines_events_deliverables_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."timelines_events"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "timelines_events_deliverables_locales" ADD CONSTRAINT "timelines_events_deliverables_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."timelines_events_deliverables"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_conditions" ADD CONSTRAINT "workflows_phases_steps_conditions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_conditions_locales" ADD CONSTRAINT "workflows_phases_steps_conditions_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps_conditions"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_checklist" ADD CONSTRAINT "workflows_phases_steps_checklist_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_checklist_locales" ADD CONSTRAINT "workflows_phases_steps_checklist_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps_checklist"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_resources" ADD CONSTRAINT "workflows_phases_steps_resources_file_id_media_id_fk" FOREIGN KEY ("file_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_resources" ADD CONSTRAINT "workflows_phases_steps_resources_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_resources_locales" ADD CONSTRAINT "workflows_phases_steps_resources_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps_resources"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_outputs" ADD CONSTRAINT "workflows_phases_steps_outputs_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_outputs_locales" ADD CONSTRAINT "workflows_phases_steps_outputs_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps_outputs"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps" ADD CONSTRAINT "workflows_phases_steps_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_steps_locales" ADD CONSTRAINT "workflows_phases_steps_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_steps"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_deliverables" ADD CONSTRAINT "workflows_phases_deliverables_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_deliverables_locales" ADD CONSTRAINT "workflows_phases_deliverables_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases_deliverables"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases" ADD CONSTRAINT "workflows_phases_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_phases_locales" ADD CONSTRAINT "workflows_phases_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_phases"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_global_resources" ADD CONSTRAINT "workflows_global_resources_file_id_media_id_fk" FOREIGN KEY ("file_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||
ALTER TABLE "workflows_global_resources" ADD CONSTRAINT "workflows_global_resources_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows_global_resources_locales" ADD CONSTRAINT "workflows_global_resources_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows_global_resources"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "workflows" ADD CONSTRAINT "workflows_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE set null ON UPDATE no action;
|
||||
ALTER TABLE "workflows" ADD CONSTRAINT "workflows_image_id_media_id_fk" FOREIGN KEY ("image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||
ALTER TABLE "workflows_locales" ADD CONSTRAINT "workflows_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;
|
||||
CREATE INDEX "timelines_events_deliverables_order_idx" ON "timelines_events_deliverables" USING btree ("_order");
|
||||
CREATE INDEX "timelines_events_deliverables_parent_id_idx" ON "timelines_events_deliverables" USING btree ("_parent_id");
|
||||
CREATE UNIQUE INDEX "timelines_events_deliverables_locales_locale_parent_id_uniqu" ON "timelines_events_deliverables_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_phases_steps_conditions_order_idx" ON "workflows_phases_steps_conditions" USING btree ("_order");
|
||||
CREATE INDEX "workflows_phases_steps_conditions_parent_id_idx" ON "workflows_phases_steps_conditions" USING btree ("_parent_id");
|
||||
CREATE UNIQUE INDEX "workflows_phases_steps_conditions_locales_locale_parent_id_u" ON "workflows_phases_steps_conditions_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_phases_steps_checklist_order_idx" ON "workflows_phases_steps_checklist" USING btree ("_order");
|
||||
CREATE INDEX "workflows_phases_steps_checklist_parent_id_idx" ON "workflows_phases_steps_checklist" USING btree ("_parent_id");
|
||||
CREATE UNIQUE INDEX "workflows_phases_steps_checklist_locales_locale_parent_id_un" ON "workflows_phases_steps_checklist_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_phases_steps_resources_order_idx" ON "workflows_phases_steps_resources" USING btree ("_order");
|
||||
CREATE INDEX "workflows_phases_steps_resources_parent_id_idx" ON "workflows_phases_steps_resources" USING btree ("_parent_id");
|
||||
CREATE INDEX "workflows_phases_steps_resources_file_idx" ON "workflows_phases_steps_resources" USING btree ("file_id");
|
||||
CREATE UNIQUE INDEX "workflows_phases_steps_resources_locales_locale_parent_id_un" ON "workflows_phases_steps_resources_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_phases_steps_outputs_order_idx" ON "workflows_phases_steps_outputs" USING btree ("_order");
|
||||
CREATE INDEX "workflows_phases_steps_outputs_parent_id_idx" ON "workflows_phases_steps_outputs" USING btree ("_parent_id");
|
||||
CREATE UNIQUE INDEX "workflows_phases_steps_outputs_locales_locale_parent_id_uniq" ON "workflows_phases_steps_outputs_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_phases_steps_order_idx" ON "workflows_phases_steps" USING btree ("_order");
|
||||
CREATE INDEX "workflows_phases_steps_parent_id_idx" ON "workflows_phases_steps" USING btree ("_parent_id");
|
||||
CREATE UNIQUE INDEX "workflows_phases_steps_locales_locale_parent_id_unique" ON "workflows_phases_steps_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_phases_deliverables_order_idx" ON "workflows_phases_deliverables" USING btree ("_order");
|
||||
CREATE INDEX "workflows_phases_deliverables_parent_id_idx" ON "workflows_phases_deliverables" USING btree ("_parent_id");
|
||||
CREATE UNIQUE INDEX "workflows_phases_deliverables_locales_locale_parent_id_uniqu" ON "workflows_phases_deliverables_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_phases_order_idx" ON "workflows_phases" USING btree ("_order");
|
||||
CREATE INDEX "workflows_phases_parent_id_idx" ON "workflows_phases" USING btree ("_parent_id");
|
||||
CREATE UNIQUE INDEX "workflows_phases_locales_locale_parent_id_unique" ON "workflows_phases_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_global_resources_order_idx" ON "workflows_global_resources" USING btree ("_order");
|
||||
CREATE INDEX "workflows_global_resources_parent_id_idx" ON "workflows_global_resources" USING btree ("_parent_id");
|
||||
CREATE INDEX "workflows_global_resources_file_idx" ON "workflows_global_resources" USING btree ("file_id");
|
||||
CREATE UNIQUE INDEX "workflows_global_resources_locales_locale_parent_id_unique" ON "workflows_global_resources_locales" USING btree ("_locale","_parent_id");
|
||||
CREATE INDEX "workflows_tenant_idx" ON "workflows" USING btree ("tenant_id");
|
||||
CREATE UNIQUE INDEX "workflows_slug_idx" ON "workflows" USING btree ("slug");
|
||||
CREATE INDEX "workflows_image_idx" ON "workflows" USING btree ("image_id");
|
||||
CREATE INDEX "workflows_updated_at_idx" ON "workflows" USING btree ("updated_at");
|
||||
CREATE INDEX "workflows_created_at_idx" ON "workflows" USING btree ("created_at");
|
||||
CREATE UNIQUE INDEX "workflows_locales_locale_parent_id_unique" ON "workflows_locales" USING btree ("_locale","_parent_id");
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_workflows_fk" FOREIGN KEY ("workflows_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;
|
||||
CREATE INDEX "payload_locked_documents_rels_workflows_id_idx" ON "payload_locked_documents_rels" USING btree ("workflows_id");`)
|
||||
}
|
||||
|
||||
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "timelines_events_deliverables" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "timelines_events_deliverables_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_conditions" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_conditions_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_checklist" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_checklist_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_resources" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_resources_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_outputs" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_outputs_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_steps_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_deliverables" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_deliverables_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_phases_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_global_resources" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_global_resources_locales" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows" DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "workflows_locales" DISABLE ROW LEVEL SECURITY;
|
||||
DROP TABLE "timelines_events_deliverables" CASCADE;
|
||||
DROP TABLE "timelines_events_deliverables_locales" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_conditions" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_conditions_locales" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_checklist" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_checklist_locales" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_resources" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_resources_locales" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_outputs" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_outputs_locales" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps" CASCADE;
|
||||
DROP TABLE "workflows_phases_steps_locales" CASCADE;
|
||||
DROP TABLE "workflows_phases_deliverables" CASCADE;
|
||||
DROP TABLE "workflows_phases_deliverables_locales" CASCADE;
|
||||
DROP TABLE "workflows_phases" CASCADE;
|
||||
DROP TABLE "workflows_phases_locales" CASCADE;
|
||||
DROP TABLE "workflows_global_resources" CASCADE;
|
||||
DROP TABLE "workflows_global_resources_locales" CASCADE;
|
||||
DROP TABLE "workflows" CASCADE;
|
||||
DROP TABLE "workflows_locales" CASCADE;
|
||||
ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT "payload_locked_documents_rels_workflows_fk";
|
||||
|
||||
DROP INDEX "payload_locked_documents_rels_workflows_id_idx";
|
||||
ALTER TABLE "timelines_events" DROP COLUMN "step_number";
|
||||
ALTER TABLE "timelines_events" DROP COLUMN "action_required";
|
||||
ALTER TABLE "timelines_events_locales" DROP COLUMN "duration";
|
||||
ALTER TABLE "timelines_events_locales" DROP COLUMN "responsible";
|
||||
ALTER TABLE "payload_locked_documents_rels" DROP COLUMN "workflows_id";
|
||||
DROP TYPE "public"."enum_timelines_events_deliverables_type";
|
||||
DROP TYPE "public"."enum_timelines_events_action_required";
|
||||
DROP TYPE "public"."enum_workflows_phases_steps_resources_type";
|
||||
DROP TYPE "public"."enum_workflows_phases_steps_step_type";
|
||||
DROP TYPE "public"."enum_workflows_phases_steps_priority";
|
||||
DROP TYPE "public"."enum_workflows_global_resources_type";
|
||||
DROP TYPE "public"."enum_workflows_type";
|
||||
DROP TYPE "public"."enum_workflows_status";
|
||||
DROP TYPE "public"."enum_workflows_properties_complexity";
|
||||
DROP TYPE "public"."enum_workflows_display_options_layout";
|
||||
DROP TYPE "public"."enum_workflows_display_options_color_scheme";`)
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import * as migration_20251210_073811_add_services_collections from './20251210_
|
|||
import * as migration_20251210_090000_enhance_form_submissions from './20251210_090000_enhance_form_submissions';
|
||||
import * as migration_20251212_211506_add_products_collections from './20251212_211506_add_products_collections';
|
||||
import * as migration_20251213_100753_add_timelines_collection from './20251213_100753_add_timelines_collection';
|
||||
import * as migration_20251213_104523_add_workflows_and_timeline_process_fields from './20251213_104523_add_workflows_and_timeline_process_fields';
|
||||
|
||||
export const migrations = [
|
||||
{
|
||||
|
|
@ -70,6 +71,11 @@ export const migrations = [
|
|||
{
|
||||
up: migration_20251213_100753_add_timelines_collection.up,
|
||||
down: migration_20251213_100753_add_timelines_collection.down,
|
||||
name: '20251213_100753_add_timelines_collection'
|
||||
name: '20251213_100753_add_timelines_collection',
|
||||
},
|
||||
{
|
||||
up: migration_20251213_104523_add_workflows_and_timeline_process_fields.up,
|
||||
down: migration_20251213_104523_add_workflows_and_timeline_process_fields.down,
|
||||
name: '20251213_104523_add_workflows_and_timeline_process_fields'
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ export interface Config {
|
|||
'product-categories': ProductCategory;
|
||||
products: Product;
|
||||
timelines: Timeline;
|
||||
workflows: Workflow;
|
||||
'cookie-configurations': CookieConfiguration;
|
||||
'cookie-inventory': CookieInventory;
|
||||
'consent-logs': ConsentLog;
|
||||
|
|
@ -119,6 +120,7 @@ export interface Config {
|
|||
'product-categories': ProductCategoriesSelect<false> | ProductCategoriesSelect<true>;
|
||||
products: ProductsSelect<false> | ProductsSelect<true>;
|
||||
timelines: TimelinesSelect<false> | TimelinesSelect<true>;
|
||||
workflows: WorkflowsSelect<false> | WorkflowsSelect<true>;
|
||||
'cookie-configurations': CookieConfigurationsSelect<false> | CookieConfigurationsSelect<true>;
|
||||
'cookie-inventory': CookieInventorySelect<false> | CookieInventorySelect<true>;
|
||||
'consent-logs': ConsentLogsSelect<false> | ConsentLogsSelect<true>;
|
||||
|
|
@ -1897,6 +1899,32 @@ export interface Timeline {
|
|||
* Highlights werden visuell hervorgehoben
|
||||
*/
|
||||
importance?: ('highlight' | 'normal' | 'minor') | null;
|
||||
/**
|
||||
* Explizite Nummerierung (optional, sonst wird Reihenfolge verwendet)
|
||||
*/
|
||||
stepNumber?: number | null;
|
||||
/**
|
||||
* z.B. "2-3 Tage", "1 Woche", "30 Minuten"
|
||||
*/
|
||||
duration?: string | null;
|
||||
/**
|
||||
* Person oder Rolle (z.B. "HR-Team", "Projektleiter")
|
||||
*/
|
||||
responsible?: string | null;
|
||||
/**
|
||||
* Wer muss in diesem Schritt aktiv werden?
|
||||
*/
|
||||
actionRequired?: ('customer' | 'internal' | 'both' | 'automatic') | null;
|
||||
/**
|
||||
* Was wird in diesem Schritt erstellt oder benötigt?
|
||||
*/
|
||||
deliverables?:
|
||||
| {
|
||||
name: string;
|
||||
type?: ('output' | 'input' | 'document') | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
* Emoji oder Lucide Icon-Name (z.B. "rocket", "award", "users")
|
||||
*/
|
||||
|
|
@ -1934,6 +1962,242 @@ export interface Timeline {
|
|||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* Komplexe Prozesse und Workflows mit Phasen, Abhängigkeiten und Status-Tracking
|
||||
*
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "workflows".
|
||||
*/
|
||||
export interface Workflow {
|
||||
id: number;
|
||||
tenant?: (number | null) | Tenant;
|
||||
/**
|
||||
* Name des Workflows (z.B. "Projektablauf Webentwicklung")
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* URL-freundlicher Identifier
|
||||
*/
|
||||
slug: string;
|
||||
/**
|
||||
* Ausführliche Beschreibung des Workflows
|
||||
*/
|
||||
description?: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: any;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
/**
|
||||
* Kurze Zusammenfassung für Übersichten
|
||||
*/
|
||||
shortDescription?: string | null;
|
||||
type: 'project' | 'business' | 'approval' | 'onboarding' | 'support' | 'development' | 'marketing' | 'other';
|
||||
status: 'draft' | 'published' | 'archived';
|
||||
/**
|
||||
* Optionales Bild für den Workflow
|
||||
*/
|
||||
image?: (number | null) | Media;
|
||||
properties?: {
|
||||
/**
|
||||
* z.B. "4-6 Wochen", "3 Monate"
|
||||
*/
|
||||
estimatedDuration?: string | null;
|
||||
complexity?: ('simple' | 'medium' | 'complex' | 'very_complex') | null;
|
||||
/**
|
||||
* Kann der Workflow wiederholt durchlaufen werden?
|
||||
*/
|
||||
isIterative?: boolean | null;
|
||||
/**
|
||||
* Können mehrere Phasen gleichzeitig aktiv sein?
|
||||
*/
|
||||
allowParallelPhases?: boolean | null;
|
||||
};
|
||||
displayOptions?: {
|
||||
layout?: ('vertical' | 'horizontal' | 'flowchart' | 'kanban' | 'gantt') | null;
|
||||
showPhaseNumbers?: boolean | null;
|
||||
showStepNumbers?: boolean | null;
|
||||
showDurations?: boolean | null;
|
||||
showResponsible?: boolean | null;
|
||||
/**
|
||||
* Zeigt Fortschrittsbalken basierend auf abgeschlossenen Schritten
|
||||
*/
|
||||
showProgress?: boolean | null;
|
||||
colorScheme?: ('phase' | 'status' | 'priority' | 'brand') | null;
|
||||
};
|
||||
/**
|
||||
* Hauptabschnitte des Workflows
|
||||
*/
|
||||
phases: {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
/**
|
||||
* Lucide Icon-Name (z.B. "rocket", "settings", "check-circle")
|
||||
*/
|
||||
icon?: string | null;
|
||||
/**
|
||||
* HEX-Farbe (z.B. "#3B82F6") oder Tailwind-Klasse
|
||||
*/
|
||||
color?: string | null;
|
||||
/**
|
||||
* z.B. "1 Woche", "2-3 Tage"
|
||||
*/
|
||||
estimatedDuration?: string | null;
|
||||
/**
|
||||
* Rolle oder Person (z.B. "Projektleiter", "Design-Team")
|
||||
*/
|
||||
responsible?: string | null;
|
||||
/**
|
||||
* Einzelne Schritte in dieser Phase
|
||||
*/
|
||||
steps: {
|
||||
name: string;
|
||||
description?: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: any;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
/**
|
||||
* Für kompakte Ansichten
|
||||
*/
|
||||
shortDescription?: string | null;
|
||||
stepType?: ('task' | 'decision' | 'milestone' | 'approval' | 'wait' | 'automatic') | null;
|
||||
priority?: ('critical' | 'high' | 'normal' | 'low' | 'optional') | null;
|
||||
/**
|
||||
* z.B. "2 Stunden", "1 Tag"
|
||||
*/
|
||||
estimatedDuration?: string | null;
|
||||
responsible?: string | null;
|
||||
icon?: string | null;
|
||||
/**
|
||||
* Welche Schritte müssen vorher abgeschlossen sein?
|
||||
*/
|
||||
dependencies?: {
|
||||
/**
|
||||
* IDs oder Namen der Vorgänger-Schritte (komma-separiert). Leer = vorheriger Schritt
|
||||
*/
|
||||
dependsOnSteps?: string | null;
|
||||
canRunParallel?: boolean | null;
|
||||
/**
|
||||
* Muss abgeschlossen sein, bevor der nächste Schritt beginnt
|
||||
*/
|
||||
isBlocking?: boolean | null;
|
||||
};
|
||||
/**
|
||||
* Für Entscheidungsschritte: Welche Wege gibt es?
|
||||
*/
|
||||
conditions?:
|
||||
| {
|
||||
/**
|
||||
* z.B. "Wenn genehmigt", "Bei Budget > 10.000€"
|
||||
*/
|
||||
condition: string;
|
||||
/**
|
||||
* ID oder Name des nächsten Schritts
|
||||
*/
|
||||
nextStep?: string | null;
|
||||
/**
|
||||
* z.B. "green" für positiv, "red" für negativ
|
||||
*/
|
||||
color?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
* To-Do-Punkte für diesen Schritt
|
||||
*/
|
||||
checklist?:
|
||||
| {
|
||||
item: string;
|
||||
isRequired?: boolean | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
resources?:
|
||||
| {
|
||||
name: string;
|
||||
type?: ('document' | 'template' | 'link' | 'tool' | 'video') | null;
|
||||
file?: (number | null) | Media;
|
||||
url?: string | null;
|
||||
description?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
* Was wird in diesem Schritt produziert?
|
||||
*/
|
||||
outputs?:
|
||||
| {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
* Für spezielle Anwendungsfälle
|
||||
*/
|
||||
metadata?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[];
|
||||
/**
|
||||
* Was wird am Ende dieser Phase abgeliefert?
|
||||
*/
|
||||
deliverables?:
|
||||
| {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[];
|
||||
/**
|
||||
* Ressourcen, die für den gesamten Workflow relevant sind
|
||||
*/
|
||||
globalResources?:
|
||||
| {
|
||||
name: string;
|
||||
type?: ('document' | 'template' | 'checklist' | 'link' | 'tool') | null;
|
||||
file?: (number | null) | Media;
|
||||
url?: string | null;
|
||||
description?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
seo?: {
|
||||
metaTitle?: string | null;
|
||||
metaDescription?: string | null;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* Cookie-Banner Konfiguration pro Tenant
|
||||
*
|
||||
|
|
@ -2591,6 +2855,10 @@ export interface PayloadLockedDocument {
|
|||
relationTo: 'timelines';
|
||||
value: number | Timeline;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'workflows';
|
||||
value: number | Workflow;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'cookie-configurations';
|
||||
value: number | CookieConfiguration;
|
||||
|
|
@ -3699,6 +3967,17 @@ export interface TimelinesSelect<T extends boolean = true> {
|
|||
};
|
||||
category?: T;
|
||||
importance?: T;
|
||||
stepNumber?: T;
|
||||
duration?: T;
|
||||
responsible?: T;
|
||||
actionRequired?: T;
|
||||
deliverables?:
|
||||
| T
|
||||
| {
|
||||
name?: T;
|
||||
type?: T;
|
||||
id?: T;
|
||||
};
|
||||
icon?: T;
|
||||
color?: T;
|
||||
links?:
|
||||
|
|
@ -3721,6 +4000,128 @@ export interface TimelinesSelect<T extends boolean = true> {
|
|||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "workflows_select".
|
||||
*/
|
||||
export interface WorkflowsSelect<T extends boolean = true> {
|
||||
tenant?: T;
|
||||
name?: T;
|
||||
slug?: T;
|
||||
description?: T;
|
||||
shortDescription?: T;
|
||||
type?: T;
|
||||
status?: T;
|
||||
image?: T;
|
||||
properties?:
|
||||
| T
|
||||
| {
|
||||
estimatedDuration?: T;
|
||||
complexity?: T;
|
||||
isIterative?: T;
|
||||
allowParallelPhases?: T;
|
||||
};
|
||||
displayOptions?:
|
||||
| T
|
||||
| {
|
||||
layout?: T;
|
||||
showPhaseNumbers?: T;
|
||||
showStepNumbers?: T;
|
||||
showDurations?: T;
|
||||
showResponsible?: T;
|
||||
showProgress?: T;
|
||||
colorScheme?: T;
|
||||
};
|
||||
phases?:
|
||||
| T
|
||||
| {
|
||||
name?: T;
|
||||
description?: T;
|
||||
icon?: T;
|
||||
color?: T;
|
||||
estimatedDuration?: T;
|
||||
responsible?: T;
|
||||
steps?:
|
||||
| T
|
||||
| {
|
||||
name?: T;
|
||||
description?: T;
|
||||
shortDescription?: T;
|
||||
stepType?: T;
|
||||
priority?: T;
|
||||
estimatedDuration?: T;
|
||||
responsible?: T;
|
||||
icon?: T;
|
||||
dependencies?:
|
||||
| T
|
||||
| {
|
||||
dependsOnSteps?: T;
|
||||
canRunParallel?: T;
|
||||
isBlocking?: T;
|
||||
};
|
||||
conditions?:
|
||||
| T
|
||||
| {
|
||||
condition?: T;
|
||||
nextStep?: T;
|
||||
color?: T;
|
||||
id?: T;
|
||||
};
|
||||
checklist?:
|
||||
| T
|
||||
| {
|
||||
item?: T;
|
||||
isRequired?: T;
|
||||
id?: T;
|
||||
};
|
||||
resources?:
|
||||
| T
|
||||
| {
|
||||
name?: T;
|
||||
type?: T;
|
||||
file?: T;
|
||||
url?: T;
|
||||
description?: T;
|
||||
id?: T;
|
||||
};
|
||||
outputs?:
|
||||
| T
|
||||
| {
|
||||
name?: T;
|
||||
description?: T;
|
||||
id?: T;
|
||||
};
|
||||
metadata?: T;
|
||||
id?: T;
|
||||
};
|
||||
deliverables?:
|
||||
| T
|
||||
| {
|
||||
name?: T;
|
||||
description?: T;
|
||||
id?: T;
|
||||
};
|
||||
id?: T;
|
||||
};
|
||||
globalResources?:
|
||||
| T
|
||||
| {
|
||||
name?: T;
|
||||
type?: T;
|
||||
file?: T;
|
||||
url?: T;
|
||||
description?: T;
|
||||
id?: T;
|
||||
};
|
||||
seo?:
|
||||
| T
|
||||
| {
|
||||
metaTitle?: T;
|
||||
metaDescription?: T;
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "cookie-configurations_select".
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ import { Products } from './collections/Products'
|
|||
// Timeline Collection
|
||||
import { Timelines } from './collections/Timelines'
|
||||
|
||||
// Workflow Collection
|
||||
import { Workflows } from './collections/Workflows'
|
||||
|
||||
// Consent Management Collections
|
||||
import { CookieConfigurations } from './collections/CookieConfigurations'
|
||||
import { CookieInventory } from './collections/CookieInventory'
|
||||
|
|
@ -155,8 +158,9 @@ export default buildConfig({
|
|||
// Products
|
||||
ProductCategories,
|
||||
Products,
|
||||
// Timelines
|
||||
// Timelines & Workflows
|
||||
Timelines,
|
||||
Workflows,
|
||||
// Consent Management
|
||||
CookieConfigurations,
|
||||
CookieInventory,
|
||||
|
|
@ -205,8 +209,9 @@ export default buildConfig({
|
|||
// Product Collections
|
||||
'product-categories': {},
|
||||
products: {},
|
||||
// Timeline Collection
|
||||
// Timeline & Workflow Collections
|
||||
timelines: {},
|
||||
workflows: {},
|
||||
// Consent Management Collections - customTenantField: true weil sie bereits ein tenant-Feld haben
|
||||
'cookie-configurations': { customTenantField: true },
|
||||
'cookie-inventory': { customTenantField: true },
|
||||
|
|
|
|||
Loading…
Reference in a new issue