feat(inquiries): website_submissions tracking + display columns; capture populates contact name/email

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 18:03:47 +02:00
parent 08adb4aeea
commit 9879b82e5f
3 changed files with 50 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
-- 0093_website_submissions_inquiry_cols.sql
-- ----------------------------------------------------------------------------
-- Inquiries workbench: tracking + display columns on website_submissions.
-- converted_client_id / converted_interest_id - set when an operator converts
-- an inquiry into CRM entities (FK to clients/interests).
-- contact_name / contact_email - extracted from the JSONB payload at capture
-- time so the list view can search/sort/display via real columns.
--
-- Idempotent: ADD COLUMN IF NOT EXISTS + CREATE INDEX IF NOT EXISTS + a
-- COALESCE backfill that only fills nulls. Safe to re-run.
ALTER TABLE website_submissions
ADD COLUMN IF NOT EXISTS converted_client_id text REFERENCES clients(id),
ADD COLUMN IF NOT EXISTS converted_interest_id text REFERENCES interests(id),
ADD COLUMN IF NOT EXISTS contact_name text,
ADD COLUMN IF NOT EXISTS contact_email text;
CREATE INDEX IF NOT EXISTS idx_ws_contact_email
ON website_submissions (port_id, contact_email);
-- Backfill display columns from existing payloads (only where still null).
UPDATE website_submissions
SET contact_email = COALESCE(contact_email, NULLIF(payload->>'email', '')),
contact_name = COALESCE(
contact_name,
NULLIF(TRIM(CONCAT_WS(' ', payload->>'first_name', payload->>'last_name')), ''),
NULLIF(payload->>'name', ''),
NULLIF(payload->>'fullName', ''),
NULLIF(payload->>'full_name', '')
)
WHERE contact_email IS NULL OR contact_name IS NULL;

View File

@@ -51,6 +51,18 @@ export const websiteSubmissions = pgTable(
* same form submission. Useful for reconciling: pick any submission
* here, look up the matching NocoDB row, confirm both halves agree. */
legacyNocodbId: text('legacy_nocodb_id'),
/** Contact name + email extracted from `payload` at capture time so the
* inquiry list can search/sort/display via real columns (payload stays
* JSONB and isn't searched directly). Populated by the capture endpoint
* and backfilled in migration 0093. */
contactName: text('contact_name'),
contactEmail: text('contact_email'),
/** Set when an operator converts this inquiry into CRM entities. FK enforced
* at the DB level (migration 0093); typed as plain text here to avoid a
* circular schema import — `clients`/`interests` already reference
* `website_submissions`. */
convertedClientId: text('converted_client_id'),
convertedInterestId: text('converted_interest_id'),
/** Capture-time metadata for debugging. */
sourceIp: text('source_ip'),
userAgent: text('user_agent'),