mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 04:31:13 -05:00
Fix ESLint errors
This commit is contained in:
@@ -66,7 +66,7 @@ export function analyzeHeuristics(userAgent: string, headers: Record<string, str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Version number patterns typical of bots (e.g., "v1.0", "version/2.3")
|
// Version number patterns typical of bots (e.g., "v1.0", "version/2.3")
|
||||||
if (userAgent.match(/\b(v|version)[\/\s]?\d+\.\d+/i)) {
|
if (userAgent.match(/\b(v|version)[/\s]?\d+\.\d+/i)) {
|
||||||
signals.push('version-pattern');
|
signals.push('version-pattern');
|
||||||
confidence += 10;
|
confidence += 10;
|
||||||
}
|
}
|
||||||
|
|||||||
10
api/ssrOG.ts
10
api/ssrOG.ts
@@ -5,12 +5,12 @@ import { join } from 'path';
|
|||||||
type VercelRequest = IncomingMessage & {
|
type VercelRequest = IncomingMessage & {
|
||||||
query: { [key: string]: string | string[] };
|
query: { [key: string]: string | string[] };
|
||||||
cookies: { [key: string]: string };
|
cookies: { [key: string]: string };
|
||||||
body: any;
|
body: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
type VercelResponse = ServerResponse & {
|
type VercelResponse = ServerResponse & {
|
||||||
status: (code: number) => VercelResponse;
|
status: (code: number) => VercelResponse;
|
||||||
json: (data: any) => VercelResponse;
|
json: (data: unknown) => VercelResponse;
|
||||||
send: (body: string) => VercelResponse;
|
send: (body: string) => VercelResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ async function getPageData(pathname: string, fullUrl: string): Promise<PageData>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Individual ride page: /parks/{park-slug}/rides/{ride-slug}
|
// Individual ride page: /parks/{park-slug}/rides/{ride-slug}
|
||||||
if (normalizedPath.match(/^\/parks\/[^\/]+\/rides\/[^\/]+$/)) {
|
if (normalizedPath.match(/^\/parks\/[^/]+\/rides\/[^/]+$/)) {
|
||||||
const parts = normalizedPath.split('/');
|
const parts = normalizedPath.split('/');
|
||||||
const rideSlug = parts[4];
|
const rideSlug = parts[4];
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ function injectOGTags(html: string, ogTags: string): string {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
export default async function handler(req: VercelRequest, res: VercelResponse): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const userAgent = req.headers['user-agent'] || '';
|
const userAgent = req.headers['user-agent'] || '';
|
||||||
const fullUrl = `https://${req.headers.host}${req.url}`;
|
const fullUrl = `https://${req.headers.host}${req.url}`;
|
||||||
@@ -239,7 +239,7 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||||||
const html = readFileSync(htmlPath, 'utf-8');
|
const html = readFileSync(htmlPath, 'utf-8');
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||||
res.status(200).send(html);
|
res.status(200).send(html);
|
||||||
} catch (fallbackError) {
|
} catch {
|
||||||
res.status(500).send('Internal Server Error');
|
res.status(500).send('Internal Server Error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ const queryClient = new QueryClient({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent(): React.JSX.Element {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
@@ -154,7 +154,7 @@ function AppContent() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const App = () => (
|
const App = (): React.JSX.Element => (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<AuthModalProvider>
|
<AuthModalProvider>
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ export function AdminPageLayout({
|
|||||||
getAdminPanelPollInterval,
|
getAdminPanelPollInterval,
|
||||||
} = useAdminSettings();
|
} = useAdminSettings();
|
||||||
|
|
||||||
const refreshMode = getAdminPanelRefreshMode();
|
const refreshMode = getAdminPanelRefreshMode() as 'auto' | 'manual';
|
||||||
const pollInterval = getAdminPanelPollInterval();
|
const pollInterval = getAdminPanelPollInterval() as number;
|
||||||
|
|
||||||
const { lastUpdated } = useModerationStats({
|
const { lastUpdated } = useModerationStats({
|
||||||
enabled: isAuthorized && showRefreshControls,
|
enabled: isAuthorized && showRefreshControls,
|
||||||
@@ -84,9 +84,9 @@ export function AdminPageLayout({
|
|||||||
return (
|
return (
|
||||||
<AdminLayout
|
<AdminLayout
|
||||||
onRefresh={showRefreshControls ? handleRefreshClick : undefined}
|
onRefresh={showRefreshControls ? handleRefreshClick : undefined}
|
||||||
refreshMode={showRefreshControls ? refreshMode : undefined}
|
refreshMode={showRefreshControls ? (refreshMode as 'auto' | 'manual') : undefined}
|
||||||
pollInterval={showRefreshControls ? pollInterval : undefined}
|
pollInterval={showRefreshControls ? pollInterval : undefined}
|
||||||
lastUpdated={showRefreshControls ? lastUpdated : undefined}
|
lastUpdated={showRefreshControls ? (lastUpdated as Date) : undefined}
|
||||||
>
|
>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
@@ -117,9 +117,9 @@ export function AdminPageLayout({
|
|||||||
return (
|
return (
|
||||||
<AdminLayout
|
<AdminLayout
|
||||||
onRefresh={showRefreshControls ? handleRefreshClick : undefined}
|
onRefresh={showRefreshControls ? handleRefreshClick : undefined}
|
||||||
refreshMode={showRefreshControls ? refreshMode : undefined}
|
refreshMode={showRefreshControls ? (refreshMode as 'auto' | 'manual') : undefined}
|
||||||
pollInterval={showRefreshControls ? pollInterval : undefined}
|
pollInterval={showRefreshControls ? pollInterval : undefined}
|
||||||
lastUpdated={showRefreshControls ? lastUpdated : undefined}
|
lastUpdated={showRefreshControls ? (lastUpdated as Date) : undefined}
|
||||||
>
|
>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||||
import { getErrorMessage } from '@/lib/errorHandler';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -15,36 +13,12 @@ import { SlugField } from '@/components/ui/slug-field';
|
|||||||
import { Ruler, Save, X } from 'lucide-react';
|
import { Ruler, Save, X } from 'lucide-react';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { HeadquartersLocationInput } from './HeadquartersLocationInput';
|
import { HeadquartersLocationInput } from './HeadquartersLocationInput';
|
||||||
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
import { EntityMultiImageUploader } 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 { useAuth } from '@/hooks/useAuth';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { handleError } from '@/lib/errorHandler';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import type { UploadedImage } from '@/types/company';
|
import type { UploadedImage } from '@/types/company';
|
||||||
|
|
||||||
// Raw form input state (before Zod transformation)
|
|
||||||
interface DesignerFormInput {
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
company_type: 'designer' | 'manufacturer' | 'operator' | 'property_owner';
|
|
||||||
description?: string;
|
|
||||||
person_type: 'company' | 'individual' | 'firm' | 'organization';
|
|
||||||
founded_year?: string;
|
|
||||||
founded_date?: string;
|
|
||||||
founded_date_precision?: 'day' | 'month' | 'year';
|
|
||||||
headquarters_location?: string;
|
|
||||||
website_url?: string;
|
|
||||||
source_url?: string;
|
|
||||||
submission_notes?: string;
|
|
||||||
images?: {
|
|
||||||
uploaded: UploadedImage[];
|
|
||||||
banner_assignment?: number | null;
|
|
||||||
card_assignment?: number | null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zod output type (after transformation)
|
// Zod output type (after transformation)
|
||||||
type DesignerFormData = z.infer<typeof entitySchemas.designer>;
|
type DesignerFormData = z.infer<typeof entitySchemas.designer>;
|
||||||
|
|
||||||
@@ -58,10 +32,9 @@ interface DesignerFormProps {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormProps) {
|
export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormProps): React.JSX.Element {
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function HeadquartersLocationInput({
|
|||||||
onChange,
|
onChange,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
className
|
className
|
||||||
}: HeadquartersLocationInputProps) {
|
}: HeadquartersLocationInputProps): React.JSX.Element {
|
||||||
const [mode, setMode] = useState<'search' | 'manual'>('search');
|
const [mode, setMode] = useState<'search' | 'manual'>('search');
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [results, setResults] = useState<LocationResult[]>([]);
|
const [results, setResults] = useState<LocationResult[]>([]);
|
||||||
@@ -44,7 +44,7 @@ export function HeadquartersLocationInput({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeoutId = setTimeout(async () => {
|
const timeoutId = setTimeout(async (): Promise<void> => {
|
||||||
setIsSearching(true);
|
setIsSearching(true);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -59,7 +59,7 @@ export function HeadquartersLocationInput({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json() as LocationResult[];
|
||||||
setResults(data);
|
setResults(data);
|
||||||
setShowResults(true);
|
setShowResults(true);
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ export function HeadquartersLocationInput({
|
|||||||
return result.display_name;
|
return result.display_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectLocation = (result: LocationResult) => {
|
const handleSelectLocation = (result: LocationResult): void => {
|
||||||
const formatted = formatLocation(result);
|
const formatted = formatLocation(result);
|
||||||
onChange(formatted);
|
onChange(formatted);
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
@@ -95,7 +95,7 @@ export function HeadquartersLocationInput({
|
|||||||
setResults([]);
|
setResults([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = (): void => {
|
||||||
onChange('');
|
onChange('');
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
setResults([]);
|
setResults([]);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ interface LocationSearchProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LocationSearch({ onLocationSelect, initialLocationId, className }: LocationSearchProps) {
|
export function LocationSearch({ onLocationSelect, initialLocationId, className }: LocationSearchProps): React.JSX.Element {
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [results, setResults] = useState<LocationResult[]>([]);
|
const [results, setResults] = useState<LocationResult[]>([]);
|
||||||
const [isSearching, setIsSearching] = useState(false);
|
const [isSearching, setIsSearching] = useState(false);
|
||||||
@@ -54,11 +54,11 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
|||||||
// Load initial location if editing
|
// Load initial location if editing
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialLocationId) {
|
if (initialLocationId) {
|
||||||
loadInitialLocation(initialLocationId);
|
void loadInitialLocation(initialLocationId);
|
||||||
}
|
}
|
||||||
}, [initialLocationId]);
|
}, [initialLocationId]);
|
||||||
|
|
||||||
const loadInitialLocation = async (locationId: string) => {
|
const loadInitialLocation = async (locationId: string): Promise<void> => {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('locations')
|
.from('locations')
|
||||||
.select('id, name, city, state_province, country, postal_code, latitude, longitude, timezone')
|
.select('id, name, city, state_province, country, postal_code, latitude, longitude, timezone')
|
||||||
@@ -119,11 +119,11 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json() as LocationResult[];
|
||||||
setResults(data);
|
setResults(data);
|
||||||
setShowResults(true);
|
setShowResults(true);
|
||||||
setSearchError(null);
|
setSearchError(null);
|
||||||
} catch (error: unknown) {
|
} catch {
|
||||||
logger.error('Location search failed', { query: searchQuery });
|
logger.error('Location search failed', { query: searchQuery });
|
||||||
setSearchError('Failed to search locations. Please check your connection.');
|
setSearchError('Failed to search locations. Please check your connection.');
|
||||||
setResults([]);
|
setResults([]);
|
||||||
@@ -135,14 +135,15 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (debouncedSearch) {
|
if (debouncedSearch) {
|
||||||
searchLocations(debouncedSearch);
|
void searchLocations(debouncedSearch);
|
||||||
} else {
|
} else {
|
||||||
setResults([]);
|
setResults([]);
|
||||||
setShowResults(false);
|
setShowResults(false);
|
||||||
}
|
}
|
||||||
}, [debouncedSearch, searchLocations]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
const handleSelectResult = async (result: LocationResult) => {
|
const handleSelectResult = (result: LocationResult): void => {
|
||||||
const latitude = parseFloat(result.lat);
|
const latitude = parseFloat(result.lat);
|
||||||
const longitude = parseFloat(result.lon);
|
const longitude = parseFloat(result.lon);
|
||||||
|
|
||||||
@@ -176,7 +177,7 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
|||||||
onLocationSelect(locationData);
|
onLocationSelect(locationData);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = (): void => {
|
||||||
setSelectedLocation(null);
|
setSelectedLocation(null);
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
setResults([]);
|
setResults([]);
|
||||||
@@ -214,7 +215,7 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
key={result.place_id}
|
key={result.place_id}
|
||||||
onClick={() => handleSelectResult(result)}
|
onClick={() => void handleSelectResult(result)}
|
||||||
className="w-full text-left p-3 hover:bg-accent transition-colors"
|
className="w-full text-left p-3 hover:bg-accent transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||||
import { getErrorMessage } from '@/lib/errorHandler';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -15,37 +13,14 @@ import { SlugField } from '@/components/ui/slug-field';
|
|||||||
import { Building2, Save, X } from 'lucide-react';
|
import { Building2, Save, X } from 'lucide-react';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { HeadquartersLocationInput } from './HeadquartersLocationInput';
|
import { HeadquartersLocationInput } from './HeadquartersLocationInput';
|
||||||
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
import { EntityMultiImageUploader } from '@/components/upload/EntityMultiImageUploader';
|
||||||
import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input';
|
import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input';
|
||||||
import { submitManufacturerCreation, submitManufacturerUpdate } from '@/lib/entitySubmissionHelpers';
|
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { handleError } from '@/lib/errorHandler';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { toDateOnly } from '@/lib/dateUtils';
|
import { toDateOnly } from '@/lib/dateUtils';
|
||||||
import type { UploadedImage } from '@/types/company';
|
import type { UploadedImage } from '@/types/company';
|
||||||
|
|
||||||
// Raw form input state (before Zod transformation)
|
|
||||||
interface ManufacturerFormInput {
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
company_type: 'designer' | 'manufacturer' | 'operator' | 'property_owner';
|
|
||||||
description?: string;
|
|
||||||
person_type: 'company' | 'individual' | 'firm' | 'organization';
|
|
||||||
founded_year?: string;
|
|
||||||
founded_date?: string;
|
|
||||||
founded_date_precision?: 'day' | 'month' | 'year';
|
|
||||||
headquarters_location?: string;
|
|
||||||
website_url?: string;
|
|
||||||
source_url?: string;
|
|
||||||
submission_notes?: string;
|
|
||||||
images?: {
|
|
||||||
uploaded: UploadedImage[];
|
|
||||||
banner_assignment?: number | null;
|
|
||||||
card_assignment?: number | null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zod output type (after transformation)
|
// Zod output type (after transformation)
|
||||||
type ManufacturerFormData = z.infer<typeof entitySchemas.manufacturer>;
|
type ManufacturerFormData = z.infer<typeof entitySchemas.manufacturer>;
|
||||||
|
|
||||||
@@ -59,10 +34,9 @@ interface ManufacturerFormProps {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ManufacturerForm({ onSubmit, onCancel, initialData }: ManufacturerFormProps) {
|
export function ManufacturerForm({ onSubmit, onCancel, initialData }: ManufacturerFormProps): React.JSX.Element {
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -112,7 +86,7 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur
|
|||||||
founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined,
|
founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
await onSubmit(formData);
|
onSubmit(formData);
|
||||||
|
|
||||||
// Only show success toast and close if not editing through moderation queue
|
// Only show success toast and close if not editing through moderation queue
|
||||||
if (!initialData?.id) {
|
if (!initialData?.id) {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function MarkdownEditor({
|
|||||||
autoSave = false,
|
autoSave = false,
|
||||||
height = 600,
|
height = 600,
|
||||||
placeholder = 'Write your content in markdown...'
|
placeholder = 'Write your content in markdown...'
|
||||||
}: MarkdownEditorProps) {
|
}: MarkdownEditorProps): React.JSX.Element {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light');
|
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light');
|
||||||
@@ -66,7 +66,7 @@ export function MarkdownEditor({
|
|||||||
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
setResolvedTheme(isDark ? 'dark' : 'light');
|
setResolvedTheme(isDark ? 'dark' : 'light');
|
||||||
} else {
|
} else {
|
||||||
setResolvedTheme(theme as 'light' | 'dark');
|
setResolvedTheme(theme);
|
||||||
}
|
}
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ export function MarkdownEditor({
|
|||||||
if (theme !== 'system') return;
|
if (theme !== 'system') return;
|
||||||
|
|
||||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
const handler = (e: MediaQueryListEvent) => {
|
const handler = (e: MediaQueryListEvent): void => {
|
||||||
setResolvedTheme(e.matches ? 'dark' : 'light');
|
setResolvedTheme(e.matches ? 'dark' : 'light');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ export function MarkdownEditor({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLastSavedText = () => {
|
const getLastSavedText = (): string | null => {
|
||||||
if (!lastSaved) return null;
|
if (!lastSaved) return null;
|
||||||
const seconds = Math.floor((Date.now() - lastSaved.getTime()) / 1000);
|
const seconds = Math.floor((Date.now() - lastSaved.getTime()) / 1000);
|
||||||
if (seconds < 60) return `Saved ${seconds}s ago`;
|
if (seconds < 60) return `Saved ${seconds}s ago`;
|
||||||
@@ -138,7 +138,7 @@ export function MarkdownEditor({
|
|||||||
linkPlugin(),
|
linkPlugin(),
|
||||||
linkDialogPlugin(),
|
linkDialogPlugin(),
|
||||||
imagePlugin({
|
imagePlugin({
|
||||||
imageUploadHandler: async (file: File) => {
|
imageUploadHandler: async (file: File): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
@@ -152,7 +152,7 @@ export function MarkdownEditor({
|
|||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
||||||
// Return CloudFlare imagedelivery.net URL
|
// Return CloudFlare imagedelivery.net URL
|
||||||
const imageUrl = getCloudflareImageUrl(data.id, 'public');
|
const imageUrl = getCloudflareImageUrl((data as { id: string }).id, 'public');
|
||||||
if (!imageUrl) throw new Error('Failed to generate image URL');
|
if (!imageUrl) throw new Error('Failed to generate image URL');
|
||||||
|
|
||||||
return imageUrl;
|
return imageUrl;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export interface MarkdownEditorProps {
|
|||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MarkdownEditorLazy(props: MarkdownEditorProps) {
|
export function MarkdownEditorLazy(props: MarkdownEditorProps): React.JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<EditorSkeleton />}>
|
<Suspense fallback={<EditorSkeleton />}>
|
||||||
<MarkdownEditor {...props} />
|
<MarkdownEditor {...props} />
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ interface MigrationResult {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NovuMigrationUtility() {
|
export function NovuMigrationUtility(): React.JSX.Element {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [isRunning, setIsRunning] = useState(false);
|
const [isRunning, setIsRunning] = useState(false);
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
const [results, setResults] = useState<MigrationResult[]>([]);
|
const [results, setResults] = useState<MigrationResult[]>([]);
|
||||||
const [totalUsers, setTotalUsers] = useState(0);
|
const [totalUsers, setTotalUsers] = useState(0);
|
||||||
|
|
||||||
const runMigration = async () => {
|
const runMigration = async (): Promise<void> => {
|
||||||
setIsRunning(true);
|
setIsRunning(true);
|
||||||
setResults([]);
|
setResults([]);
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
@@ -35,7 +35,7 @@ export function NovuMigrationUtility() {
|
|||||||
throw new Error('You must be logged in to run the migration');
|
throw new Error('You must be logged in to run the migration');
|
||||||
}
|
}
|
||||||
|
|
||||||
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL || 'https://api.thrillwiki.com';
|
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL as string || 'https://api.thrillwiki.com';
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${supabaseUrl}/functions/v1/migrate-novu-users`,
|
`${supabaseUrl}/functions/v1/migrate-novu-users`,
|
||||||
{
|
{
|
||||||
@@ -47,7 +47,7 @@ export function NovuMigrationUtility() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json() as { success: boolean; error?: string; results?: MigrationResult[]; total?: number };
|
||||||
|
|
||||||
if (!response.ok || !data.success) {
|
if (!response.ok || !data.success) {
|
||||||
throw new Error(data.error || 'Migration failed');
|
throw new Error(data.error || 'Migration failed');
|
||||||
@@ -62,12 +62,12 @@ export function NovuMigrationUtility() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTotalUsers(data.total);
|
setTotalUsers(data.total ?? 0);
|
||||||
setResults(data.results);
|
setResults(data.results ?? []);
|
||||||
setProgress(100);
|
setProgress(100);
|
||||||
|
|
||||||
const successCount = data.results.filter((r: MigrationResult) => r.success).length;
|
const successCount = (data.results ?? []).filter((r: MigrationResult) => r.success).length;
|
||||||
const failureCount = data.results.filter((r: MigrationResult) => !r.success).length;
|
const failureCount = (data.results ?? []).length - successCount;
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Migration completed",
|
title: "Migration completed",
|
||||||
@@ -106,7 +106,7 @@ export function NovuMigrationUtility() {
|
|||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={runMigration}
|
onClick={() => void runMigration()}
|
||||||
disabled={isRunning}
|
disabled={isRunning}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||||
import { getErrorMessage } from '@/lib/errorHandler';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -15,36 +13,12 @@ import { SlugField } from '@/components/ui/slug-field';
|
|||||||
import { FerrisWheel, Save, X } from 'lucide-react';
|
import { FerrisWheel, Save, X } from 'lucide-react';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { HeadquartersLocationInput } from './HeadquartersLocationInput';
|
import { HeadquartersLocationInput } from './HeadquartersLocationInput';
|
||||||
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
import { EntityMultiImageUploader } 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 { useAuth } from '@/hooks/useAuth';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { handleError } from '@/lib/errorHandler';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import type { UploadedImage } from '@/types/company';
|
import type { UploadedImage } from '@/types/company';
|
||||||
|
|
||||||
// Raw form input state (before Zod transformation)
|
|
||||||
interface OperatorFormInput {
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
company_type: 'designer' | 'manufacturer' | 'operator' | 'property_owner';
|
|
||||||
description?: string;
|
|
||||||
person_type: 'company' | 'individual' | 'firm' | 'organization';
|
|
||||||
founded_year?: string;
|
|
||||||
founded_date?: string;
|
|
||||||
founded_date_precision?: 'day' | 'month' | 'year';
|
|
||||||
headquarters_location?: string;
|
|
||||||
website_url?: string;
|
|
||||||
source_url?: string;
|
|
||||||
submission_notes?: string;
|
|
||||||
images?: {
|
|
||||||
uploaded: UploadedImage[];
|
|
||||||
banner_assignment?: number | null;
|
|
||||||
card_assignment?: number | null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zod output type (after transformation)
|
// Zod output type (after transformation)
|
||||||
type OperatorFormData = z.infer<typeof entitySchemas.operator>;
|
type OperatorFormData = z.infer<typeof entitySchemas.operator>;
|
||||||
|
|
||||||
@@ -58,10 +32,9 @@ interface OperatorFormProps {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormProps) {
|
export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormProps): React.JSX.Element {
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -109,7 +82,7 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr
|
|||||||
founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined,
|
founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
await onSubmit(formData);
|
onSubmit(formData);
|
||||||
|
|
||||||
// Only show success toast and close if not editing through moderation queue
|
// Only show success toast and close if not editing through moderation queue
|
||||||
if (!initialData?.id) {
|
if (!initialData?.id) {
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ import { format } from 'date-fns';
|
|||||||
import { handleError } from '@/lib/errorHandler';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
import { AuditLogEntry } from '@/types/database';
|
import { AuditLogEntry } from '@/types/database';
|
||||||
|
|
||||||
export function ProfileAuditLog() {
|
export function ProfileAuditLog(): React.JSX.Element {
|
||||||
const [logs, setLogs] = useState<AuditLogEntry[]>([]);
|
const [logs, setLogs] = useState<AuditLogEntry[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAuditLogs();
|
void fetchAuditLogs();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const fetchAuditLogs = async () => {
|
const fetchAuditLogs = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('profile_audit_log')
|
.from('profile_audit_log')
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
import { entitySchemas } from '@/lib/entityValidationSchemas';
|
||||||
import { getErrorMessage } from '@/lib/errorHandler';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -15,36 +13,12 @@ import { SlugField } from '@/components/ui/slug-field';
|
|||||||
import { Building2, Save, X } from 'lucide-react';
|
import { Building2, Save, X } from 'lucide-react';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { HeadquartersLocationInput } from './HeadquartersLocationInput';
|
import { HeadquartersLocationInput } from './HeadquartersLocationInput';
|
||||||
import { EntityMultiImageUploader, ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
|
import { EntityMultiImageUploader } 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 { useAuth } from '@/hooks/useAuth';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { handleError } from '@/lib/errorHandler';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import type { UploadedImage } from '@/types/company';
|
import type { UploadedImage } from '@/types/company';
|
||||||
|
|
||||||
// Raw form input state (before Zod transformation)
|
|
||||||
interface PropertyOwnerFormInput {
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
company_type: 'designer' | 'manufacturer' | 'operator' | 'property_owner';
|
|
||||||
description?: string;
|
|
||||||
person_type: 'company' | 'individual' | 'firm' | 'organization';
|
|
||||||
founded_year?: string;
|
|
||||||
founded_date?: string;
|
|
||||||
founded_date_precision?: 'day' | 'month' | 'year';
|
|
||||||
headquarters_location?: string;
|
|
||||||
website_url?: string;
|
|
||||||
source_url?: string;
|
|
||||||
submission_notes?: string;
|
|
||||||
images?: {
|
|
||||||
uploaded: UploadedImage[];
|
|
||||||
banner_assignment?: number | null;
|
|
||||||
card_assignment?: number | null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zod output type (after transformation)
|
// Zod output type (after transformation)
|
||||||
type PropertyOwnerFormData = z.infer<typeof entitySchemas.property_owner>;
|
type PropertyOwnerFormData = z.infer<typeof entitySchemas.property_owner>;
|
||||||
|
|
||||||
@@ -58,10 +32,9 @@ interface PropertyOwnerFormProps {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyOwnerFormProps) {
|
export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyOwnerFormProps): React.JSX.Element {
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -109,7 +82,7 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO
|
|||||||
founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined,
|
founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
await onSubmit(formData);
|
onSubmit(formData);
|
||||||
|
|
||||||
// Only show success toast and close if not editing through moderation queue
|
// Only show success toast and close if not editing through moderation queue
|
||||||
if (!initialData?.id) {
|
if (!initialData?.id) {
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ const activityTypeConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const SystemActivityLog = forwardRef<SystemActivityLogRef, SystemActivityLogProps>(
|
export const SystemActivityLog = forwardRef<SystemActivityLogRef, SystemActivityLogProps>(
|
||||||
({ limit = 50, showFilters = true }, ref) => {
|
({ limit = 50, showFilters = true }, ref): React.JSX.Element => {
|
||||||
const [activities, setActivities] = useState<SystemActivity[]>([]);
|
const [activities, setActivities] = useState<SystemActivity[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
@@ -182,7 +182,7 @@ export const SystemActivityLog = forwardRef<SystemActivityLogRef, SystemActivity
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [showFiltersPanel, setShowFiltersPanel] = useState(false);
|
const [showFiltersPanel, setShowFiltersPanel] = useState(false);
|
||||||
|
|
||||||
const loadActivities = async (showLoader = true) => {
|
const loadActivities = async (showLoader = true): Promise<void> => {
|
||||||
if (showLoader) {
|
if (showLoader) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -201,19 +201,20 @@ export const SystemActivityLog = forwardRef<SystemActivityLogRef, SystemActivity
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = (): void => {
|
||||||
loadActivities(false);
|
void loadActivities(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadActivities();
|
void loadActivities();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [limit, filterType]);
|
}, [limit, filterType]);
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
refresh: loadActivities,
|
refresh: loadActivities,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const toggleExpanded = (id: string) => {
|
const toggleExpanded = (id: string): void => {
|
||||||
setExpandedIds(prev => {
|
setExpandedIds(prev => {
|
||||||
const next = new Set(prev);
|
const next = new Set(prev);
|
||||||
if (next.has(id)) {
|
if (next.has(id)) {
|
||||||
@@ -225,7 +226,7 @@ export const SystemActivityLog = forwardRef<SystemActivityLogRef, SystemActivity
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearFilters = () => {
|
const clearFilters = (): void => {
|
||||||
setFilterType('all');
|
setFilterType('all');
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
};
|
};
|
||||||
@@ -258,7 +259,7 @@ export const SystemActivityLog = forwardRef<SystemActivityLogRef, SystemActivity
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderActivityDetails = (activity: SystemActivity) => {
|
const renderActivityDetails = (activity: SystemActivity): React.JSX.Element => {
|
||||||
const isExpanded = expandedIds.has(activity.id);
|
const isExpanded = expandedIds.has(activity.id);
|
||||||
|
|
||||||
switch (activity.type) {
|
switch (activity.type) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ interface TestDataResults {
|
|||||||
time?: string;
|
time?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TestDataGenerator() {
|
export function TestDataGenerator(): React.JSX.Element {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [preset, setPreset] = useState<'small' | 'medium' | 'large' | 'stress'>('small');
|
const [preset, setPreset] = useState<'small' | 'medium' | 'large' | 'stress'>('small');
|
||||||
const [fieldDensity, setFieldDensity] = useState<'mixed' | 'minimal' | 'standard' | 'maximum'>('mixed');
|
const [fieldDensity, setFieldDensity] = useState<'mixed' | 'minimal' | 'standard' | 'maximum'>('mixed');
|
||||||
@@ -78,14 +78,14 @@ export function TestDataGenerator() {
|
|||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
const selectedEntityTypes = Object.entries(entityTypes)
|
const selectedEntityTypes = Object.entries(entityTypes)
|
||||||
.filter(([_, enabled]) => enabled)
|
.filter(([, enabled]) => enabled)
|
||||||
.map(([type]) => type);
|
.map(([type]) => type);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadStats();
|
void loadStats();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadStats = async () => {
|
const loadStats = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const data = await getTestDataStats();
|
const data = await getTestDataStats();
|
||||||
setStats(data);
|
setStats(data);
|
||||||
@@ -94,7 +94,7 @@ export function TestDataGenerator() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGenerate = async () => {
|
const handleGenerate = async (): Promise<void> => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setResults(null);
|
setResults(null);
|
||||||
|
|
||||||
@@ -137,9 +137,10 @@ export function TestDataGenerator() {
|
|||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
||||||
// Merge results
|
// Merge results
|
||||||
Object.keys(data.summary).forEach(key => {
|
const summary = data.summary as Record<string, number>;
|
||||||
|
Object.keys(summary).forEach(key => {
|
||||||
if (allResults[key as keyof typeof allResults] !== undefined) {
|
if (allResults[key as keyof typeof allResults] !== undefined) {
|
||||||
allResults[key as keyof typeof allResults] += data.summary[key];
|
allResults[key as keyof typeof allResults] += summary[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -162,7 +163,7 @@ export function TestDataGenerator() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClear = async () => {
|
const handleClear = async (): Promise<void> => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -273,7 +274,7 @@ export function TestDataGenerator() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-base font-semibold">Preset</Label>
|
<Label className="text-base font-semibold">Preset</Label>
|
||||||
<RadioGroup value={preset} onValueChange={(v: any) => setPreset(v)} className="mt-2 space-y-3">
|
<RadioGroup value={preset} onValueChange={(v: string) => setPreset(v as 'small' | 'medium' | 'large' | 'stress')} className="mt-2 space-y-3">
|
||||||
{Object.entries(PRESETS).map(([key, { label, description, counts }]) => (
|
{Object.entries(PRESETS).map(([key, { label, description, counts }]) => (
|
||||||
<div key={key} className="flex items-start space-x-2">
|
<div key={key} className="flex items-start space-x-2">
|
||||||
<RadioGroupItem value={key} id={key} className="mt-1" />
|
<RadioGroupItem value={key} id={key} className="mt-1" />
|
||||||
@@ -292,7 +293,7 @@ export function TestDataGenerator() {
|
|||||||
<p className="text-sm text-muted-foreground mt-1 mb-3">
|
<p className="text-sm text-muted-foreground mt-1 mb-3">
|
||||||
Controls how many optional fields are populated in generated entities
|
Controls how many optional fields are populated in generated entities
|
||||||
</p>
|
</p>
|
||||||
<RadioGroup value={fieldDensity} onValueChange={(v: any) => setFieldDensity(v)} className="space-y-2">
|
<RadioGroup value={fieldDensity} onValueChange={(v: string) => setFieldDensity(v as 'mixed' | 'minimal' | 'standard' | 'maximum')} className="space-y-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<RadioGroupItem value="mixed" id="mixed" />
|
<RadioGroupItem value="mixed" id="mixed" />
|
||||||
<Label htmlFor="mixed" className="cursor-pointer">
|
<Label htmlFor="mixed" className="cursor-pointer">
|
||||||
|
|||||||
Reference in New Issue
Block a user