From 5adf855b0492839de34ed570d3d2a9fe59bbeb03 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 19:28:15 +0000 Subject: [PATCH] Fix: Implement company submission and validation plan --- src/components/admin/DesignerForm.tsx | 38 +- src/components/admin/ManufacturerForm.tsx | 38 +- src/components/admin/OperatorForm.tsx | 38 +- src/components/admin/PropertyOwnerForm.tsx | 38 +- src/lib/entitySubmissionHelpers.ts | 446 ++++++++++++++++++ src/lib/entityValidationSchemas.ts | 8 +- .../process-selective-approval/validation.ts | 6 +- 7 files changed, 591 insertions(+), 21 deletions(-) diff --git a/src/components/admin/DesignerForm.tsx b/src/components/admin/DesignerForm.tsx index 154bf568..99e97281 100644 --- a/src/components/admin/DesignerForm.tsx +++ b/src/components/admin/DesignerForm.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; @@ -15,6 +16,10 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; +import { submitDesignerCreation, submitDesignerUpdate } from '@/lib/entitySubmissionHelpers'; +import { useAuth } from '@/hooks/useAuth'; +import { toast } from 'sonner'; +import { useNavigate } from 'react-router-dom'; type DesignerFormData = z.infer; @@ -53,6 +58,9 @@ interface DesignerFormProps { export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormProps) { const { isModerator } = useUserRole(); const { headquarters } = useCompanyHeadquarters(); + const { user } = useAuth(); + const navigate = useNavigate(); + const [isSubmitting, setIsSubmitting] = useState(false); const { register, @@ -84,7 +92,29 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr -
onSubmit(data as unknown as DesignerFormData))} className="space-y-6"> + { + if (!user) { + toast.error('You must be logged in to submit'); + return; + } + + setIsSubmitting(true); + try { + if (initialData?.id) { + await submitDesignerUpdate(initialData.id, data as unknown as DesignerFormData, user.id); + toast.success('Designer update submitted for review'); + } else { + await submitDesignerCreation(data as unknown as DesignerFormData, user.id); + toast.success('Designer submitted for review'); + } + onCancel(); + } catch (error) { + console.error('Submission error:', error); + toast.error(error instanceof Error ? error.message : 'Failed to submit designer'); + } finally { + setIsSubmitting(false); + } + })} className="space-y-6"> {/* Basic Information */}
@@ -202,13 +232,13 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr {/* Actions */}
- -
diff --git a/src/components/admin/ManufacturerForm.tsx b/src/components/admin/ManufacturerForm.tsx index 78c4cf2b..845312cd 100644 --- a/src/components/admin/ManufacturerForm.tsx +++ b/src/components/admin/ManufacturerForm.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; @@ -15,6 +16,10 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; +import { submitManufacturerCreation, submitManufacturerUpdate } from '@/lib/entitySubmissionHelpers'; +import { useAuth } from '@/hooks/useAuth'; +import { toast } from 'sonner'; +import { useNavigate } from 'react-router-dom'; type ManufacturerFormData = z.infer; @@ -55,6 +60,9 @@ interface ManufacturerFormProps { export function ManufacturerForm({ onSubmit, onCancel, initialData }: ManufacturerFormProps) { const { isModerator } = useUserRole(); const { headquarters } = useCompanyHeadquarters(); + const { user } = useAuth(); + const navigate = useNavigate(); + const [isSubmitting, setIsSubmitting] = useState(false); const { register, @@ -88,7 +96,29 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur -
onSubmit(data as unknown as ManufacturerFormData))} className="space-y-6"> + { + if (!user) { + toast.error('You must be logged in to submit'); + return; + } + + setIsSubmitting(true); + try { + if (initialData?.id) { + await submitManufacturerUpdate(initialData.id, data as unknown as ManufacturerFormData, user.id); + toast.success('Manufacturer update submitted for review'); + } else { + await submitManufacturerCreation(data as unknown as ManufacturerFormData, user.id); + toast.success('Manufacturer submitted for review'); + } + onCancel(); + } catch (error) { + console.error('Submission error:', error); + toast.error(error instanceof Error ? error.message : 'Failed to submit manufacturer'); + } finally { + setIsSubmitting(false); + } + })} className="space-y-6"> {/* Basic Information */}
@@ -204,13 +234,13 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur {/* Actions */}
- -
diff --git a/src/components/admin/OperatorForm.tsx b/src/components/admin/OperatorForm.tsx index 1c1b419e..c000b992 100644 --- a/src/components/admin/OperatorForm.tsx +++ b/src/components/admin/OperatorForm.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; @@ -15,6 +16,10 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; +import { submitOperatorCreation, submitOperatorUpdate } from '@/lib/entitySubmissionHelpers'; +import { useAuth } from '@/hooks/useAuth'; +import { toast } from 'sonner'; +import { useNavigate } from 'react-router-dom'; type OperatorFormData = z.infer; @@ -55,6 +60,9 @@ interface OperatorFormProps { export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormProps) { const { isModerator } = useUserRole(); const { headquarters } = useCompanyHeadquarters(); + const { user } = useAuth(); + const navigate = useNavigate(); + const [isSubmitting, setIsSubmitting] = useState(false); const { register, @@ -86,7 +94,29 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr -
onSubmit(data as unknown as OperatorFormData))} className="space-y-6"> + { + if (!user) { + toast.error('You must be logged in to submit'); + return; + } + + setIsSubmitting(true); + try { + if (initialData?.id) { + await submitOperatorUpdate(initialData.id, data as unknown as OperatorFormData, user.id); + toast.success('Operator update submitted for review'); + } else { + await submitOperatorCreation(data as unknown as OperatorFormData, user.id); + toast.success('Operator submitted for review'); + } + onCancel(); + } catch (error) { + console.error('Submission error:', error); + toast.error(error instanceof Error ? error.message : 'Failed to submit operator'); + } finally { + setIsSubmitting(false); + } + })} className="space-y-6"> {/* Basic Information */}
@@ -204,13 +234,13 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr {/* Actions */}
- -
diff --git a/src/components/admin/PropertyOwnerForm.tsx b/src/components/admin/PropertyOwnerForm.tsx index e1715b52..b9d27f0d 100644 --- a/src/components/admin/PropertyOwnerForm.tsx +++ b/src/components/admin/PropertyOwnerForm.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; @@ -14,6 +15,10 @@ import { useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; import { useUserRole } from '@/hooks/useUserRole'; import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; +import { submitPropertyOwnerCreation, submitPropertyOwnerUpdate } from '@/lib/entitySubmissionHelpers'; +import { useAuth } from '@/hooks/useAuth'; +import { toast } from 'sonner'; +import { useNavigate } from 'react-router-dom'; const propertyOwnerSchema = z.object({ name: z.string().min(1, 'Name is required'), @@ -82,6 +87,9 @@ interface PropertyOwnerFormProps { export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyOwnerFormProps) { const { isModerator } = useUserRole(); const { headquarters } = useCompanyHeadquarters(); + const { user } = useAuth(); + const navigate = useNavigate(); + const [isSubmitting, setIsSubmitting] = useState(false); const { register, @@ -113,7 +121,29 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO -
onSubmit(data as unknown as PropertyOwnerFormData))} className="space-y-6"> + { + if (!user) { + toast.error('You must be logged in to submit'); + return; + } + + setIsSubmitting(true); + try { + if (initialData?.id) { + await submitPropertyOwnerUpdate(initialData.id, data as unknown as PropertyOwnerFormData, user.id); + toast.success('Property owner update submitted for review'); + } else { + await submitPropertyOwnerCreation(data as unknown as PropertyOwnerFormData, user.id); + toast.success('Property owner submitted for review'); + } + onCancel(); + } catch (error) { + console.error('Submission error:', error); + toast.error(error instanceof Error ? error.message : 'Failed to submit property owner'); + } finally { + setIsSubmitting(false); + } + })} className="space-y-6"> {/* Basic Information */}
@@ -231,13 +261,13 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO {/* Actions */}
- -
diff --git a/src/lib/entitySubmissionHelpers.ts b/src/lib/entitySubmissionHelpers.ts index c5762249..274203a6 100644 --- a/src/lib/entitySubmissionHelpers.ts +++ b/src/lib/entitySubmissionHelpers.ts @@ -117,6 +117,23 @@ export interface RideFormData { card_image_id?: string; } +export interface CompanyFormData { + name: string; + slug: string; + description?: string; + person_type: 'company' | 'individual' | 'firm' | 'organization'; + founded_year?: number; + founded_date?: string; + founded_date_precision?: string; + headquarters_location?: string; + website_url?: string; + images?: ImageAssignments; + banner_image_url?: string; + banner_image_id?: string; + card_image_url?: string; + card_image_id?: string; +} + /** * ⚠️ CRITICAL SECURITY PATTERN ⚠️ * @@ -438,3 +455,432 @@ export async function submitRideUpdate( return { submitted: true, submissionId: submissionData.id }; } + +/** + * ⚠️ CRITICAL SECURITY PATTERN ⚠️ + * + * Submits a new manufacturer for creation through the moderation queue. + */ +export async function submitManufacturerCreation( + data: CompanyFormData, + userId: string +): Promise<{ submitted: boolean; submissionId: string }> { + let processedImages = data.images; + if (data.images?.uploaded && data.images.uploaded.length > 0) { + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { ...data.images, uploaded: uploadedImages }; + } catch (error) { + console.error('Failed to upload images:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } + } + + const { data: submissionData, error: submissionError } = await supabase + .from('content_submissions') + .insert({ + user_id: userId, + submission_type: 'manufacturer', + content: { action: 'create' }, + status: 'pending' + }) + .select() + .single(); + + if (submissionError) throw submissionError; + + const { error: itemError } = await supabase + .from('submission_items') + .insert({ + submission_id: submissionData.id, + item_type: 'manufacturer', + item_data: { + ...data, + company_type: 'manufacturer', + images: processedImages as unknown as Json + }, + status: 'pending', + order_index: 0 + }); + + if (itemError) throw itemError; + + return { submitted: true, submissionId: submissionData.id }; +} + +export async function submitManufacturerUpdate( + companyId: string, + data: CompanyFormData, + userId: string +): Promise<{ submitted: boolean; submissionId: string }> { + const { data: existingCompany, error: fetchError } = await supabase + .from('companies') + .select('*') + .eq('id', companyId) + .single(); + + if (fetchError) throw new Error(`Failed to fetch manufacturer: ${fetchError.message}`); + if (!existingCompany) throw new Error('Manufacturer not found'); + + let processedImages = data.images; + if (data.images?.uploaded && data.images.uploaded.length > 0) { + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { ...data.images, uploaded: uploadedImages }; + } catch (error) { + console.error('Failed to upload images:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } + } + + const { data: submissionData, error: submissionError } = await supabase + .from('content_submissions') + .insert({ + user_id: userId, + submission_type: 'manufacturer', + content: { action: 'edit', company_id: companyId }, + status: 'pending' + }) + .select() + .single(); + + if (submissionError) throw submissionError; + + const { error: itemError } = await supabase + .from('submission_items') + .insert({ + submission_id: submissionData.id, + item_type: 'manufacturer', + item_data: { + ...data, + company_id: companyId, + company_type: 'manufacturer', + images: processedImages as any + }, + original_data: JSON.parse(JSON.stringify(existingCompany)), + status: 'pending', + order_index: 0 + }); + + if (itemError) throw itemError; + + return { submitted: true, submissionId: submissionData.id }; +} + +export async function submitDesignerCreation( + data: CompanyFormData, + userId: string +): Promise<{ submitted: boolean; submissionId: string }> { + let processedImages = data.images; + if (data.images?.uploaded && data.images.uploaded.length > 0) { + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { ...data.images, uploaded: uploadedImages }; + } catch (error) { + console.error('Failed to upload images:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } + } + + const { data: submissionData, error: submissionError } = await supabase + .from('content_submissions') + .insert({ + user_id: userId, + submission_type: 'designer', + content: { action: 'create' }, + status: 'pending' + }) + .select() + .single(); + + if (submissionError) throw submissionError; + + const { error: itemError } = await supabase + .from('submission_items') + .insert({ + submission_id: submissionData.id, + item_type: 'designer', + item_data: { + ...data, + company_type: 'designer', + images: processedImages as unknown as Json + }, + status: 'pending', + order_index: 0 + }); + + if (itemError) throw itemError; + + return { submitted: true, submissionId: submissionData.id }; +} + +export async function submitDesignerUpdate( + companyId: string, + data: CompanyFormData, + userId: string +): Promise<{ submitted: boolean; submissionId: string }> { + const { data: existingCompany, error: fetchError } = await supabase + .from('companies') + .select('*') + .eq('id', companyId) + .single(); + + if (fetchError) throw new Error(`Failed to fetch designer: ${fetchError.message}`); + if (!existingCompany) throw new Error('Designer not found'); + + let processedImages = data.images; + if (data.images?.uploaded && data.images.uploaded.length > 0) { + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { ...data.images, uploaded: uploadedImages }; + } catch (error) { + console.error('Failed to upload images:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } + } + + const { data: submissionData, error: submissionError } = await supabase + .from('content_submissions') + .insert({ + user_id: userId, + submission_type: 'designer', + content: { action: 'edit', company_id: companyId }, + status: 'pending' + }) + .select() + .single(); + + if (submissionError) throw submissionError; + + const { error: itemError } = await supabase + .from('submission_items') + .insert({ + submission_id: submissionData.id, + item_type: 'designer', + item_data: { + ...data, + company_id: companyId, + company_type: 'designer', + images: processedImages as any + }, + original_data: JSON.parse(JSON.stringify(existingCompany)), + status: 'pending', + order_index: 0 + }); + + if (itemError) throw itemError; + + return { submitted: true, submissionId: submissionData.id }; +} + +export async function submitOperatorCreation( + data: CompanyFormData, + userId: string +): Promise<{ submitted: boolean; submissionId: string }> { + let processedImages = data.images; + if (data.images?.uploaded && data.images.uploaded.length > 0) { + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { ...data.images, uploaded: uploadedImages }; + } catch (error) { + console.error('Failed to upload images:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } + } + + const { data: submissionData, error: submissionError } = await supabase + .from('content_submissions') + .insert({ + user_id: userId, + submission_type: 'operator', + content: { action: 'create' }, + status: 'pending' + }) + .select() + .single(); + + if (submissionError) throw submissionError; + + const { error: itemError } = await supabase + .from('submission_items') + .insert({ + submission_id: submissionData.id, + item_type: 'operator', + item_data: { + ...data, + company_type: 'operator', + images: processedImages as unknown as Json + }, + status: 'pending', + order_index: 0 + }); + + if (itemError) throw itemError; + + return { submitted: true, submissionId: submissionData.id }; +} + +export async function submitOperatorUpdate( + companyId: string, + data: CompanyFormData, + userId: string +): Promise<{ submitted: boolean; submissionId: string }> { + const { data: existingCompany, error: fetchError } = await supabase + .from('companies') + .select('*') + .eq('id', companyId) + .single(); + + if (fetchError) throw new Error(`Failed to fetch operator: ${fetchError.message}`); + if (!existingCompany) throw new Error('Operator not found'); + + let processedImages = data.images; + if (data.images?.uploaded && data.images.uploaded.length > 0) { + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { ...data.images, uploaded: uploadedImages }; + } catch (error) { + console.error('Failed to upload images:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } + } + + const { data: submissionData, error: submissionError } = await supabase + .from('content_submissions') + .insert({ + user_id: userId, + submission_type: 'operator', + content: { action: 'edit', company_id: companyId }, + status: 'pending' + }) + .select() + .single(); + + if (submissionError) throw submissionError; + + const { error: itemError } = await supabase + .from('submission_items') + .insert({ + submission_id: submissionData.id, + item_type: 'operator', + item_data: { + ...data, + company_id: companyId, + company_type: 'operator', + images: processedImages as any + }, + original_data: JSON.parse(JSON.stringify(existingCompany)), + status: 'pending', + order_index: 0 + }); + + if (itemError) throw itemError; + + return { submitted: true, submissionId: submissionData.id }; +} + +export async function submitPropertyOwnerCreation( + data: CompanyFormData, + userId: string +): Promise<{ submitted: boolean; submissionId: string }> { + let processedImages = data.images; + if (data.images?.uploaded && data.images.uploaded.length > 0) { + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { ...data.images, uploaded: uploadedImages }; + } catch (error) { + console.error('Failed to upload images:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } + } + + const { data: submissionData, error: submissionError } = await supabase + .from('content_submissions') + .insert({ + user_id: userId, + submission_type: 'property_owner', + content: { action: 'create' }, + status: 'pending' + }) + .select() + .single(); + + if (submissionError) throw submissionError; + + const { error: itemError } = await supabase + .from('submission_items') + .insert({ + submission_id: submissionData.id, + item_type: 'property_owner', + item_data: { + ...data, + company_type: 'property_owner', + images: processedImages as unknown as Json + }, + status: 'pending', + order_index: 0 + }); + + if (itemError) throw itemError; + + return { submitted: true, submissionId: submissionData.id }; +} + +export async function submitPropertyOwnerUpdate( + companyId: string, + data: CompanyFormData, + userId: string +): Promise<{ submitted: boolean; submissionId: string }> { + const { data: existingCompany, error: fetchError } = await supabase + .from('companies') + .select('*') + .eq('id', companyId) + .single(); + + if (fetchError) throw new Error(`Failed to fetch property owner: ${fetchError.message}`); + if (!existingCompany) throw new Error('Property owner not found'); + + let processedImages = data.images; + if (data.images?.uploaded && data.images.uploaded.length > 0) { + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { ...data.images, uploaded: uploadedImages }; + } catch (error) { + console.error('Failed to upload images:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } + } + + const { data: submissionData, error: submissionError } = await supabase + .from('content_submissions') + .insert({ + user_id: userId, + submission_type: 'property_owner', + content: { action: 'edit', company_id: companyId }, + status: 'pending' + }) + .select() + .single(); + + if (submissionError) throw submissionError; + + const { error: itemError } = await supabase + .from('submission_items') + .insert({ + submission_id: submissionData.id, + item_type: 'property_owner', + item_data: { + ...data, + company_id: companyId, + company_type: 'property_owner', + images: processedImages as any + }, + original_data: JSON.parse(JSON.stringify(existingCompany)), + status: 'pending', + order_index: 0 + }); + + if (itemError) throw itemError; + + return { submitted: true, submissionId: submissionData.id }; +} diff --git a/src/lib/entityValidationSchemas.ts b/src/lib/entityValidationSchemas.ts index 0c1e2c7d..efd4b454 100644 --- a/src/lib/entityValidationSchemas.ts +++ b/src/lib/entityValidationSchemas.ts @@ -96,11 +96,11 @@ export const rideValidationSchema = z.object({ // Company Schema (Manufacturer, Designer, Operator, Property Owner) export const companyValidationSchema = z.object({ - name: z.string().min(1, 'Company name is required').max(200, 'Name must be less than 200 characters'), - slug: z.string().min(1, 'Slug is required').regex(/^[a-z0-9-]+$/, 'Slug must contain only lowercase letters, numbers, and hyphens'), + name: z.string().trim().min(1, 'Company name is required').max(200, 'Name must be less than 200 characters'), + slug: z.string().trim().min(1, 'Slug is required').regex(/^[a-z0-9-]+$/, 'Slug must contain only lowercase letters, numbers, and hyphens'), + company_type: z.enum(['manufacturer', 'designer', 'operator', 'property_owner']).optional(), description: z.string().max(2000, 'Description must be less than 2000 characters').optional(), - company_type: z.string().min(1, 'Company type is required'), - person_type: z.enum(['company', 'individual', 'firm', 'organization']).optional(), + person_type: z.enum(['company', 'individual', 'firm', 'organization']), founded_year: z.number().min(1800, 'Founded year must be after 1800').max(currentYear, `Founded year cannot be in the future`).optional(), founded_date: z.string().optional(), founded_date_precision: z.enum(['day', 'month', 'year']).optional(), diff --git a/supabase/functions/process-selective-approval/validation.ts b/supabase/functions/process-selective-approval/validation.ts index 5e09ca34..bd4b70cc 100644 --- a/supabase/functions/process-selective-approval/validation.ts +++ b/supabase/functions/process-selective-approval/validation.ts @@ -72,7 +72,11 @@ export function validateEntityData(entityType: string, data: any): ValidationRes case 'designer': case 'operator': case 'property_owner': - if (!data.company_type) errors.push('Company type is required'); + if (!data.company_type) { + errors.push(`Company type is required (expected: ${entityType})`); + } else if (data.company_type !== entityType) { + errors.push(`Company type mismatch: expected '${entityType}' but got '${data.company_type}'`); + } if (data.founded_year) { const year = parseInt(data.founded_year); const currentYear = new Date().getFullYear();