From 6e553b23f6d20bbe7e6c2aa25c8cd2a4a51692c9 Mon Sep 17 00:00:00 2001 From: Connor Yoh Date: Thu, 28 Aug 2025 15:25:33 +0100 Subject: [PATCH] TypeId --- .../src/components/shared/TopControls.tsx | 2 +- .../tools/automate/AutomationCreation.tsx | 4 +-- .../tools/automate/AutomationRun.tsx | 2 +- .../tools/automate/ToolConfigurationModal.tsx | 2 +- frontend/src/contexts/NavigationContext.tsx | 10 +++++--- frontend/src/contexts/ToolWorkflowContext.tsx | 12 +++++---- frontend/src/data/toolsTaxonomy.ts | 10 ++++---- .../src/data/useTranslatedToolRegistry.tsx | 18 ++++++------- .../hooks/tools/automate/useAutomationForm.ts | 12 ++++----- frontend/src/hooks/useSuggestedTools.ts | 5 ++-- frontend/src/hooks/useToolManagement.tsx | 2 +- frontend/src/hooks/useUrlSync.ts | 4 +-- frontend/src/tools/Automate.tsx | 2 +- frontend/src/types/navigation.ts | 16 ++---------- frontend/src/types/navigationActions.ts | 3 ++- frontend/src/types/toolId.ts | 25 +++++++++++++++++++ frontend/src/types/workbench.ts | 12 +++++++++ frontend/src/utils/automationExecutor.ts | 2 +- frontend/src/utils/urlMapping.ts | 16 ++++++------ frontend/src/utils/urlRouting.ts | 14 +++++------ 20 files changed, 101 insertions(+), 72 deletions(-) create mode 100644 frontend/src/types/toolId.ts create mode 100644 frontend/src/types/workbench.ts diff --git a/frontend/src/components/shared/TopControls.tsx b/frontend/src/components/shared/TopControls.tsx index b5f5ff629..489468eb2 100644 --- a/frontend/src/components/shared/TopControls.tsx +++ b/frontend/src/components/shared/TopControls.tsx @@ -5,7 +5,7 @@ import rainbowStyles from '../../styles/rainbow.module.css'; import VisibilityIcon from "@mui/icons-material/Visibility"; import EditNoteIcon from "@mui/icons-material/EditNote"; import FolderIcon from "@mui/icons-material/Folder"; -import { WorkbenchType, isValidWorkbench } from '../../types/navigation'; +import { WorkbenchType, isValidWorkbench } from '../../types/workbench'; import { Tooltip } from "./Tooltip"; const viewOptionStyle = { diff --git a/frontend/src/components/tools/automate/AutomationCreation.tsx b/frontend/src/components/tools/automate/AutomationCreation.tsx index 49e9f6f1b..5dfdc0468 100644 --- a/frontend/src/components/tools/automate/AutomationCreation.tsx +++ b/frontend/src/components/tools/automate/AutomationCreation.tsx @@ -11,7 +11,7 @@ import { Modal } from '@mantine/core'; import CheckIcon from '@mui/icons-material/Check'; -import { ToolRegistryEntry } from '../../../data/toolsTaxonomy'; +import { ToolRegistry } from '../../../data/toolsTaxonomy'; import ToolConfigurationModal from './ToolConfigurationModal'; import ToolList from './ToolList'; import IconSelector from './IconSelector'; @@ -24,7 +24,7 @@ interface AutomationCreationProps { existingAutomation?: AutomationConfig; onBack: () => void; onComplete: (automation: AutomationConfig) => void; - toolRegistry: Record; + toolRegistry: ToolRegistry; } export default function AutomationCreation({ mode, existingAutomation, onBack, onComplete, toolRegistry }: AutomationCreationProps) { diff --git a/frontend/src/components/tools/automate/AutomationRun.tsx b/frontend/src/components/tools/automate/AutomationRun.tsx index 640f802f6..1a3cb1263 100644 --- a/frontend/src/components/tools/automate/AutomationRun.tsx +++ b/frontend/src/components/tools/automate/AutomationRun.tsx @@ -33,7 +33,7 @@ export default function AutomationRun({ automation, onComplete, automateOperatio React.useEffect(() => { if (automation?.operations) { const steps = automation.operations.map((op: any, index: number) => { - const tool = toolRegistry[op.operation]; + const tool = toolRegistry[op.operation as keyof typeof toolRegistry]; return { id: `${op.operation}-${index}`, operation: op.operation, diff --git a/frontend/src/components/tools/automate/ToolConfigurationModal.tsx b/frontend/src/components/tools/automate/ToolConfigurationModal.tsx index d97819fb8..b2aa1c975 100644 --- a/frontend/src/components/tools/automate/ToolConfigurationModal.tsx +++ b/frontend/src/components/tools/automate/ToolConfigurationModal.tsx @@ -35,7 +35,7 @@ export default function ToolConfigurationModal({ opened, tool, onSave, onCancel, const [isValid, setIsValid] = useState(true); // Get tool info from registry - const toolInfo = toolRegistry[tool.operation]; + const toolInfo = toolRegistry[tool.operation as keyof ToolRegistry]; const SettingsComponent = toolInfo?.settingsComponent; // Initialize parameters from tool (which should contain defaults from registry) diff --git a/frontend/src/contexts/NavigationContext.tsx b/frontend/src/contexts/NavigationContext.tsx index 5c3f64e1b..3b65f01d1 100644 --- a/frontend/src/contexts/NavigationContext.tsx +++ b/frontend/src/contexts/NavigationContext.tsx @@ -1,5 +1,6 @@ import React, { createContext, useContext, useReducer, useCallback } from 'react'; -import { WorkbenchType, ToolId, getDefaultWorkbench } from '../types/navigation'; +import { WorkbenchType, getDefaultWorkbench } from '../types/workbench'; +import { ToolId, isValidToolId } from '../types/toolId'; import { useFlatToolRegistry } from '../data/useTranslatedToolRegistry'; /** @@ -167,10 +168,13 @@ export const NavigationProvider: React.FC<{ } // Look up the tool in the registry to get its proper workbench - const tool = toolRegistry[toolId]; + + const tool = isValidToolId(toolId)? toolRegistry[toolId] : null; const workbench = tool ? (tool.workbench || getDefaultWorkbench()) : getDefaultWorkbench(); - dispatch({ type: 'SET_TOOL_AND_WORKBENCH', payload: { toolId, workbench } }); + // Validate toolId and convert to ToolId type + const validToolId = isValidToolId(toolId) ? toolId : null; + dispatch({ type: 'SET_TOOL_AND_WORKBENCH', payload: { toolId: validToolId, workbench } }); }, [toolRegistry]) }; diff --git a/frontend/src/contexts/ToolWorkflowContext.tsx b/frontend/src/contexts/ToolWorkflowContext.tsx index 40c6b7044..4077bac60 100644 --- a/frontend/src/contexts/ToolWorkflowContext.tsx +++ b/frontend/src/contexts/ToolWorkflowContext.tsx @@ -6,10 +6,11 @@ import React, { createContext, useContext, useReducer, useCallback, useMemo } from 'react'; import { useToolManagement } from '../hooks/useToolManagement'; import { PageEditorFunctions } from '../types/pageEditor'; -import { ToolRegistryEntry } from '../data/toolsTaxonomy'; +import { ToolRegistryEntry, ToolRegistry } from '../data/toolsTaxonomy'; import { useNavigationActions, useNavigationState } from './NavigationContext'; +import { ToolId, isValidToolId } from '../types/toolId'; import { useNavigationUrlSync } from '../hooks/useUrlSync'; -import { getDefaultWorkbench } from '../types/navigation'; +import { getDefaultWorkbench } from '../types/workbench'; // State interface interface ToolWorkflowState { @@ -84,7 +85,7 @@ interface ToolWorkflowContextValue extends ToolWorkflowState { setSearchQuery: (query: string) => void; // Tool Actions - selectTool: (toolId: string) => void; + selectTool: (toolId: ToolId | null) => void; clearToolSelection: () => void; // Tool Reset Actions @@ -174,7 +175,8 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { // Workflow actions (compound actions that coordinate multiple state changes) const handleToolSelect = useCallback((toolId: string) => { // Set the selected tool and determine the appropriate workbench - actions.setSelectedTool(toolId); + const validToolId = isValidToolId(toolId) ? toolId : null; + actions.setSelectedTool(validToolId); // Get the tool from registry to determine workbench const tool = getSelectedTool(toolId); @@ -229,7 +231,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { navigationState.selectedTool, handleToolSelect, handleBackToTools, - toolRegistry, + toolRegistry as ToolRegistry, true ); diff --git a/frontend/src/data/toolsTaxonomy.ts b/frontend/src/data/toolsTaxonomy.ts index 95fbfa612..6d2590481 100644 --- a/frontend/src/data/toolsTaxonomy.ts +++ b/frontend/src/data/toolsTaxonomy.ts @@ -1,9 +1,9 @@ import { type TFunction } from 'i18next'; import React from 'react'; -import { ToolOperationHook, ToolOperationConfig } from '../hooks/tools/shared/useToolOperation'; +import { ToolOperationConfig } from '../hooks/tools/shared/useToolOperation'; import { BaseToolProps } from '../types/tool'; -import { BaseParameters } from '../types/parameters'; -import { WorkbenchType } from '../types/navigation'; +import { WorkbenchType } from '../types/workbench'; +import { ToolId } from '../types/toolId'; export enum SubcategoryId { SIGNING = 'signing', @@ -47,7 +47,7 @@ export type ToolRegistryEntry = { settingsComponent?: React.ComponentType; } -export type ToolRegistry = Record; +export type ToolRegistry = Record; export const SUBCATEGORY_ORDER: SubcategoryId[] = [ SubcategoryId.SIGNING, @@ -126,7 +126,7 @@ export const getToolWorkbench = (tool: ToolRegistryEntry): WorkbenchType => { }; /** - * Get URL path for a tool + * Get URL path for a tool */ export const getToolUrlPath = (toolId: string, tool: ToolRegistryEntry): string => { return tool.urlPath || `/${toolId.replace(/([A-Z])/g, '-$1').toLowerCase()}`; diff --git a/frontend/src/data/useTranslatedToolRegistry.tsx b/frontend/src/data/useTranslatedToolRegistry.tsx index d1628a76e..68883fe92 100644 --- a/frontend/src/data/useTranslatedToolRegistry.tsx +++ b/frontend/src/data/useTranslatedToolRegistry.tsx @@ -39,6 +39,7 @@ import AddWatermarkSingleStepSettings from "../components/tools/addWatermark/Add import OCRSettings from "../components/tools/ocr/OCRSettings"; import ConvertSettings from "../components/tools/convert/ConvertSettings"; import ChangePermissionsSettings from "../components/tools/changePermissions/ChangePermissionsSettings"; +import { ToolId } from "../types/toolId"; const showPlaceholderTools = true; // Show all tools; grey out unavailable ones in UI @@ -162,7 +163,7 @@ export function useFlatToolRegistry(): ToolRegistry { operationConfig: addPasswordOperationConfig, settingsComponent: AddPasswordSettings, }, - watermark: { + addWatermark: { icon: , name: t("home.watermark.title", "Add Watermark"), component: AddWatermark, @@ -258,7 +259,7 @@ export function useFlatToolRegistry(): ToolRegistry { // Document Review - read: { + read: { icon: , name: t("home.read.title", "Read"), component: null, @@ -284,7 +285,6 @@ export function useFlatToolRegistry(): ToolRegistry { icon: , name: t("home.crop.title", "Crop PDF"), component: null, - description: t("home.crop.desc", "Crop a PDF to reduce its size (maintains text!)"), categoryId: ToolCategoryId.STANDARD_TOOLS, subcategoryId: SubcategoryId.PAGE_FORMATTING, @@ -293,16 +293,14 @@ export function useFlatToolRegistry(): ToolRegistry { icon: , name: t("home.rotate.title", "Rotate"), component: null, - description: t("home.rotate.desc", "Easily rotate your PDFs."), categoryId: ToolCategoryId.STANDARD_TOOLS, subcategoryId: SubcategoryId.PAGE_FORMATTING, }, - splitPdf: { + split: { icon: , name: t("home.split.title", "Split"), component: SplitPdfPanel, - description: t("home.split.desc", "Split PDFs into multiple documents"), categoryId: ToolCategoryId.STANDARD_TOOLS, subcategoryId: SubcategoryId.PAGE_FORMATTING, @@ -372,7 +370,7 @@ export function useFlatToolRegistry(): ToolRegistry { // Extraction - extractPages: { + "extract-page": { icon: , name: t("home.extractPages.title", "Extract Pages"), component: null, @@ -490,7 +488,7 @@ export function useFlatToolRegistry(): ToolRegistry { // Advanced Formatting - adjustContrast: { + "adjust-contrast": { icon: , name: t("home.adjustContrast.title", "Adjust Colors/Contrast"), component: null, @@ -700,9 +698,9 @@ export function useFlatToolRegistry(): ToolRegistry { return allTools; } const filteredTools = Object.keys(allTools) - .filter((key) => allTools[key].component !== null || allTools[key].link) + .filter((key) => allTools[key as ToolId].component !== null || allTools[key as ToolId].link) .reduce((obj, key) => { - obj[key] = allTools[key]; + obj[key as ToolId] = allTools[key as ToolId]; return obj; }, {} as ToolRegistry); return filteredTools; diff --git a/frontend/src/hooks/tools/automate/useAutomationForm.ts b/frontend/src/hooks/tools/automate/useAutomationForm.ts index deb5fbd2b..6ad8afe81 100644 --- a/frontend/src/hooks/tools/automate/useAutomationForm.ts +++ b/frontend/src/hooks/tools/automate/useAutomationForm.ts @@ -2,29 +2,29 @@ import { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { AutomationTool, AutomationConfig, AutomationMode } from '../../../types/automation'; import { AUTOMATION_CONSTANTS } from '../../../constants/automation'; -import { ToolRegistryEntry } from '../../../data/toolsTaxonomy'; +import { ToolRegistry } from '../../../data/toolsTaxonomy'; interface UseAutomationFormProps { mode: AutomationMode; existingAutomation?: AutomationConfig; - toolRegistry: Record; + toolRegistry: ToolRegistry; } export function useAutomationForm({ mode, existingAutomation, toolRegistry }: UseAutomationFormProps) { const { t } = useTranslation(); - + const [automationName, setAutomationName] = useState(''); const [automationDescription, setAutomationDescription] = useState(''); const [automationIcon, setAutomationIcon] = useState(''); const [selectedTools, setSelectedTools] = useState([]); const getToolName = useCallback((operation: string) => { - const tool = toolRegistry?.[operation] as any; + const tool = toolRegistry?.[operation as keyof ToolRegistry] as any; return tool?.name || t(`tools.${operation}.name`, operation); }, [toolRegistry, t]); const getToolDefaultParameters = useCallback((operation: string): Record => { - const config = toolRegistry[operation]?.operationConfig; + const config = toolRegistry[operation as keyof ToolRegistry]?.operationConfig; if (config?.defaultParameters) { return { ...config.defaultParameters }; } @@ -119,4 +119,4 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us getToolName, getToolDefaultParameters }; -} \ No newline at end of file +} diff --git a/frontend/src/hooks/useSuggestedTools.ts b/frontend/src/hooks/useSuggestedTools.ts index e66e78779..8478bbc6b 100644 --- a/frontend/src/hooks/useSuggestedTools.ts +++ b/frontend/src/hooks/useSuggestedTools.ts @@ -1,5 +1,6 @@ import { useMemo } from 'react'; import { useNavigationActions, useNavigationState } from '../contexts/NavigationContext'; +import { ToolId } from '../types/toolId'; // Material UI Icons import CompressIcon from '@mui/icons-material/Compress'; @@ -9,7 +10,7 @@ import CropIcon from '@mui/icons-material/Crop'; import TextFieldsIcon from '@mui/icons-material/TextFields'; export interface SuggestedTool { - id: string /* FIX ME: Should be ToolId */; + id: ToolId; title: string; icon: React.ComponentType; navigate: () => void; @@ -32,7 +33,7 @@ const ALL_SUGGESTED_TOOLS: Omit[] = [ icon: CleaningServicesIcon }, { - id: 'splitPdf', + id: 'split', title: 'Split', icon: CropIcon }, diff --git a/frontend/src/hooks/useToolManagement.tsx b/frontend/src/hooks/useToolManagement.tsx index 37d29278a..f73f458db 100644 --- a/frontend/src/hooks/useToolManagement.tsx +++ b/frontend/src/hooks/useToolManagement.tsx @@ -35,7 +35,7 @@ export const useToolManagement = (): ToolManagementResult => { const isToolAvailable = useCallback((toolKey: string): boolean => { if (endpointsLoading) return true; - const endpoints = baseRegistry[toolKey]?.endpoints || []; + const endpoints = baseRegistry[toolKey as keyof typeof baseRegistry]?.endpoints || []; return endpoints.length === 0 || endpoints.some((endpoint: string) => endpointStatus[endpoint] === true); }, [endpointsLoading, endpointStatus, baseRegistry]); diff --git a/frontend/src/hooks/useUrlSync.ts b/frontend/src/hooks/useUrlSync.ts index 601c53eae..e65e5500f 100644 --- a/frontend/src/hooks/useUrlSync.ts +++ b/frontend/src/hooks/useUrlSync.ts @@ -3,7 +3,7 @@ */ import { useEffect, useCallback, useRef } from 'react'; -import { WorkbenchType, ToolId } from '../types/navigation'; +import { ToolId } from '../types/toolId'; import { parseToolRoute, updateToolRoute, clearToolRoute } from '../utils/urlRouting'; import { ToolRegistry } from '../data/toolsTaxonomy'; import { firePixel } from '../utils/scarfTracking'; @@ -55,7 +55,7 @@ export function useNavigationUrlSync( clearToolRoute(false); // Use pushState for user navigation } } - + prevSelectedTool.current = selectedTool; }, [selectedTool, registry, enableSync]); diff --git a/frontend/src/tools/Automate.tsx b/frontend/src/tools/Automate.tsx index 74af39d2e..15e5ea3fa 100644 --- a/frontend/src/tools/Automate.tsx +++ b/frontend/src/tools/Automate.tsx @@ -161,7 +161,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const filesPlaceholder = useMemo(() => { if (currentStep === AUTOMATION_STEPS.RUN && stepData.automation?.operations?.length) { const firstOperation = stepData.automation.operations[0]; - const toolConfig = toolRegistry[firstOperation.operation]; + const toolConfig = toolRegistry[firstOperation.operation as keyof typeof toolRegistry]; // Check if the tool has supportedFormats that include non-PDF formats if (toolConfig?.supportedFormats && toolConfig.supportedFormats.length > 1) { diff --git a/frontend/src/types/navigation.ts b/frontend/src/types/navigation.ts index 4a58771f6..b66b3276d 100644 --- a/frontend/src/types/navigation.ts +++ b/frontend/src/types/navigation.ts @@ -2,14 +2,8 @@ * Navigation types for workbench and tool separation */ -// Define workbench values once as source of truth -const WORKBENCH_TYPES = ['viewer', 'pageEditor', 'fileEditor'] as const; - -// Workbench types - how the user interacts with content -export type WorkbenchType = typeof WORKBENCH_TYPES[number]; - -// Tool identity - what PDF operation we're performing (derived from registry) -export type ToolId = string; +import { WorkbenchType } from './workbench'; +import { ToolId } from './toolId'; // Navigation state export interface NavigationState { @@ -17,12 +11,6 @@ export interface NavigationState { selectedTool: ToolId | null; } -export const getDefaultWorkbench = (): WorkbenchType => 'fileEditor'; - -// Type guard using the same source of truth - no duplication -export const isValidWorkbench = (value: string): value is WorkbenchType => { - return WORKBENCH_TYPES.includes(value as WorkbenchType); -}; // Route parsing result export interface ToolRoute { diff --git a/frontend/src/types/navigationActions.ts b/frontend/src/types/navigationActions.ts index eb50708e6..d200ffdbc 100644 --- a/frontend/src/types/navigationActions.ts +++ b/frontend/src/types/navigationActions.ts @@ -2,7 +2,8 @@ * Navigation action interfaces to break circular dependencies */ -import { WorkbenchType, ToolId } from './navigation'; +import { WorkbenchType } from './workbench'; +import { ToolId } from './toolId'; export interface NavigationActions { setWorkbench: (workbench: WorkbenchType) => void; diff --git a/frontend/src/types/toolId.ts b/frontend/src/types/toolId.ts new file mode 100644 index 000000000..be38bdf37 --- /dev/null +++ b/frontend/src/types/toolId.ts @@ -0,0 +1,25 @@ +// Define all possible tool IDs as source of truth +const TOOL_IDS = [ + 'certSign', 'sign', 'addPassword', 'remove-password', 'removePages', 'remove-blank-pages', 'remove-annotations', 'remove-image', + 'change-permissions', 'addWatermark', + 'sanitize', 'auto-split-pages', 'auto-split-by-size-count', 'split', 'mergePdfs', + 'convert', 'ocr', 'add-image', 'rotate', + 'detect-split-scanned-photos', + 'edit-table-of-contents', + 'scanner-effect', + 'auto-rename-pdf-file', 'multi-page-layout', 'adjust-page-size-scale', 'adjust-contrast', 'cropPdf', 'single-large-page', 'multi-tool', + 'repair', 'compare', 'addPageNumbers', 'redact', + 'flatten', 'remove-certificate-sign', + 'unlock-pdf-forms', 'compress', 'extract-page', 'reorganize-pages', 'extract-images', + 'add-stamp', 'add-attachments', 'change-metadata', 'overlay-pdfs', + 'manage-certificates', 'get-all-info-on-pdf', 'validate-pdf-signature', 'read', 'automate', 'replace-and-invert-color', + 'show-javascript', 'dev-api', 'dev-folder-scanning', 'dev-sso-guide', 'dev-airgapped' +] as const; + +// Tool identity - what PDF operation we're performing (type-safe) +export type ToolId = typeof TOOL_IDS[number]; + +// Type guard using the same source of truth +export const isValidToolId = (value: string): value is ToolId => { + return TOOL_IDS.includes(value as ToolId); +}; diff --git a/frontend/src/types/workbench.ts b/frontend/src/types/workbench.ts new file mode 100644 index 000000000..8943638f2 --- /dev/null +++ b/frontend/src/types/workbench.ts @@ -0,0 +1,12 @@ +// Define workbench values once as source of truth +const WORKBENCH_TYPES = ['viewer', 'pageEditor', 'fileEditor'] as const; + +// Workbench types - how the user interacts with content +export type WorkbenchType = typeof WORKBENCH_TYPES[number]; + +export const getDefaultWorkbench = (): WorkbenchType => 'fileEditor'; + +// Type guard using the same source of truth +export const isValidWorkbench = (value: string): value is WorkbenchType => { + return WORKBENCH_TYPES.includes(value as WorkbenchType); +}; \ No newline at end of file diff --git a/frontend/src/utils/automationExecutor.ts b/frontend/src/utils/automationExecutor.ts index 0eb052e70..124f065a7 100644 --- a/frontend/src/utils/automationExecutor.ts +++ b/frontend/src/utils/automationExecutor.ts @@ -31,7 +31,7 @@ export const executeToolOperationWithPrefix = async ( ): Promise => { console.log(`🔧 Executing tool: ${operationName}`, { parameters, fileCount: files.length }); - const config = toolRegistry[operationName]?.operationConfig; + const config = toolRegistry[operationName as keyof ToolRegistry]?.operationConfig; if (!config) { console.error(`❌ Tool operation not supported: ${operationName}`); throw new Error(`Tool operation not supported: ${operationName}`); diff --git a/frontend/src/utils/urlMapping.ts b/frontend/src/utils/urlMapping.ts index 953340a3d..909924ca6 100644 --- a/frontend/src/utils/urlMapping.ts +++ b/frontend/src/utils/urlMapping.ts @@ -1,10 +1,10 @@ -import { ToolId } from '../types/navigation'; +import { ToolId } from '../types/toolId'; // Map URL paths to tool keys (multiple URLs can map to same tool) export const URL_TO_TOOL_MAP: Record = { '/split-pdfs': 'split', '/split': 'split', - '/merge-pdfs': 'merge', + '/merge-pdfs': 'mergePdfs', '/compress-pdf': 'compress', '/convert': 'convert', '/convert-pdf': 'convert', @@ -19,15 +19,15 @@ export const URL_TO_TOOL_MAP: Record = { '/pdf-to-word': 'convert', '/pdf-to-xml': 'convert', '/add-password': 'addPassword', - '/change-permissions': 'changePermissions', + '/change-permissions': 'change-permissions', '/sanitize-pdf': 'sanitize', '/ocr': 'ocr', '/ocr-pdf': 'ocr', '/add-watermark': 'addWatermark', - '/remove-password': 'removePassword', + '/remove-password': 'remove-password', '/single-large-page': 'single-large-page', '/repair': 'repair', - '/unlock-pdf-forms': 'unlockPdfForms', - '/remove-certificate-sign': 'removeCertificateSign', - '/remove-cert-sign': 'removeCertificateSign' -}; \ No newline at end of file + '/unlock-pdf-forms': 'unlock-pdf-forms', + '/remove-certificate-sign': 'remove-certificate-sign', + '/remove-cert-sign': 'remove-certificate-sign' +}; diff --git a/frontend/src/utils/urlRouting.ts b/frontend/src/utils/urlRouting.ts index 930d1b90d..3ca35e9a7 100644 --- a/frontend/src/utils/urlRouting.ts +++ b/frontend/src/utils/urlRouting.ts @@ -2,12 +2,10 @@ * URL routing utilities for tool navigation with registry support */ -import { - ToolId, - ToolRoute, - getDefaultWorkbench -} from '../types/navigation'; -import { ToolRegistry, getToolWorkbench, getToolUrlPath, isValidToolId } from '../data/toolsTaxonomy'; +import { ToolRoute } from '../types/navigation'; +import { ToolId, isValidToolId } from '../types/toolId'; +import { getDefaultWorkbench } from '../types/workbench'; +import { ToolRegistry, getToolWorkbench, getToolUrlPath } from '../data/toolsTaxonomy'; import { firePixel } from './scarfTracking'; import { URL_TO_TOOL_MAP } from './urlMapping'; @@ -31,7 +29,7 @@ export function parseToolRoute(registry: ToolRegistry): ToolRoute { // Fallback: Try to find tool by primary URL path in registry for (const [toolId, tool] of Object.entries(registry)) { const toolUrlPath = getToolUrlPath(toolId, tool); - if (path === toolUrlPath) { + if (path === toolUrlPath && isValidToolId(toolId)) { return { workbench: getToolWorkbench(tool), toolId @@ -41,7 +39,7 @@ export function parseToolRoute(registry: ToolRegistry): ToolRoute { // Check for query parameter fallback (e.g., ?tool=split) const toolParam = searchParams.get('tool'); - if (toolParam && isValidToolId(toolParam, registry)) { + if (toolParam && isValidToolId(toolParam) && registry[toolParam]) { const tool = registry[toolParam]; return { workbench: getToolWorkbench(tool),