mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 17:24:12 +00:00
feat: enhance FormSubmissions with workflow and tracking
- Add status workflow: new → read → in-progress → waiting → completed → archived - Add priority levels (high, normal, low) - Add assignedTo field for team member assignment - Add internal notes array with author and timestamp - Add response tracking (responded, method, summary) - Add tags for categorization - Auto-mark as read on first view - Auto-set note author and timestamp - Improved admin view with better columns - Update documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
8868a5be30
commit
17eb46a787
6 changed files with 476 additions and 2 deletions
|
|
@ -411,7 +411,7 @@ SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT 10;
|
||||||
| NewsletterSubscribers | newsletter-subscribers | Newsletter mit Double Opt-In |
|
| NewsletterSubscribers | newsletter-subscribers | Newsletter mit Double Opt-In |
|
||||||
| SocialLinks | social-links | Social Media Links |
|
| SocialLinks | social-links | Social Media Links |
|
||||||
| Forms | forms | Formular-Builder |
|
| Forms | forms | Formular-Builder |
|
||||||
| FormSubmissions | form-submissions | Formular-Einsendungen |
|
| FormSubmissions | form-submissions | Formular-Einsendungen mit Status-Workflow |
|
||||||
| EmailLogs | email-logs | E-Mail-Protokollierung |
|
| EmailLogs | email-logs | E-Mail-Protokollierung |
|
||||||
| AuditLogs | audit-logs | Security Audit Trail |
|
| AuditLogs | audit-logs | Security Audit Trail |
|
||||||
| CookieConfigurations | cookie-configurations | Cookie-Banner Konfiguration |
|
| CookieConfigurations | cookie-configurations | Cookie-Banner Konfiguration |
|
||||||
|
|
|
||||||
215
src/collections/FormSubmissionsOverrides.ts
Normal file
215
src/collections/FormSubmissionsOverrides.ts
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
// src/collections/FormSubmissionsOverrides.ts
|
||||||
|
|
||||||
|
import type { CollectionConfig } from 'payload'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FormSubmissions Overrides
|
||||||
|
*
|
||||||
|
* Erweitert die vom formBuilderPlugin erstellte FormSubmissions Collection um:
|
||||||
|
* - Status-Workflow (neu → in Bearbeitung → erledigt)
|
||||||
|
* - Interne Notizen
|
||||||
|
* - Zuständigkeits-Zuweisung
|
||||||
|
* - Verbesserte Admin-Ansicht
|
||||||
|
*/
|
||||||
|
export const formSubmissionOverrides: Partial<CollectionConfig> = {
|
||||||
|
admin: {
|
||||||
|
useAsTitle: 'id',
|
||||||
|
group: 'Formulare',
|
||||||
|
defaultColumns: ['id', 'form', 'status', 'assignedTo', 'createdAt'],
|
||||||
|
description: 'Eingegangene Formular-Einsendungen',
|
||||||
|
listSearchableFields: ['id', 'status'],
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
singular: 'Formular-Einsendung',
|
||||||
|
plural: 'Formular-Einsendungen',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
// Status-Workflow
|
||||||
|
{
|
||||||
|
name: 'status',
|
||||||
|
type: 'select',
|
||||||
|
defaultValue: 'new',
|
||||||
|
label: 'Status',
|
||||||
|
options: [
|
||||||
|
{ label: '🆕 Neu', value: 'new' },
|
||||||
|
{ label: '👀 Gelesen', value: 'read' },
|
||||||
|
{ label: '🔄 In Bearbeitung', value: 'in-progress' },
|
||||||
|
{ label: '⏳ Warten auf Rückmeldung', value: 'waiting' },
|
||||||
|
{ label: '✅ Erledigt', value: 'completed' },
|
||||||
|
{ label: '🗑️ Spam/Archiviert', value: 'archived' },
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Priorität
|
||||||
|
{
|
||||||
|
name: 'priority',
|
||||||
|
type: 'select',
|
||||||
|
defaultValue: 'normal',
|
||||||
|
label: 'Priorität',
|
||||||
|
options: [
|
||||||
|
{ label: '🔴 Hoch', value: 'high' },
|
||||||
|
{ label: '🟡 Normal', value: 'normal' },
|
||||||
|
{ label: '🟢 Niedrig', value: 'low' },
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Zuständigkeit
|
||||||
|
{
|
||||||
|
name: 'assignedTo',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'users',
|
||||||
|
label: 'Zuständig',
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
description: 'Wer bearbeitet diese Anfrage?',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Interne Notizen
|
||||||
|
{
|
||||||
|
name: 'internalNotes',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Interne Notizen',
|
||||||
|
admin: {
|
||||||
|
description: 'Interne Kommunikation zur Anfrage (nicht für Kunden sichtbar)',
|
||||||
|
initCollapsed: false,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'note',
|
||||||
|
type: 'textarea',
|
||||||
|
required: true,
|
||||||
|
label: 'Notiz',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'author',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'users',
|
||||||
|
label: 'Autor',
|
||||||
|
admin: {
|
||||||
|
readOnly: true,
|
||||||
|
description: 'Wird automatisch gesetzt',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
type: 'date',
|
||||||
|
label: 'Erstellt am',
|
||||||
|
admin: {
|
||||||
|
readOnly: true,
|
||||||
|
date: {
|
||||||
|
pickerAppearance: 'dayAndTime',
|
||||||
|
displayFormat: 'dd.MM.yyyy HH:mm',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// Antwort-Tracking
|
||||||
|
{
|
||||||
|
name: 'responseTracking',
|
||||||
|
type: 'group',
|
||||||
|
label: 'Antwort-Tracking',
|
||||||
|
admin: {
|
||||||
|
description: 'Tracking der Kommunikation mit dem Absender',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'responded',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: false,
|
||||||
|
label: 'Antwort gesendet',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'respondedAt',
|
||||||
|
type: 'date',
|
||||||
|
label: 'Antwort gesendet am',
|
||||||
|
admin: {
|
||||||
|
condition: (data, siblingData) => siblingData?.responded,
|
||||||
|
date: {
|
||||||
|
pickerAppearance: 'dayAndTime',
|
||||||
|
displayFormat: 'dd.MM.yyyy HH:mm',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'respondedBy',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'users',
|
||||||
|
label: 'Antwort gesendet von',
|
||||||
|
admin: {
|
||||||
|
condition: (data, siblingData) => siblingData?.responded,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'responseMethod',
|
||||||
|
type: 'select',
|
||||||
|
label: 'Antwort-Methode',
|
||||||
|
options: [
|
||||||
|
{ label: 'E-Mail', value: 'email' },
|
||||||
|
{ label: 'Telefon', value: 'phone' },
|
||||||
|
{ label: 'Persönlich', value: 'in-person' },
|
||||||
|
{ label: 'Brief', value: 'letter' },
|
||||||
|
],
|
||||||
|
admin: {
|
||||||
|
condition: (data, siblingData) => siblingData?.responded,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'responseSummary',
|
||||||
|
type: 'textarea',
|
||||||
|
label: 'Zusammenfassung der Antwort',
|
||||||
|
admin: {
|
||||||
|
condition: (data, siblingData) => siblingData?.responded,
|
||||||
|
description: 'Kurze Zusammenfassung was geantwortet wurde',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// Tags für Kategorisierung
|
||||||
|
{
|
||||||
|
name: 'tags',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Tags',
|
||||||
|
admin: {
|
||||||
|
description: 'Tags zur Kategorisierung (z.B. "Preisanfrage", "Support")',
|
||||||
|
initCollapsed: true,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'tag',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
label: 'Tag',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// Gelesen-Markierung
|
||||||
|
{
|
||||||
|
name: 'readAt',
|
||||||
|
type: 'date',
|
||||||
|
label: 'Gelesen am',
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
readOnly: true,
|
||||||
|
date: {
|
||||||
|
pickerAppearance: 'dayAndTime',
|
||||||
|
displayFormat: 'dd.MM.yyyy HH:mm',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'readBy',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'users',
|
||||||
|
label: 'Gelesen von',
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
132
src/hooks/formSubmissionHooks.ts
Normal file
132
src/hooks/formSubmissionHooks.ts
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
// src/hooks/formSubmissionHooks.ts
|
||||||
|
|
||||||
|
import type {
|
||||||
|
CollectionBeforeChangeHook,
|
||||||
|
CollectionAfterReadHook,
|
||||||
|
FieldHook,
|
||||||
|
} from 'payload'
|
||||||
|
|
||||||
|
interface InternalNote {
|
||||||
|
note: string
|
||||||
|
author?: number | string | { id: number | string }
|
||||||
|
createdAt?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormSubmissionDoc {
|
||||||
|
id: number | string
|
||||||
|
status?: string
|
||||||
|
readAt?: string
|
||||||
|
readBy?: number | string | { id: number | string }
|
||||||
|
internalNotes?: InternalNote[]
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook: Setzt automatisch den Autor und Zeitstempel bei neuen Notizen
|
||||||
|
*/
|
||||||
|
export const setNoteMetadata: CollectionBeforeChangeHook<FormSubmissionDoc> = async ({
|
||||||
|
data,
|
||||||
|
req,
|
||||||
|
originalDoc,
|
||||||
|
}) => {
|
||||||
|
if (!data.internalNotes || !req.user) return data
|
||||||
|
|
||||||
|
const originalNotes = originalDoc?.internalNotes || []
|
||||||
|
const newNotes = data.internalNotes || []
|
||||||
|
|
||||||
|
// Finde neue Notizen (die noch keinen Autor haben)
|
||||||
|
const updatedNotes = newNotes.map((note, index) => {
|
||||||
|
const isNew = !note.author && !note.createdAt
|
||||||
|
const isExisting = originalNotes[index]
|
||||||
|
|
||||||
|
if (isNew || (!isExisting && !note.author)) {
|
||||||
|
return {
|
||||||
|
...note,
|
||||||
|
author: req.user?.id,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return note
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
internalNotes: updatedNotes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook: Markiert Einsendung als gelesen beim ersten Öffnen
|
||||||
|
*/
|
||||||
|
export const markAsRead: CollectionBeforeChangeHook<FormSubmissionDoc> = async ({
|
||||||
|
data,
|
||||||
|
req,
|
||||||
|
originalDoc,
|
||||||
|
operation,
|
||||||
|
}) => {
|
||||||
|
// Nur beim Update und wenn noch nicht gelesen
|
||||||
|
if (operation !== 'update' || !req.user) return data
|
||||||
|
|
||||||
|
// Wenn noch nicht gelesen, jetzt markieren
|
||||||
|
if (!originalDoc?.readAt) {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
readAt: new Date().toISOString(),
|
||||||
|
readBy: req.user.id,
|
||||||
|
// Setze Status auf "gelesen" wenn noch "neu"
|
||||||
|
status: originalDoc?.status === 'new' ? 'read' : (data.status || originalDoc?.status),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook: Setzt Antwort-Zeitstempel automatisch
|
||||||
|
*/
|
||||||
|
export const setResponseTimestamp: CollectionBeforeChangeHook<FormSubmissionDoc> = async ({
|
||||||
|
data,
|
||||||
|
req,
|
||||||
|
originalDoc,
|
||||||
|
}) => {
|
||||||
|
// Prüfe ob "responded" gerade auf true gesetzt wurde
|
||||||
|
const wasResponded = originalDoc?.responseTracking?.responded
|
||||||
|
const isNowResponded = data?.responseTracking?.responded
|
||||||
|
|
||||||
|
if (!wasResponded && isNowResponded && req.user) {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
responseTracking: {
|
||||||
|
...data.responseTracking,
|
||||||
|
respondedAt: new Date().toISOString(),
|
||||||
|
respondedBy: req.user.id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kombinierter BeforeChange Hook
|
||||||
|
*/
|
||||||
|
export const formSubmissionBeforeChange: CollectionBeforeChangeHook<FormSubmissionDoc> = async (
|
||||||
|
args
|
||||||
|
) => {
|
||||||
|
let data = args.data
|
||||||
|
|
||||||
|
// Notiz-Metadaten setzen
|
||||||
|
const result1 = await setNoteMetadata({ ...args, data })
|
||||||
|
data = result1 as FormSubmissionDoc
|
||||||
|
|
||||||
|
// Als gelesen markieren
|
||||||
|
const result2 = await markAsRead({ ...args, data })
|
||||||
|
data = result2 as FormSubmissionDoc
|
||||||
|
|
||||||
|
// Antwort-Zeitstempel setzen
|
||||||
|
const result3 = await setResponseTimestamp({ ...args, data })
|
||||||
|
data = result3 as FormSubmissionDoc
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
115
src/migrations/20251210_090000_enhance_form_submissions.ts
Normal file
115
src/migrations/20251210_090000_enhance_form_submissions.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
-- Status Enum
|
||||||
|
CREATE TYPE "public"."enum_form_submissions_status" AS ENUM('new', 'read', 'in-progress', 'waiting', 'completed', 'archived');
|
||||||
|
|
||||||
|
-- Priority Enum
|
||||||
|
CREATE TYPE "public"."enum_form_submissions_priority" AS ENUM('high', 'normal', 'low');
|
||||||
|
|
||||||
|
-- Response Method Enum
|
||||||
|
CREATE TYPE "public"."enum_form_submissions_response_tracking_response_method" AS ENUM('email', 'phone', 'in-person', 'letter');
|
||||||
|
|
||||||
|
-- Add new columns to form_submissions
|
||||||
|
ALTER TABLE "form_submissions"
|
||||||
|
ADD COLUMN "status" "enum_form_submissions_status" DEFAULT 'new',
|
||||||
|
ADD COLUMN "priority" "enum_form_submissions_priority" DEFAULT 'normal',
|
||||||
|
ADD COLUMN "assigned_to_id" integer,
|
||||||
|
ADD COLUMN "read_at" timestamp(3) with time zone,
|
||||||
|
ADD COLUMN "read_by_id" integer,
|
||||||
|
ADD COLUMN "response_tracking_responded" boolean DEFAULT false,
|
||||||
|
ADD COLUMN "response_tracking_responded_at" timestamp(3) with time zone,
|
||||||
|
ADD COLUMN "response_tracking_responded_by_id" integer,
|
||||||
|
ADD COLUMN "response_tracking_response_method" "enum_form_submissions_response_tracking_response_method",
|
||||||
|
ADD COLUMN "response_tracking_response_summary" varchar;
|
||||||
|
|
||||||
|
-- Create internal notes table
|
||||||
|
CREATE TABLE "form_submissions_internal_notes" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"note" varchar NOT NULL,
|
||||||
|
"author_id" integer,
|
||||||
|
"created_at" timestamp(3) with time zone
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Create tags table
|
||||||
|
CREATE TABLE "form_submissions_tags" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"tag" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Add foreign keys
|
||||||
|
ALTER TABLE "form_submissions"
|
||||||
|
ADD CONSTRAINT "form_submissions_assigned_to_id_users_id_fk"
|
||||||
|
FOREIGN KEY ("assigned_to_id") REFERENCES "public"."users"("id") ON DELETE SET NULL ON UPDATE NO ACTION;
|
||||||
|
|
||||||
|
ALTER TABLE "form_submissions"
|
||||||
|
ADD CONSTRAINT "form_submissions_read_by_id_users_id_fk"
|
||||||
|
FOREIGN KEY ("read_by_id") REFERENCES "public"."users"("id") ON DELETE SET NULL ON UPDATE NO ACTION;
|
||||||
|
|
||||||
|
ALTER TABLE "form_submissions"
|
||||||
|
ADD CONSTRAINT "form_submissions_response_tracking_responded_by_id_users_id_fk"
|
||||||
|
FOREIGN KEY ("response_tracking_responded_by_id") REFERENCES "public"."users"("id") ON DELETE SET NULL ON UPDATE NO ACTION;
|
||||||
|
|
||||||
|
ALTER TABLE "form_submissions_internal_notes"
|
||||||
|
ADD CONSTRAINT "form_submissions_internal_notes_author_id_users_id_fk"
|
||||||
|
FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE SET NULL ON UPDATE NO ACTION;
|
||||||
|
|
||||||
|
ALTER TABLE "form_submissions_internal_notes"
|
||||||
|
ADD CONSTRAINT "form_submissions_internal_notes_parent_id_fk"
|
||||||
|
FOREIGN KEY ("_parent_id") REFERENCES "public"."form_submissions"("id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
||||||
|
|
||||||
|
ALTER TABLE "form_submissions_tags"
|
||||||
|
ADD CONSTRAINT "form_submissions_tags_parent_id_fk"
|
||||||
|
FOREIGN KEY ("_parent_id") REFERENCES "public"."form_submissions"("id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
||||||
|
|
||||||
|
-- Create indexes
|
||||||
|
CREATE INDEX "form_submissions_status_idx" ON "form_submissions" USING btree ("status");
|
||||||
|
CREATE INDEX "form_submissions_priority_idx" ON "form_submissions" USING btree ("priority");
|
||||||
|
CREATE INDEX "form_submissions_assigned_to_idx" ON "form_submissions" USING btree ("assigned_to_id");
|
||||||
|
CREATE INDEX "form_submissions_internal_notes_order_idx" ON "form_submissions_internal_notes" USING btree ("_order");
|
||||||
|
CREATE INDEX "form_submissions_internal_notes_parent_id_idx" ON "form_submissions_internal_notes" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "form_submissions_tags_order_idx" ON "form_submissions_tags" USING btree ("_order");
|
||||||
|
CREATE INDEX "form_submissions_tags_parent_id_idx" ON "form_submissions_tags" USING btree ("_parent_id");
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
-- Drop tables
|
||||||
|
DROP TABLE IF EXISTS "form_submissions_tags" CASCADE;
|
||||||
|
DROP TABLE IF EXISTS "form_submissions_internal_notes" CASCADE;
|
||||||
|
|
||||||
|
-- Drop indexes
|
||||||
|
DROP INDEX IF EXISTS "form_submissions_status_idx";
|
||||||
|
DROP INDEX IF EXISTS "form_submissions_priority_idx";
|
||||||
|
DROP INDEX IF EXISTS "form_submissions_assigned_to_idx";
|
||||||
|
|
||||||
|
-- Drop foreign keys and columns
|
||||||
|
ALTER TABLE "form_submissions"
|
||||||
|
DROP CONSTRAINT IF EXISTS "form_submissions_assigned_to_id_users_id_fk",
|
||||||
|
DROP CONSTRAINT IF EXISTS "form_submissions_read_by_id_users_id_fk",
|
||||||
|
DROP CONSTRAINT IF EXISTS "form_submissions_response_tracking_responded_by_id_users_id_fk";
|
||||||
|
|
||||||
|
ALTER TABLE "form_submissions"
|
||||||
|
DROP COLUMN IF EXISTS "status",
|
||||||
|
DROP COLUMN IF EXISTS "priority",
|
||||||
|
DROP COLUMN IF EXISTS "assigned_to_id",
|
||||||
|
DROP COLUMN IF EXISTS "read_at",
|
||||||
|
DROP COLUMN IF EXISTS "read_by_id",
|
||||||
|
DROP COLUMN IF EXISTS "response_tracking_responded",
|
||||||
|
DROP COLUMN IF EXISTS "response_tracking_responded_at",
|
||||||
|
DROP COLUMN IF EXISTS "response_tracking_responded_by_id",
|
||||||
|
DROP COLUMN IF EXISTS "response_tracking_response_method",
|
||||||
|
DROP COLUMN IF EXISTS "response_tracking_response_summary";
|
||||||
|
|
||||||
|
-- Drop enums
|
||||||
|
DROP TYPE IF EXISTS "public"."enum_form_submissions_status";
|
||||||
|
DROP TYPE IF EXISTS "public"."enum_form_submissions_priority";
|
||||||
|
DROP TYPE IF EXISTS "public"."enum_form_submissions_response_tracking_response_method";
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import * as migration_20251207_205727_audit_logs_collection from './20251207_205
|
||||||
import * as migration_20251210_052757_add_faqs_collection from './20251210_052757_add_faqs_collection';
|
import * as migration_20251210_052757_add_faqs_collection from './20251210_052757_add_faqs_collection';
|
||||||
import * as migration_20251210_071506_add_team_collection from './20251210_071506_add_team_collection';
|
import * as migration_20251210_071506_add_team_collection from './20251210_071506_add_team_collection';
|
||||||
import * as migration_20251210_073811_add_services_collections from './20251210_073811_add_services_collections';
|
import * as migration_20251210_073811_add_services_collections from './20251210_073811_add_services_collections';
|
||||||
|
import * as migration_20251210_090000_enhance_form_submissions from './20251210_090000_enhance_form_submissions';
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [
|
||||||
{
|
{
|
||||||
|
|
@ -52,6 +53,11 @@ export const migrations = [
|
||||||
{
|
{
|
||||||
up: migration_20251210_073811_add_services_collections.up,
|
up: migration_20251210_073811_add_services_collections.up,
|
||||||
down: migration_20251210_073811_add_services_collections.down,
|
down: migration_20251210_073811_add_services_collections.down,
|
||||||
name: '20251210_073811_add_services_collections'
|
name: '20251210_073811_add_services_collections',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20251210_090000_enhance_form_submissions.up,
|
||||||
|
down: migration_20251210_090000_enhance_form_submissions.down,
|
||||||
|
name: '20251210_090000_enhance_form_submissions',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@ import { SEOSettings } from './globals/SEOSettings'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { sendFormNotification } from './hooks/sendFormNotification'
|
import { sendFormNotification } from './hooks/sendFormNotification'
|
||||||
|
import { formSubmissionBeforeChange } from './hooks/formSubmissionHooks'
|
||||||
|
|
||||||
|
// Form Submissions Overrides
|
||||||
|
import { formSubmissionOverrides } from './collections/FormSubmissionsOverrides'
|
||||||
|
|
||||||
// Email
|
// Email
|
||||||
import { multiTenantEmailAdapter } from './lib/email/payload-email-adapter'
|
import { multiTenantEmailAdapter } from './lib/email/payload-email-adapter'
|
||||||
|
|
@ -240,7 +244,9 @@ export default buildConfig({
|
||||||
// Fix für TypeScript Types Generation - das Plugin braucht explizite relationTo Angaben
|
// Fix für TypeScript Types Generation - das Plugin braucht explizite relationTo Angaben
|
||||||
redirectRelationships: ['pages'],
|
redirectRelationships: ['pages'],
|
||||||
formSubmissionOverrides: {
|
formSubmissionOverrides: {
|
||||||
|
...formSubmissionOverrides,
|
||||||
hooks: {
|
hooks: {
|
||||||
|
beforeChange: [formSubmissionBeforeChange],
|
||||||
afterChange: [sendFormNotification],
|
afterChange: [sendFormNotification],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue