diff --git a/src/components/moderation/FieldComparison.tsx b/src/components/moderation/FieldComparison.tsx
index 017cea2d..dd41e4a3 100644
--- a/src/components/moderation/FieldComparison.tsx
+++ b/src/components/moderation/FieldComparison.tsx
@@ -6,8 +6,8 @@ import { ArrayFieldDiff } from './ArrayFieldDiff';
import { SpecialFieldDisplay } from './SpecialFieldDisplay';
// Helper to format compact values (truncate long strings)
-function formatCompactValue(value: any, maxLength = 30): string {
- const formatted = formatFieldValue(value);
+function formatCompactValue(value: any, precision?: 'day' | 'month' | 'year', maxLength = 30): string {
+ const formatted = formatFieldValue(value, precision);
if (formatted.length > maxLength) {
return formatted.substring(0, maxLength) + '...';
}
@@ -20,7 +20,12 @@ interface FieldDiffProps {
}
export function FieldDiff({ change, compact = false }: FieldDiffProps) {
- const { field, oldValue, newValue, changeType } = change;
+ const { field, oldValue, newValue, changeType, metadata } = change;
+
+ // Extract precision for date fields
+ const precision = metadata?.precision;
+ const oldPrecision = metadata?.oldPrecision;
+ const newPrecision = metadata?.newPrecision;
// Check if this is an array field that needs special handling
if (Array.isArray(oldValue) && Array.isArray(newValue)) {
@@ -55,7 +60,7 @@ export function FieldDiff({ change, compact = false }: FieldDiffProps) {
if (changeType === 'added') {
return (
- {fieldName}: + {formatCompactValue(newValue)}
+ {fieldName}: + {formatCompactValue(newValue, precision)}
);
}
@@ -63,7 +68,7 @@ export function FieldDiff({ change, compact = false }: FieldDiffProps) {
if (changeType === 'removed') {
return (
- {fieldName}: {formatCompactValue(oldValue)}
+ {fieldName}: {formatCompactValue(oldValue, precision)}
);
}
@@ -71,7 +76,7 @@ export function FieldDiff({ change, compact = false }: FieldDiffProps) {
if (changeType === 'modified') {
return (
- {fieldName}: {formatCompactValue(oldValue)} → {formatCompactValue(newValue)}
+ {fieldName}: {formatCompactValue(oldValue, oldPrecision || precision)} → {formatCompactValue(newValue, newPrecision || precision)}
);
}
@@ -89,24 +94,24 @@ export function FieldDiff({ change, compact = false }: FieldDiffProps) {
{changeType === 'added' && (
- + {formatFieldValue(newValue)}
+ + {formatFieldValue(newValue, precision)}
)}
{changeType === 'removed' && (
- {formatFieldValue(oldValue)}
+ {formatFieldValue(oldValue, precision)}
)}
{changeType === 'modified' && (
- {formatFieldValue(oldValue)}
+ {formatFieldValue(oldValue, oldPrecision || precision)}
- {formatFieldValue(newValue)}
+ {formatFieldValue(newValue, newPrecision || precision)}
)}
diff --git a/src/components/moderation/SpecialFieldDisplay.tsx b/src/components/moderation/SpecialFieldDisplay.tsx
index 296d0f24..0fd1a200 100644
--- a/src/components/moderation/SpecialFieldDisplay.tsx
+++ b/src/components/moderation/SpecialFieldDisplay.tsx
@@ -186,6 +186,11 @@ function StatusFieldDisplay({ change, compact }: { change: FieldChange; compact:
}
function DateFieldDisplay({ change, compact }: { change: FieldChange; compact: boolean }) {
+ // Extract precision from metadata
+ const precision = change.metadata?.precision;
+ const oldPrecision = change.metadata?.oldPrecision;
+ const newPrecision = change.metadata?.newPrecision;
+
if (compact) {
return (
@@ -204,29 +209,34 @@ function DateFieldDisplay({ change, compact }: { change: FieldChange; compact: b
{formatFieldName(change.field)}
+ {precision && (
+
+ {precision === 'year' ? 'Year Only' : precision === 'month' ? 'Month & Year' : 'Full Date'}
+
+ )}
{change.changeType === 'modified' && (
- {formatFieldValue(change.oldValue)}
+ {formatFieldValue(change.oldValue, oldPrecision || precision)}
- {formatFieldValue(change.newValue)}
+ {formatFieldValue(change.newValue, newPrecision || precision)}
)}
{change.changeType === 'added' && (
- + {formatFieldValue(change.newValue)}
+ + {formatFieldValue(change.newValue, precision)}
)}
{change.changeType === 'removed' && (
- {formatFieldValue(change.oldValue)}
+ {formatFieldValue(change.oldValue, precision)}
)}
diff --git a/src/lib/entityValidationSchemas.ts b/src/lib/entityValidationSchemas.ts
index a9b4fd8d..57186cde 100644
--- a/src/lib/entityValidationSchemas.ts
+++ b/src/lib/entityValidationSchemas.ts
@@ -101,18 +101,10 @@ export const companyValidationSchema = z.object({
company_type: z.enum(['manufacturer', 'designer', 'operator', 'property_owner']),
description: z.string().max(2000, 'Description must be less than 2000 characters').optional(),
person_type: z.enum(['company', 'individual', 'firm', 'organization']),
- founded_year: z.string()
- .optional()
- .transform(val => {
- if (!val || val.trim() === '') return undefined;
- const num = Number(val);
- return isNaN(num) ? undefined : num;
- })
- .refine(val => val === undefined || (typeof val === 'number' && val >= 1800 && val <= currentYear), {
- message: `Founded year must be between 1800 and ${currentYear}`
- }),
+ // Support both founded_date (preferred) and founded_year (legacy)
founded_date: z.string().optional(),
founded_date_precision: z.enum(['day', 'month', 'year']).optional(),
+ founded_year: z.number().int().min(1800).max(currentYear).optional().nullable(),
headquarters_location: z.string().max(200, 'Location must be less than 200 characters').optional(),
website_url: z.string().optional().refine((val) => {
if (!val || val === '') return true;
diff --git a/src/lib/submissionChangeDetection.ts b/src/lib/submissionChangeDetection.ts
index e51b93ee..50addf49 100644
--- a/src/lib/submissionChangeDetection.ts
+++ b/src/lib/submissionChangeDetection.ts
@@ -8,6 +8,9 @@ export interface FieldChange {
changeType: 'added' | 'removed' | 'modified';
metadata?: {
isCreatingNewLocation?: boolean;
+ precision?: 'day' | 'month' | 'year';
+ oldPrecision?: 'day' | 'month' | 'year';
+ newPrecision?: 'day' | 'month' | 'year';
};
}
@@ -234,28 +237,32 @@ export async function detectChanges(
// Check for changes
if (!isEqual(oldValue, newValue)) {
- if (oldEmpty && !newEmpty) {
- fieldChanges.push({
- field: key,
- oldValue,
- newValue,
- changeType: 'added',
- });
- } else if (newEmpty && !oldEmpty) {
- fieldChanges.push({
- field: key,
- oldValue,
- newValue,
- changeType: 'removed',
- });
- } else {
- fieldChanges.push({
- field: key,
- oldValue,
- newValue,
- changeType: 'modified',
- });
+ const fieldChange: FieldChange = {
+ field: key,
+ oldValue,
+ newValue,
+ changeType: oldEmpty && !newEmpty ? 'added' :
+ newEmpty && !oldEmpty ? 'removed' :
+ 'modified',
+ };
+
+ // Add precision metadata for date fields
+ if (key.endsWith('_date') && !key.endsWith('_precision')) {
+ const precisionKey = `${key}_precision`;
+ const newPrecision = itemData[precisionKey];
+ const oldPrecision = originalData[precisionKey];
+
+ if (newPrecision || oldPrecision) {
+ fieldChange.metadata = {
+ ...fieldChange.metadata,
+ precision: newPrecision || oldPrecision,
+ oldPrecision,
+ newPrecision,
+ };
+ }
}
+
+ fieldChanges.push(fieldChange);
}
});
@@ -490,14 +497,23 @@ function formatEntityType(entityType: string): string {
/**
* Format field value for display
*/
-export function formatFieldValue(value: any): string {
+export function formatFieldValue(value: any, precision?: 'day' | 'month' | 'year'): string {
if (value === null || value === undefined) return 'None';
if (typeof value === 'boolean') return value ? 'Yes' : 'No';
- // Handle dates
+ // Handle dates with precision support
if (value instanceof Date || (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}/.test(value))) {
try {
const date = new Date(value);
+
+ // Apply precision if provided
+ if (precision === 'year') {
+ return date.getFullYear().toString();
+ } else if (precision === 'month') {
+ return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long' });
+ }
+
+ // Default: full date
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
} catch {
return String(value);
@@ -525,6 +541,15 @@ export function formatFieldValue(value: any): string {
return entries.map(([k, v]) => `${k}: ${v}`).join(', ');
}
+ // Handle year-like numbers (prevent comma formatting for founded_year)
+ if (typeof value === 'number') {
+ const currentYear = new Date().getFullYear();
+ if (value >= 1800 && value <= currentYear + 10) {
+ return value.toString(); // Don't add commas for year values
+ }
+ return value.toLocaleString(); // Add commas for other numbers
+ }
+
// Handle URLs
if (typeof value === 'string' && value.startsWith('http')) {
try {