diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index a405edb7c..ad0fe8aa6 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -1597,7 +1597,7 @@ "auto": { "header": "Auto Redact", "settings": "Redaction Settings", - "colorLabel": "Colour", + "colorLabel": "Box Colour", "wordsToRedact": { "title": "Words to Redact", "placeholder": "Enter a word", diff --git a/frontend/src/components/tools/redact/AutomaticRedactSettings.tsx b/frontend/src/components/tools/redact/AutomaticRedactSettings.tsx index a724b0407..1cac3ff05 100644 --- a/frontend/src/components/tools/redact/AutomaticRedactSettings.tsx +++ b/frontend/src/components/tools/redact/AutomaticRedactSettings.tsx @@ -33,27 +33,28 @@ const AutomaticRedactSettings = ({ parameters, onParameterChange, disabled = fal {/* Box Color */} - {t('redact.auto.colorLabel', 'Colour')} - - onParameterChange('redactColor', value)} - disabled={disabled} - size="sm" - style={{ width: '80px' }} - /> - onParameterChange('customPadding', typeof value === 'number' ? value : 0.1)} - min={0} - max={10} - step={0.1} - disabled={disabled} - size="sm" - style={{ width: '80px' }} - placeholder="0.1" - /> - + {t('redact.auto.colorLabel', 'Box Colour')} + onParameterChange('redactColor', value)} + disabled={disabled} + size="sm" + /> + + + {/* Box Padding */} + + {t('redact.auto.paddingLabel', 'Box Padding')} + onParameterChange('customPadding', typeof value === 'number' ? value : 0.1)} + min={0} + max={10} + step={0.1} + disabled={disabled} + size="sm" + placeholder="0.1" + /> {/* Use Regex */} diff --git a/frontend/src/components/tools/redact/RedactAdvancedStep.tsx b/frontend/src/components/tools/redact/RedactAdvancedStep.tsx new file mode 100644 index 000000000..8c9ab493e --- /dev/null +++ b/frontend/src/components/tools/redact/RedactAdvancedStep.tsx @@ -0,0 +1,76 @@ +import { Stack, Text, NumberInput, ColorInput } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import { RedactParameters } from '../../../hooks/tools/redact/useRedactParameters'; + +interface RedactAdvancedStepProps { + parameters: RedactParameters; + onParameterChange: (key: K, value: RedactParameters[K]) => void; + disabled?: boolean; +} + +export default function RedactAdvancedStep({ parameters, onParameterChange, disabled }: RedactAdvancedStepProps) { + const { t } = useTranslation(); + + return ( + + {/* Box Color */} + + {t('redact.advanced.colorLabel', 'Box Color')} + onParameterChange('redactColor', value)} + disabled={disabled} + size="sm" + /> + + + {/* Box Padding */} + + {t('redact.advanced.paddingLabel', 'Box Padding')} + onParameterChange('customPadding', typeof value === 'number' ? value : 0.1)} + min={0} + max={10} + step={0.1} + disabled={disabled} + size="sm" + placeholder="0.1" + /> + + + {/* Use Regex */} + + + {/* Whole Word Search */} + + + {/* Convert PDF to PDF-Image */} + + + ); +} diff --git a/frontend/src/components/tools/redact/RedactModeStep.tsx b/frontend/src/components/tools/redact/RedactModeStep.tsx new file mode 100644 index 000000000..ec98b56a0 --- /dev/null +++ b/frontend/src/components/tools/redact/RedactModeStep.tsx @@ -0,0 +1,21 @@ +import { Stack } from '@mantine/core'; +import { RedactParameters } from '../../../hooks/tools/redact/useRedactParameters'; +import RedactModeSelector from './RedactModeSelector'; + +interface RedactModeStepProps { + parameters: RedactParameters; + onParameterChange: (key: K, value: RedactParameters[K]) => void; + disabled?: boolean; +} + +export default function RedactModeStep({ parameters, onParameterChange, disabled }: RedactModeStepProps) { + return ( + + onParameterChange('mode', mode)} + disabled={disabled} + /> + + ); +} diff --git a/frontend/src/components/tools/redact/RedactWordsStep.tsx b/frontend/src/components/tools/redact/RedactWordsStep.tsx new file mode 100644 index 000000000..6de3c3350 --- /dev/null +++ b/frontend/src/components/tools/redact/RedactWordsStep.tsx @@ -0,0 +1,21 @@ +import { Stack } from '@mantine/core'; +import { RedactParameters } from '../../../hooks/tools/redact/useRedactParameters'; +import WordsToRedactInput from './WordsToRedactInput'; + +interface RedactWordsStepProps { + parameters: RedactParameters; + onParameterChange: (key: K, value: RedactParameters[K]) => void; + disabled?: boolean; +} + +export default function RedactWordsStep({ parameters, onParameterChange, disabled }: RedactWordsStepProps) { + return ( + + onParameterChange('wordsToRedact', words)} + disabled={disabled} + /> + + ); +} diff --git a/frontend/src/components/tools/redact/WordsToRedactInput.tsx b/frontend/src/components/tools/redact/WordsToRedactInput.tsx index 1b2f48ef4..e574c4819 100644 --- a/frontend/src/components/tools/redact/WordsToRedactInput.tsx +++ b/frontend/src/components/tools/redact/WordsToRedactInput.tsx @@ -58,14 +58,13 @@ export default function WordsToRedactInput({ wordsToRedact, onWordsChange, disab ))} {/* Add new word input */} - + setCurrentWord(e.target.value)} onKeyDown={handleKeyPress} disabled={disabled} - style={{ flex: 1 }} size="sm" /> - + {/* Examples */} {wordsToRedact.length === 0 && ( diff --git a/frontend/src/components/tooltips/useRedactTips.ts b/frontend/src/components/tooltips/useRedactTips.ts new file mode 100644 index 000000000..af925f4fb --- /dev/null +++ b/frontend/src/components/tooltips/useRedactTips.ts @@ -0,0 +1,79 @@ +import { useTranslation } from 'react-i18next'; +import { TooltipContent } from '../../types/tips'; + +export const useRedactModeTips = (): TooltipContent => { + const { t } = useTranslation(); + + return { + header: { + title: t("redact.tooltip.mode.header.title", "Redaction Method") + }, + tips: [ + { + title: t("redact.tooltip.mode.automatic.title", "Automatic Redaction"), + description: t("redact.tooltip.mode.automatic.text", "Automatically finds and redacts specified text throughout the document. Perfect for removing consistent sensitive information like names, SSNs, or confidential markers.") + }, + { + title: t("redact.tooltip.mode.manual.title", "Manual Redaction"), + description: t("redact.tooltip.mode.manual.text", "Click and drag to manually select specific areas to redact. Gives you precise control over what gets redacted. (Coming soon)") + } + ] + }; +}; + +export const useRedactWordsTips = (): TooltipContent => { + const { t } = useTranslation(); + + return { + header: { + title: t("redact.tooltip.words.header.title", "Words to Redact") + }, + tips: [ + { + title: t("redact.tooltip.words.description.title", "Text Matching"), + description: t("redact.tooltip.words.description.text", "Enter words or phrases to find and redact in your document. Each word will be searched for separately."), + bullets: [ + t("redact.tooltip.words.bullet1", "Add one word at a time"), + t("redact.tooltip.words.bullet2", "Press Enter or click 'Add Another' to add"), + t("redact.tooltip.words.bullet3", "Click × to remove words") + ] + }, + { + title: t("redact.tooltip.words.examples.title", "Common Examples"), + description: t("redact.tooltip.words.examples.text", "Typical words to redact include: 'Confidential', 'SSN:', phone numbers, email addresses, or specific names.") + } + ] + }; +}; + +export const useRedactAdvancedTips = (): TooltipContent => { + const { t } = useTranslation(); + + return { + header: { + title: t("redact.tooltip.advanced.header.title", "Advanced Redaction Settings") + }, + tips: [ + { + title: t("redact.tooltip.advanced.color.title", "Box Color & Padding"), + description: t("redact.tooltip.advanced.color.text", "Customize the appearance of redaction boxes. Black is standard, but you can choose any color. Padding adds extra space around the found text."), + }, + { + title: t("redact.tooltip.advanced.regex.title", "Use Regex"), + description: t("redact.tooltip.advanced.regex.text", "Enable regular expressions for advanced pattern matching. Useful for finding phone numbers, emails, or complex patterns."), + bullets: [ + t("redact.tooltip.advanced.regex.bullet1", "Example: \\d{3}-\\d{3}-\\d{4} for phone numbers"), + t("redact.tooltip.advanced.regex.bullet2", "Use with caution - test thoroughly") + ] + }, + { + title: t("redact.tooltip.advanced.wholeWord.title", "Whole Word Search"), + description: t("redact.tooltip.advanced.wholeWord.text", "Only match complete words, not partial matches. 'John' won't match 'Johnson' when enabled.") + }, + { + title: t("redact.tooltip.advanced.convert.title", "Convert to PDF-Image"), + description: t("redact.tooltip.advanced.convert.text", "Converts the PDF to an image-based PDF after redaction. This ensures text behind redaction boxes is completely removed and unrecoverable.") + } + ] + }; +}; diff --git a/frontend/src/tools/Redact.tsx b/frontend/src/tools/Redact.tsx index 3a4d67d7c..c902ce30a 100644 --- a/frontend/src/tools/Redact.tsx +++ b/frontend/src/tools/Redact.tsx @@ -1,14 +1,23 @@ import { useTranslation } from "react-i18next"; +import { useState } from "react"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; -import RedactSettings from "../components/tools/redact/RedactSettings"; +import RedactModeStep from "../components/tools/redact/RedactModeStep"; +import RedactWordsStep from "../components/tools/redact/RedactWordsStep"; +import RedactAdvancedStep from "../components/tools/redact/RedactAdvancedStep"; import { useRedactParameters } from "../hooks/tools/redact/useRedactParameters"; import { useRedactOperation } from "../hooks/tools/redact/useRedactOperation"; import { useBaseTool } from "../hooks/tools/shared/useBaseTool"; import { BaseToolProps, ToolComponent } from "../types/tool"; +import { useRedactModeTips, useRedactWordsTips, useRedactAdvancedTips } from "../components/tooltips/useRedactTips"; const Redact = (props: BaseToolProps) => { const { t } = useTranslation(); + // State for managing step collapse status + const [methodCollapsed, setMethodCollapsed] = useState(false); + const [wordsCollapsed, setWordsCollapsed] = useState(false); + const [advancedCollapsed, setAdvancedCollapsed] = useState(true); + const base = useBaseTool( 'redact', useRedactParameters, @@ -16,6 +25,11 @@ const Redact = (props: BaseToolProps) => { props ); + // Tooltips for each step + const modeTips = useRedactModeTips(); + const wordsTips = useRedactWordsTips(); + const advancedTips = useRedactAdvancedTips(); + const isExecuteDisabled = () => { if (base.params.parameters.mode === 'manual') { return true; // Manual mode not implemented yet @@ -23,6 +37,11 @@ const Redact = (props: BaseToolProps) => { return !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled; }; + // Compute actual collapsed state based on results and user state + const getActualCollapsedState = (userCollapsed: boolean) => { + return base.hasResults ? true : userCollapsed; // Force collapse when results are shown + }; + return createToolFlow({ files: { selectedFiles: base.selectedFiles, @@ -30,11 +49,38 @@ const Redact = (props: BaseToolProps) => { }, steps: [ { - title: "Settings", - isCollapsed: base.settingsCollapsed, - onCollapsedClick: base.settingsCollapsed ? base.handleSettingsReset : undefined, + title: t("redact.steps.method", "Method"), + isCollapsed: getActualCollapsedState(methodCollapsed), + onCollapsedClick: () => base.settingsCollapsed ? base.handleSettingsReset() : setMethodCollapsed(!methodCollapsed), + tooltip: modeTips, content: ( - + ), + }, + { + title: t("redact.steps.words", "Words to Redact"), + isCollapsed: getActualCollapsedState(wordsCollapsed), + onCollapsedClick: () => base.settingsCollapsed ? base.handleSettingsReset() : setWordsCollapsed(!wordsCollapsed), + tooltip: wordsTips, + content: ( + + ), + }, + { + title: t("redact.steps.advanced", "Advanced Settings"), + isCollapsed: getActualCollapsedState(advancedCollapsed), + onCollapsedClick: () => base.settingsCollapsed ? base.handleSettingsReset() : setAdvancedCollapsed(!advancedCollapsed), + tooltip: advancedTips, + content: ( +