Redesign settings to be closer to Figma

This commit is contained in:
James Brunton 2025-09-08 11:09:11 +01:00
parent 1991cdbe07
commit ca9b18844b
6 changed files with 168 additions and 102 deletions

View File

@ -1596,13 +1596,18 @@
},
"auto": {
"header": "Auto Redact",
"settings": "Redaction Settings",
"colorLabel": "Colour",
"textsToRedactLabel": "Text to Redact (line-separated)",
"textsToRedactPlaceholder": "e.g. \nConfidential \nTop-Secret",
"wordsToRedact": {
"title": "Words to Redact",
"placeholder": "Enter a word",
"addAnother": "Add Another",
"examples": "Examples: Confidential, Top-Secret"
},
"useRegexLabel": "Use Regex",
"wholeWordSearchLabel": "Whole Word Search",
"customPaddingLabel": "Custom Extra Padding",
"convertPDFToImageLabel": "Convert PDF to PDF-Image (Used to remove text behind the box)"
"convertPDFToImageLabel": "Convert PDF to PDF-Image"
},
"manual": {
"header": "Manual Redaction",

View File

@ -1,6 +1,7 @@
import { Stack, Text, Textarea, Select, NumberInput, Divider } from "@mantine/core";
import { Stack, Divider, Text, NumberInput, Group, ColorInput } from "@mantine/core";
import { useTranslation } from "react-i18next";
import { RedactParameters } from "../../../hooks/tools/redact/useRedactParameters";
import WordsToRedactInput from "./WordsToRedactInput";
interface AutomaticRedactSettingsProps {
parameters: RedactParameters;
@ -11,61 +12,53 @@ interface AutomaticRedactSettingsProps {
const AutomaticRedactSettings = ({ parameters, onParameterChange, disabled = false }: AutomaticRedactSettingsProps) => {
const { t } = useTranslation();
const colorOptions = [
{ value: '#000000', label: t('black', 'Black') },
{ value: '#FFFFFF', label: t('white', 'White') },
{ value: '#FF0000', label: t('red', 'Red') },
{ value: '#00FF00', label: t('green', 'Green') },
{ value: '#0000FF', label: t('blue', 'Blue') },
];
return (
<Stack gap="md">
<Divider ml='-md' />
{/* Text to Redact */}
<Stack gap="sm">
<Text size="sm" fw={500}>
{t('redact.auto.textsToRedactLabel', 'Text to Redact (line-separated)')}
</Text>
<Textarea
placeholder={t('redact.auto.textsToRedactPlaceholder', 'e.g. \nConfidential \nTop-Secret')}
value={parameters.listOfText}
onChange={(e) => onParameterChange('listOfText', e.target.value)}
disabled={disabled}
rows={4}
required
/>
</Stack>
{/* Words to Redact */}
<WordsToRedactInput
wordsToRedact={parameters.wordsToRedact}
onWordsChange={(words) => onParameterChange('wordsToRedact', words)}
disabled={disabled}
/>
<Divider />
{/* Redaction Color */}
{/* Redaction Settings */}
<Stack gap="sm">
<Text size="sm" fw={500}>
{t('redact.auto.colorLabel', 'Color')}
{t('redact.auto.settings', 'Redaction Settings')}
</Text>
<Select
value={parameters.redactColor}
onChange={(value) => {
if (value) {
onParameterChange('redactColor', value);
}
}}
disabled={disabled}
data={colorOptions}
/>
</Stack>
<Divider />
{/* Search Options */}
<Stack gap="sm">
<Text size="sm" fw={500}>Search Options</Text>
{/* Box Color */}
<Stack gap="sm">
<Text size="sm">{t('redact.auto.colorLabel', 'Colour')}</Text>
<Group gap="sm">
<ColorInput
value={parameters.redactColor}
onChange={(value) => onParameterChange('redactColor', value)}
disabled={disabled}
size="sm"
style={{ width: '80px' }}
/>
<NumberInput
value={parameters.customPadding}
onChange={(value) => 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"
/>
</Group>
</Stack>
{/* Use Regex */}
<label
style={{ display: 'flex', alignItems: 'center', gap: '8px' }}
title="Use regular expressions for pattern matching"
>
<input
type="checkbox"
@ -76,9 +69,9 @@ const AutomaticRedactSettings = ({ parameters, onParameterChange, disabled = fal
<Text size="sm">{t('redact.auto.useRegexLabel', 'Use Regex')}</Text>
</label>
{/* Whole Word Search */}
<label
style={{ display: 'flex', alignItems: 'center', gap: '8px' }}
title="Match whole words only, not partial matches within words"
>
<input
type="checkbox"
@ -88,30 +81,10 @@ const AutomaticRedactSettings = ({ parameters, onParameterChange, disabled = fal
/>
<Text size="sm">{t('redact.auto.wholeWordSearchLabel', 'Whole Word Search')}</Text>
</label>
</Stack>
<Divider />
{/* Advanced Options */}
<Stack gap="sm">
<Text size="sm" fw={500}>Advanced Options</Text>
<Stack gap="sm">
<Text size="sm">{t('redact.auto.customPaddingLabel', 'Custom Extra Padding')}</Text>
<NumberInput
value={parameters.customPadding}
onChange={(value) => onParameterChange('customPadding', typeof value === 'number' ? value : 0.1)}
min={0}
max={10}
step={0.1}
disabled={disabled}
placeholder="0.1"
/>
</Stack>
{/* Convert PDF to PDF-Image */}
<label
style={{ display: 'flex', alignItems: 'center', gap: '8px' }}
title="Convert PDF to PDF-Image to remove text behind the redaction box"
>
<input
type="checkbox"

View File

@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
import { Radio, Stack, Text, Tooltip } from '@mantine/core';
import { Select, Stack, Text } from '@mantine/core';
import { RedactMode } from '../../../hooks/tools/redact/useRedactParameters';
interface RedactModeSelectorProps {
@ -11,40 +11,35 @@ interface RedactModeSelectorProps {
export default function RedactModeSelector({ mode, onModeChange, disabled }: RedactModeSelectorProps) {
const { t } = useTranslation();
const modeOptions = [
{
value: 'automatic',
label: t('redact.modeSelector.automatic', 'Automatic'),
disabled: false
},
{
value: 'manual',
label: t('redact.modeSelector.manual', 'Manual'),
disabled: true // Disabled until implemented
}
];
return (
<Stack gap="sm">
<Text size="sm" fw={600}>
{t('redact.modeSelector.title', 'Redaction Mode')}
</Text>
<Radio.Group
<Select
value={mode}
onChange={(value) => onModeChange(value as RedactMode)}
>
<Stack gap="xs">
<Radio
value="automatic"
label={t('redact.modeSelector.automatic', 'Automatic')}
description={t('redact.modeSelector.automaticDesc', 'Redact text based on search terms')}
disabled={disabled}
/>
<Tooltip
label={t('redact.modeSelector.manualComingSoon', 'Manual redaction coming soon')}
position="right"
>
<div>
<Radio
value="manual"
label={t('redact.modeSelector.manual', 'Manual')}
description={t('redact.modeSelector.manualDesc', 'Click and drag to redact specific areas')}
disabled={true}
style={{ opacity: 0.5 }}
/>
</div>
</Tooltip>
</Stack>
</Radio.Group>
onChange={(value) => {
if (value && value !== 'manual') { // Don't allow manual selection yet
onModeChange(value as RedactMode);
}
}}
disabled={disabled}
data={modeOptions}
/>
</Stack>
);
}

View File

@ -0,0 +1,89 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Stack, Text, TextInput, Button, Group, ActionIcon } from '@mantine/core';
interface WordsToRedactInputProps {
wordsToRedact: string[];
onWordsChange: (words: string[]) => void;
disabled?: boolean;
}
export default function WordsToRedactInput({ wordsToRedact, onWordsChange, disabled }: WordsToRedactInputProps) {
const { t } = useTranslation();
const [currentWord, setCurrentWord] = useState('');
const addWord = () => {
if (currentWord.trim() && !wordsToRedact.includes(currentWord.trim())) {
onWordsChange([...wordsToRedact, currentWord.trim()]);
setCurrentWord('');
}
};
const removeWord = (index: number) => {
onWordsChange(wordsToRedact.filter((_, i) => i !== index));
};
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
addWord();
}
};
return (
<Stack gap="sm">
<Text size="sm" fw={500}>
{t('redact.auto.wordsToRedact.title', 'Words to Redact')}
</Text>
{/* Current words */}
{wordsToRedact.map((word, index) => (
<Group key={index} justify="space-between" style={{
padding: '8px 12px',
backgroundColor: '#f8f9fa',
borderRadius: '4px',
border: '1px solid #e9ecef'
}}>
<Text size="sm">{word}</Text>
<ActionIcon
size="sm"
variant="subtle"
color="red"
onClick={() => removeWord(index)}
disabled={disabled}
>
×
</ActionIcon>
</Group>
))}
{/* Add new word input */}
<Group gap="sm">
<TextInput
placeholder={t('redact.auto.wordsToRedact.placeholder', 'Enter a word')}
value={currentWord}
onChange={(e) => setCurrentWord(e.target.value)}
onKeyDown={handleKeyPress}
disabled={disabled}
style={{ flex: 1 }}
size="sm"
/>
<Button
size="sm"
variant="light"
onClick={addWord}
disabled={disabled || !currentWord.trim()}
>
+ {t('redact.auto.wordsToRedact.addAnother', 'Add Another')}
</Button>
</Group>
{/* Examples */}
{wordsToRedact.length === 0 && (
<Text size="xs" c="dimmed">
{t('redact.auto.wordsToRedact.examples', 'Examples: Confidential, Top-Secret')}
</Text>
)}
</Stack>
);
}

View File

@ -9,7 +9,8 @@ export const buildRedactFormData = (parameters: RedactParameters, file: File): F
formData.append("fileInput", file);
if (parameters.mode === 'automatic') {
formData.append("listOfText", parameters.listOfText);
// Convert array to newline-separated string as expected by backend
formData.append("listOfText", parameters.wordsToRedact.join('\n'));
formData.append("useRegex", parameters.useRegex.toString());
formData.append("wholeWordSearch", parameters.wholeWordSearch.toString());
formData.append("redactColor", parameters.redactColor.replace('#', ''));

View File

@ -1,12 +1,13 @@
import { BaseParameters } from '../../../types/parameters';
import { useBaseParameters, BaseParametersHook } from '../shared/useBaseParameters';
export type RedactMode = 'automatic' | 'manual';
export interface RedactParameters {
export interface RedactParameters extends BaseParameters {
mode: RedactMode;
// Automatic redaction parameters
listOfText: string;
wordsToRedact: string[];
useRegex: boolean;
wholeWordSearch: boolean;
redactColor: string;
@ -16,7 +17,7 @@ export interface RedactParameters {
export const defaultParameters: RedactParameters = {
mode: 'automatic',
listOfText: '',
wordsToRedact: [],
useRegex: false,
wholeWordSearch: false,
redactColor: '#000000',
@ -24,8 +25,10 @@ export const defaultParameters: RedactParameters = {
convertPDFToImage: true,
};
export const useRedactParameters = (): BaseParametersHook<RedactParameters> => {
return useBaseParameters<RedactParameters>({
export type RedactParametersHook = BaseParametersHook<RedactParameters>;
export const useRedactParameters = (): RedactParametersHook => {
return useBaseParameters({
defaultParameters,
endpointName: (params) => {
if (params.mode === 'automatic') {
@ -36,7 +39,7 @@ export const useRedactParameters = (): BaseParametersHook<RedactParameters> => {
},
validateFn: (params) => {
if (params.mode === 'automatic') {
return params.listOfText.trim().length > 0;
return params.wordsToRedact.length > 0 && params.wordsToRedact.some(word => word.trim().length > 0);
}
// Manual mode validation would go here when implemented
return false;