diff --git a/.changeset/fix-company-type-validation.md b/.changeset/fix-company-type-validation.md new file mode 100644 index 0000000000..6328c43f77 --- /dev/null +++ b/.changeset/fix-company-type-validation.md @@ -0,0 +1,5 @@ +--- +"adcontextprotocol": patch +--- + +Fix company profile modal stuck on "Saving..." when selecting Data & Measurement or AI & Tech Platforms company types. Backend validation now uses centralized COMPANY_TYPE_VALUES to accept all 7 company types. diff --git a/.markdown-link-check.json b/.markdown-link-check.json index cf00aad7a2..f6f09cc322 100644 --- a/.markdown-link-check.json +++ b/.markdown-link-check.json @@ -16,9 +16,9 @@ "pattern": "https://adcontextprotocol.org/docs/" } ], - "timeout": "20s", + "timeout": "30s", "retryOn429": true, "retryCount": 5, - "fallbackRetryDelay": "5s", + "fallbackRetryDelay": "10s", "aliveStatusCodes": [200, 206] } diff --git a/package-lock.json b/package-lock.json index 6553fed6fa..08d3cd09ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "adcontextprotocol", - "version": "2.5.1", + "version": "2.5.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "adcontextprotocol", - "version": "2.5.1", + "version": "2.5.2", "dependencies": { "@adcp/client": "^3.5.2", "@anthropic-ai/sdk": "^0.71.2", diff --git a/server/src/addie/mcp/admin-tools.ts b/server/src/addie/mcp/admin-tools.ts index a5a429d61e..d92fbc9a17 100644 --- a/server/src/addie/mcp/admin-tools.ts +++ b/server/src/addie/mcp/admin-tools.ts @@ -38,6 +38,7 @@ import { isLushaConfigured, mapIndustryToCompanyType, } from '../../services/lusha.js'; +import { COMPANY_TYPE_VALUES } from '../../config/company-types.js'; import { createProspect } from '../../services/prospect.js'; import { getAllFeedsWithStats, @@ -395,8 +396,8 @@ This replaces find_prospect and lookup_organization with a unified view.`, }, company_type: { type: 'string', - enum: ['adtech', 'agency', 'brand', 'publisher', 'other'], - description: 'Type of company (adtech, agency, brand, publisher, or other)', + enum: COMPANY_TYPE_VALUES, + description: 'Type of company', }, domain: { type: 'string', @@ -455,7 +456,7 @@ This replaces find_prospect and lookup_organization with a unified view.`, }, company_type: { type: 'string', - enum: ['adtech', 'agency', 'brand', 'publisher', 'other'], + enum: COMPANY_TYPE_VALUES, description: 'Type of company', }, status: { @@ -525,7 +526,7 @@ This replaces find_prospect and lookup_organization with a unified view.`, }, company_type: { type: 'string', - enum: ['adtech', 'agency', 'brand', 'publisher', 'other'], + enum: COMPANY_TYPE_VALUES, description: 'Filter by company type', }, limit: { diff --git a/server/src/db/migrations/144_update_company_type_constraint.sql b/server/src/db/migrations/144_update_company_type_constraint.sql new file mode 100644 index 0000000000..74e581d200 --- /dev/null +++ b/server/src/db/migrations/144_update_company_type_constraint.sql @@ -0,0 +1,11 @@ +-- Migration: Update company_type CHECK constraint to include 'data' and 'ai' types +-- These types were added to the frontend but the database constraint was not updated + +-- Drop the old constraint (using IF EXISTS for idempotency) +ALTER TABLE organizations + DROP CONSTRAINT IF EXISTS organizations_company_type_check; + +-- Add updated constraint with all valid company types +ALTER TABLE organizations + ADD CONSTRAINT organizations_company_type_check + CHECK (company_type IS NULL OR company_type IN ('adtech', 'agency', 'brand', 'data', 'ai', 'publisher', 'other')); diff --git a/server/src/routes/billing-public.ts b/server/src/routes/billing-public.ts index b1ae3f71b3..d7b96b00b6 100644 --- a/server/src/routes/billing-public.ts +++ b/server/src/routes/billing-public.ts @@ -31,6 +31,7 @@ import { mapIndustryToCompanyType, mapRevenueToTier, } from "../services/lusha.js"; +import { COMPANY_TYPE_VALUES } from "../config/company-types.js"; import { WorkOS } from "@workos-inc/node"; const logger = createLogger("billing-public-routes"); @@ -627,14 +628,8 @@ export function createPublicBillingRouter(): Router { const { orgId } = req.params; const { company_type, revenue_tier } = req.body; - // Validate inputs - must match CompanyType in organization-db.ts - const validCompanyTypes = [ - "brand", - "publisher", - "agency", - "adtech", - "other", - ]; + // Validate inputs - use centralized company types config + const validCompanyTypes = COMPANY_TYPE_VALUES; const validRevenueTiers = [ "under_1m", "1m_5m", diff --git a/server/src/routes/organizations.ts b/server/src/routes/organizations.ts index 038c3cd47a..f68502b215 100644 --- a/server/src/routes/organizations.ts +++ b/server/src/routes/organizations.ts @@ -18,6 +18,7 @@ import { import { invitationRateLimiter, orgCreationRateLimiter } from "../middleware/rate-limit.js"; import { validateOrganizationName, validateEmail } from "../middleware/validation.js"; import { OrganizationDatabase, CompanyType, RevenueTier } from "../db/organization-db.js"; +import { COMPANY_TYPE_VALUES } from "../config/company-types.js"; import { JoinRequestDatabase } from "../db/join-request-db.js"; import { SlackDatabase } from "../db/slack-db.js"; import { getCompanyDomain } from "../utils/email-domain.js"; @@ -747,11 +748,10 @@ export function createOrganizationsRouter(): Router { } // Validate company_type if provided - const validCompanyTypes = ['brand', 'publisher', 'agency', 'adtech', 'other']; - if (company_type && !validCompanyTypes.includes(company_type)) { + if (company_type && !COMPANY_TYPE_VALUES.includes(company_type)) { return res.status(400).json({ error: 'Invalid company type', - message: `company_type must be one of: ${validCompanyTypes.join(', ')}`, + message: `company_type must be one of: ${COMPANY_TYPE_VALUES.join(', ')}`, }); } @@ -1083,11 +1083,10 @@ export function createOrganizationsRouter(): Router { } // Validate company_type if provided - const validCompanyTypes = ['brand', 'publisher', 'agency', 'adtech', 'other']; - if (company_type !== undefined && company_type !== null && !validCompanyTypes.includes(company_type)) { + if (company_type !== undefined && company_type !== null && !COMPANY_TYPE_VALUES.includes(company_type)) { return res.status(400).json({ error: 'Invalid company type', - message: `company_type must be one of: ${validCompanyTypes.join(', ')}`, + message: `company_type must be one of: ${COMPANY_TYPE_VALUES.join(', ')}`, }); }