From 7dad484aa7df99f6a43bbe72d8288662ac4c533c Mon Sep 17 00:00:00 2001 From: James Brunton Date: Mon, 15 Sep 2025 14:28:18 +0100 Subject: [PATCH 1/4] Improve type info on param hooks (#4438) # Description of Changes Changes it so that callers of `useBaseTool` know what actual type the parameters hook that they passed in returned, so they can actually make use of any extra methods that that params hook has. --- frontend/src/hooks/tools/shared/useBaseTool.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/hooks/tools/shared/useBaseTool.ts b/frontend/src/hooks/tools/shared/useBaseTool.ts index 723f95e44..996fae712 100644 --- a/frontend/src/hooks/tools/shared/useBaseTool.ts +++ b/frontend/src/hooks/tools/shared/useBaseTool.ts @@ -6,12 +6,12 @@ import { ToolOperationHook } from './useToolOperation'; import { BaseParametersHook } from './useBaseParameters'; import { StirlingFile } from '../../../types/fileContext'; -interface BaseToolReturn { +interface BaseToolReturn> { // File management selectedFiles: StirlingFile[]; // Tool-specific hooks - params: BaseParametersHook; + params: TParamsHook; operation: ToolOperationHook; // Endpoint validation @@ -33,13 +33,13 @@ interface BaseToolReturn { /** * Base tool hook for tool components. Manages standard behaviour for tools. */ -export function useBaseTool( +export function useBaseTool>( toolName: string, - useParams: () => BaseParametersHook, + useParams: () => TParamsHook, useOperation: () => ToolOperationHook, props: BaseToolProps, options?: { minFiles?: number } -): BaseToolReturn { +): BaseToolReturn { const minFiles = options?.minFiles ?? 1; const { onPreviewFile, onComplete, onError } = props; From a57373b9682b767cdb38db9954f6074b1c17da1e Mon Sep 17 00:00:00 2001 From: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> Date: Mon, 15 Sep 2025 17:11:29 +0100 Subject: [PATCH 2/4] V2 Flatten split options to remove layers of drop downs (#4439) Co-authored-by: Connor Yoh --- .../public/locales/en-GB/translation.json | 71 +++++++++++++++++- .../components/tools/split/SplitSettings.tsx | 75 +++++++++++-------- .../src/components/tooltips/useSplitTips.ts | 59 +++++++++++++++ frontend/src/constants/splitConstants.ts | 33 ++++---- .../hooks/tools/split/useSplitOperation.ts | 41 +++++----- .../hooks/tools/split/useSplitParameters.ts | 26 +++---- frontend/src/theme/mantineTheme.ts | 4 +- frontend/src/tools/Split.tsx | 3 + 8 files changed, 229 insertions(+), 83 deletions(-) create mode 100644 frontend/src/components/tooltips/useSplitTips.ts diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index 6ba7c83ba..b7875c314 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -683,7 +683,76 @@ "8": "Document #6: Page 10" }, "splitPages": "Enter pages to split on:", - "submit": "Split" + "submit": "Split", + "error": { + "failed": "An error occurred while splitting the PDF." + }, + "method": { + "label": "Choose split method", + "placeholder": "Select how to split the PDF" + }, + "methods": { + "byPages": "Split at Page Numbers", + "bySections": "Split by Sections", + "bySize": "Split by File Size", + "byPageCount": "Split by Page Count", + "byDocCount": "Split by Document Count", + "byChapters": "Split by Chapters" + }, + "value": { + "fileSize": { + "label": "File Size", + "placeholder": "e.g. 10MB, 500KB" + }, + "pageCount": { + "label": "Pages per File", + "placeholder": "e.g. 5, 10" + }, + "docCount": { + "label": "Number of Files", + "placeholder": "e.g. 3, 5" + } + }, + "tooltip": { + "header": { + "title": "Split Methods Overview" + }, + "byPages": { + "title": "Split at Page Numbers", + "text": "Split your PDF at specific page numbers. Using 'n' splits after page n. Using 'n-m' splits before page n and after page m.", + "bullet1": "Single split points: 3,7 (splits after pages 3 and 7)", + "bullet2": "Range split points: 3-8 (splits before page 3 and after page 8)", + "bullet3": "Mixed: 2,5-10,15 (splits after page 2, before page 5, after page 10, and after page 15)" + }, + "bySections": { + "title": "Split by Grid Sections", + "text": "Divide each page into a grid of sections. Useful for splitting documents with multiple columns or extracting specific areas.", + "bullet1": "Horizontal: Number of rows to create", + "bullet2": "Vertical: Number of columns to create", + "bullet3": "Merge: Combine all sections into one PDF" + }, + "bySize": { + "title": "Split by File Size", + "text": "Create multiple PDFs that don't exceed a specified file size. Ideal for file size limitations or email attachments.", + "bullet1": "Use MB for larger files (e.g., 10MB)", + "bullet2": "Use KB for smaller files (e.g., 500KB)", + "bullet3": "System will split at page boundaries" + }, + "byCount": { + "title": "Split by Count", + "text": "Create multiple PDFs with a specific number of pages or documents each.", + "bullet1": "Page Count: Fixed number of pages per file", + "bullet2": "Document Count: Fixed number of output files", + "bullet3": "Useful for batch processing workflows" + }, + "byChapters": { + "title": "Split by Chapters", + "text": "Use PDF bookmarks to automatically split at chapter boundaries. Requires PDFs with bookmark structure.", + "bullet1": "Bookmark Level: Which level to split on (1=top level)", + "bullet2": "Include Metadata: Preserve document properties", + "bullet3": "Allow Duplicates: Handle repeated bookmark names" + } + } }, "rotate": { "tags": "server side", diff --git a/frontend/src/components/tools/split/SplitSettings.tsx b/frontend/src/components/tools/split/SplitSettings.tsx index 936b5a09f..98a612c41 100644 --- a/frontend/src/components/tools/split/SplitSettings.tsx +++ b/frontend/src/components/tools/split/SplitSettings.tsx @@ -1,6 +1,6 @@ import { Stack, TextInput, Select, Checkbox } from '@mantine/core'; import { useTranslation } from 'react-i18next'; -import { isSplitMode, isSplitType, SPLIT_MODES, SPLIT_TYPES } from '../../../constants/splitConstants'; +import { isSplitMethod, SPLIT_METHODS } from '../../../constants/splitConstants'; import { SplitParameters } from '../../../hooks/tools/split/useSplitParameters'; export interface SplitSettingsProps { @@ -57,28 +57,37 @@ const SplitSettings = ({ ); - const renderBySizeOrCountForm = () => ( - - isSplitMode(v) && onParameterChange('mode', v)} + label={t("split.method.label", "Choose split method")} + placeholder={t("split.method.placeholder", "Select how to split the PDF")} + value={parameters.method} + onChange={(v) => isSplitMethod(v) && onParameterChange('method', v)} disabled={disabled} data={[ - { value: SPLIT_MODES.BY_PAGES, label: t("split.header", "Split by Pages") + " (e.g. 1,3,5-10)" }, - { value: SPLIT_MODES.BY_SECTIONS, label: t("split-by-sections.title", "Split by Grid Sections") }, - { value: SPLIT_MODES.BY_SIZE_OR_COUNT, label: t("split-by-size-or-count.title", "Split by Size or Count") }, - { value: SPLIT_MODES.BY_CHAPTERS, label: t("splitByChapters.title", "Split by Chapters") }, + { value: SPLIT_METHODS.BY_PAGES, label: t("split.methods.byPages", "Split at Pages Numbers") }, + { value: SPLIT_METHODS.BY_SECTIONS, label: t("split.methods.bySections", "Split by Sections") }, + { value: SPLIT_METHODS.BY_SIZE, label: t("split.methods.bySize", "Split by Size") }, + { value: SPLIT_METHODS.BY_PAGE_COUNT, label: t("split.methods.byPageCount", "Split by Page Count") }, + { value: SPLIT_METHODS.BY_DOC_COUNT, label: t("split.methods.byDocCount", "Split by Document Count") }, + { value: SPLIT_METHODS.BY_CHAPTERS, label: t("split.methods.byChapters", "Split by Chapters") }, ]} /> {/* Parameter Form */} - {parameters.mode === SPLIT_MODES.BY_PAGES && renderByPagesForm()} - {parameters.mode === SPLIT_MODES.BY_SECTIONS && renderBySectionsForm()} - {parameters.mode === SPLIT_MODES.BY_SIZE_OR_COUNT && renderBySizeOrCountForm()} - {parameters.mode === SPLIT_MODES.BY_CHAPTERS && renderByChaptersForm()} + {parameters.method === SPLIT_METHODS.BY_PAGES && renderByPagesForm()} + {parameters.method === SPLIT_METHODS.BY_SECTIONS && renderBySectionsForm()} + {(parameters.method === SPLIT_METHODS.BY_SIZE || + parameters.method === SPLIT_METHODS.BY_PAGE_COUNT || + parameters.method === SPLIT_METHODS.BY_DOC_COUNT) && renderSplitValueForm()} + {parameters.method === SPLIT_METHODS.BY_CHAPTERS && renderByChaptersForm()} ); } diff --git a/frontend/src/components/tooltips/useSplitTips.ts b/frontend/src/components/tooltips/useSplitTips.ts new file mode 100644 index 000000000..ff655aabe --- /dev/null +++ b/frontend/src/components/tooltips/useSplitTips.ts @@ -0,0 +1,59 @@ +import { useTranslation } from 'react-i18next'; +import { TooltipContent } from '../../types/tips'; + +export const useSplitTips = (): TooltipContent => { + const { t } = useTranslation(); + + return { + header: { + title: t("split.tooltip.header.title", "Split Methods Overview") + }, + tips: [ + { + title: t("split.tooltip.byPages.title", "Split at Page Numbers"), + description: t("split.tooltip.byPages.text", "Extract specific pages or ranges from your PDF. Use commas to separate individual pages and hyphens for ranges."), + bullets: [ + t("split.tooltip.byPages.bullet1", "Single pages: 1,3,5"), + t("split.tooltip.byPages.bullet2", "Page ranges: 1-5,10-15"), + t("split.tooltip.byPages.bullet3", "Mixed: 1,3-7,12,15-20") + ] + }, + { + title: t("split.tooltip.bySections.title", "Split by Grid Sections"), + description: t("split.tooltip.bySections.text", "Divide each page into a grid of sections. Useful for splitting documents with multiple columns or extracting specific areas."), + bullets: [ + t("split.tooltip.bySections.bullet1", "Horizontal: Number of rows to create"), + t("split.tooltip.bySections.bullet2", "Vertical: Number of columns to create"), + t("split.tooltip.bySections.bullet3", "Merge: Combine all sections into one PDF") + ] + }, + { + title: t("split.tooltip.bySize.title", "Split by File Size"), + description: t("split.tooltip.bySize.text", "Create multiple PDFs that don't exceed a specified file size. Ideal for file size limitations or email attachments."), + bullets: [ + t("split.tooltip.bySize.bullet1", "Use MB for larger files (e.g., 10MB)"), + t("split.tooltip.bySize.bullet2", "Use KB for smaller files (e.g., 500KB)"), + t("split.tooltip.bySize.bullet3", "System will split at page boundaries") + ] + }, + { + title: t("split.tooltip.byCount.title", "Split by Count"), + description: t("split.tooltip.byCount.text", "Create multiple PDFs with a specific number of pages or documents each."), + bullets: [ + t("split.tooltip.byCount.bullet1", "Page Count: Fixed number of pages per file"), + t("split.tooltip.byCount.bullet2", "Document Count: Fixed number of output files"), + t("split.tooltip.byCount.bullet3", "Useful for batch processing workflows") + ] + }, + { + title: t("split.tooltip.byChapters.title", "Split by Chapters"), + description: t("split.tooltip.byChapters.text", "Use PDF bookmarks to automatically split at chapter boundaries. Requires PDFs with bookmark structure."), + bullets: [ + t("split.tooltip.byChapters.bullet1", "Bookmark Level: Which level to split on (1=top level)"), + t("split.tooltip.byChapters.bullet2", "Include Metadata: Preserve document properties"), + t("split.tooltip.byChapters.bullet3", "Allow Duplicates: Handle repeated bookmark names") + ] + } + ] + }; +}; diff --git a/frontend/src/constants/splitConstants.ts b/frontend/src/constants/splitConstants.ts index 1e7098669..896e4bc44 100644 --- a/frontend/src/constants/splitConstants.ts +++ b/frontend/src/constants/splitConstants.ts @@ -1,30 +1,25 @@ -export const SPLIT_MODES = { +export const SPLIT_METHODS = { BY_PAGES: 'byPages', BY_SECTIONS: 'bySections', - BY_SIZE_OR_COUNT: 'bySizeOrCount', + BY_SIZE: 'bySize', + BY_PAGE_COUNT: 'byPageCount', + BY_DOC_COUNT: 'byDocCount', BY_CHAPTERS: 'byChapters' } as const; -export const SPLIT_TYPES = { - SIZE: 'size', - PAGES: 'pages', - DOCS: 'docs' -} as const; export const ENDPOINTS = { - [SPLIT_MODES.BY_PAGES]: 'split-pages', - [SPLIT_MODES.BY_SECTIONS]: 'split-pdf-by-sections', - [SPLIT_MODES.BY_SIZE_OR_COUNT]: 'split-by-size-or-count', - [SPLIT_MODES.BY_CHAPTERS]: 'split-pdf-by-chapters' + [SPLIT_METHODS.BY_PAGES]: 'split-pages', + [SPLIT_METHODS.BY_SECTIONS]: 'split-pdf-by-sections', + [SPLIT_METHODS.BY_SIZE]: 'split-by-size-or-count', + [SPLIT_METHODS.BY_PAGE_COUNT]: 'split-by-size-or-count', + [SPLIT_METHODS.BY_DOC_COUNT]: 'split-by-size-or-count', + [SPLIT_METHODS.BY_CHAPTERS]: 'split-pdf-by-chapters' } as const; -export type SplitMode = typeof SPLIT_MODES[keyof typeof SPLIT_MODES]; -export type SplitType = typeof SPLIT_TYPES[keyof typeof SPLIT_TYPES]; - -export const isSplitMode = (value: string | null): value is SplitMode => { - return Object.values(SPLIT_MODES).includes(value as SplitMode); +export type SplitMethod = typeof SPLIT_METHODS[keyof typeof SPLIT_METHODS]; +export const isSplitMethod = (value: string | null): value is SplitMethod => { + return Object.values(SPLIT_METHODS).includes(value as SplitMethod); } -export const isSplitType = (value: string | null): value is SplitType => { - return Object.values(SPLIT_TYPES).includes(value as SplitType); -} + diff --git a/frontend/src/hooks/tools/split/useSplitOperation.ts b/frontend/src/hooks/tools/split/useSplitOperation.ts index b18b7c1f5..b7ad93af0 100644 --- a/frontend/src/hooks/tools/split/useSplitOperation.ts +++ b/frontend/src/hooks/tools/split/useSplitOperation.ts @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { ToolType, useToolOperation, ToolOperationConfig } from '../shared/useToolOperation'; import { createStandardErrorHandler } from '../../../utils/toolErrorHandler'; import { SplitParameters, defaultParameters } from './useSplitParameters'; -import { SPLIT_MODES } from '../../../constants/splitConstants'; +import { SPLIT_METHODS } from '../../../constants/splitConstants'; import { useToolResources } from '../shared/useToolResources'; // Static functions that can be used by both the hook and automation executor @@ -12,46 +12,53 @@ export const buildSplitFormData = (parameters: SplitParameters, file: File): For formData.append("fileInput", file); - switch (parameters.mode) { - case SPLIT_MODES.BY_PAGES: + switch (parameters.method) { + case SPLIT_METHODS.BY_PAGES: formData.append("pageNumbers", parameters.pages); break; - case SPLIT_MODES.BY_SECTIONS: + case SPLIT_METHODS.BY_SECTIONS: formData.append("horizontalDivisions", parameters.hDiv); formData.append("verticalDivisions", parameters.vDiv); formData.append("merge", parameters.merge.toString()); break; - case SPLIT_MODES.BY_SIZE_OR_COUNT: - formData.append( - "splitType", - parameters.splitType === "size" ? "0" : parameters.splitType === "pages" ? "1" : "2" - ); + case SPLIT_METHODS.BY_SIZE: + formData.append("splitType", "0"); formData.append("splitValue", parameters.splitValue); break; - case SPLIT_MODES.BY_CHAPTERS: + case SPLIT_METHODS.BY_PAGE_COUNT: + formData.append("splitType", "1"); + formData.append("splitValue", parameters.splitValue); + break; + case SPLIT_METHODS.BY_DOC_COUNT: + formData.append("splitType", "2"); + formData.append("splitValue", parameters.splitValue); + break; + case SPLIT_METHODS.BY_CHAPTERS: formData.append("bookmarkLevel", parameters.bookmarkLevel); formData.append("includeMetadata", parameters.includeMetadata.toString()); formData.append("allowDuplicates", parameters.allowDuplicates.toString()); break; default: - throw new Error(`Unknown split mode: ${parameters.mode}`); + throw new Error(`Unknown split method: ${parameters.method}`); } return formData; }; export const getSplitEndpoint = (parameters: SplitParameters): string => { - switch (parameters.mode) { - case SPLIT_MODES.BY_PAGES: + switch (parameters.method) { + case SPLIT_METHODS.BY_PAGES: return "/api/v1/general/split-pages"; - case SPLIT_MODES.BY_SECTIONS: + case SPLIT_METHODS.BY_SECTIONS: return "/api/v1/general/split-pdf-by-sections"; - case SPLIT_MODES.BY_SIZE_OR_COUNT: + case SPLIT_METHODS.BY_SIZE: + case SPLIT_METHODS.BY_PAGE_COUNT: + case SPLIT_METHODS.BY_DOC_COUNT: return "/api/v1/general/split-by-size-or-count"; - case SPLIT_MODES.BY_CHAPTERS: + case SPLIT_METHODS.BY_CHAPTERS: return "/api/v1/general/split-pdf-by-chapters"; default: - throw new Error(`Unknown split mode: ${parameters.mode}`); + throw new Error(`Unknown split method: ${parameters.method}`); } }; diff --git a/frontend/src/hooks/tools/split/useSplitParameters.ts b/frontend/src/hooks/tools/split/useSplitParameters.ts index e48504304..09b1ff1c9 100644 --- a/frontend/src/hooks/tools/split/useSplitParameters.ts +++ b/frontend/src/hooks/tools/split/useSplitParameters.ts @@ -1,14 +1,13 @@ -import { SPLIT_MODES, SPLIT_TYPES, ENDPOINTS, type SplitMode, SplitType } from '../../../constants/splitConstants'; +import { SPLIT_METHODS, ENDPOINTS, type SplitMethod } from '../../../constants/splitConstants'; import { BaseParameters } from '../../../types/parameters'; import { useBaseParameters, BaseParametersHook } from '../shared/useBaseParameters'; export interface SplitParameters extends BaseParameters { - mode: SplitMode | ''; + method: SplitMethod | ''; pages: string; hDiv: string; vDiv: string; merge: boolean; - splitType: SplitType | ''; splitValue: string; bookmarkLevel: string; includeMetadata: boolean; @@ -18,12 +17,11 @@ export interface SplitParameters extends BaseParameters { export type SplitParametersHook = BaseParametersHook; export const defaultParameters: SplitParameters = { - mode: '', + method: '', pages: '', hDiv: '2', vDiv: '2', merge: false, - splitType: SPLIT_TYPES.SIZE, splitValue: '', bookmarkLevel: '1', includeMetadata: false, @@ -34,20 +32,22 @@ export const useSplitParameters = (): SplitParametersHook => { return useBaseParameters({ defaultParameters, endpointName: (params) => { - if (!params.mode) return ENDPOINTS[SPLIT_MODES.BY_PAGES]; - return ENDPOINTS[params.mode as SplitMode]; + if (!params.method) return ENDPOINTS[SPLIT_METHODS.BY_PAGES]; + return ENDPOINTS[params.method as SplitMethod]; }, validateFn: (params) => { - if (!params.mode) return false; + if (!params.method) return false; - switch (params.mode) { - case SPLIT_MODES.BY_PAGES: + switch (params.method) { + case SPLIT_METHODS.BY_PAGES: return params.pages.trim() !== ""; - case SPLIT_MODES.BY_SECTIONS: + case SPLIT_METHODS.BY_SECTIONS: return params.hDiv !== "" && params.vDiv !== ""; - case SPLIT_MODES.BY_SIZE_OR_COUNT: + case SPLIT_METHODS.BY_SIZE: + case SPLIT_METHODS.BY_PAGE_COUNT: + case SPLIT_METHODS.BY_DOC_COUNT: return params.splitValue.trim() !== ""; - case SPLIT_MODES.BY_CHAPTERS: + case SPLIT_METHODS.BY_CHAPTERS: return params.bookmarkLevel !== ""; default: return false; diff --git a/frontend/src/theme/mantineTheme.ts b/frontend/src/theme/mantineTheme.ts index b7cd70a18..47bb1393d 100644 --- a/frontend/src/theme/mantineTheme.ts +++ b/frontend/src/theme/mantineTheme.ts @@ -183,10 +183,10 @@ export const mantineTheme = createTheme({ }, option: { color: 'var(--text-primary)', - '&[data-hovered]': { + '&[dataHovered]': { backgroundColor: 'var(--hover-bg)', }, - '&[data-selected]': { + '&[dataSelected]': { backgroundColor: 'var(--color-primary-100)', color: 'var(--color-primary-900)', }, diff --git a/frontend/src/tools/Split.tsx b/frontend/src/tools/Split.tsx index f22ee9159..9d4570322 100644 --- a/frontend/src/tools/Split.tsx +++ b/frontend/src/tools/Split.tsx @@ -4,10 +4,12 @@ import SplitSettings from "../components/tools/split/SplitSettings"; import { useSplitParameters } from "../hooks/tools/split/useSplitParameters"; import { useSplitOperation } from "../hooks/tools/split/useSplitOperation"; import { useBaseTool } from "../hooks/tools/shared/useBaseTool"; +import { useSplitTips } from "../components/tooltips/useSplitTips"; import { BaseToolProps, ToolComponent } from "../types/tool"; const Split = (props: BaseToolProps) => { const { t } = useTranslation(); + const splitTips = useSplitTips(); const base = useBaseTool( 'split', @@ -26,6 +28,7 @@ const Split = (props: BaseToolProps) => { title: "Settings", isCollapsed: base.settingsCollapsed, onCollapsedClick: base.hasResults ? base.handleSettingsReset : undefined, + tooltip: splitTips, content: ( Date: Tue, 16 Sep 2025 13:08:54 +0100 Subject: [PATCH 3/4] V2 Tool - Auto split (#4446) integrated auto split, with flattened split tool --------- Co-authored-by: Connor Yoh --- .../public/locales/en-GB/translation.json | 52 ++++++- .../src/components/shared/CardSelector.tsx | 99 +++++++++++++ .../components/tools/split/SplitSettings.tsx | 57 +++++--- .../components/tooltips/useSplitMethodTips.ts | 24 ++++ .../tooltips/useSplitSettingsTips.ts | 134 ++++++++++++++++++ .../src/components/tooltips/useSplitTips.ts | 59 -------- frontend/src/constants/splitConstants.ts | 57 +++++++- .../hooks/tools/split/useSplitOperation.ts | 5 + .../hooks/tools/split/useSplitParameters.ts | 4 + frontend/src/tools/Split.tsx | 48 ++++++- 10 files changed, 447 insertions(+), 92 deletions(-) create mode 100644 frontend/src/components/shared/CardSelector.tsx create mode 100644 frontend/src/components/tooltips/useSplitMethodTips.ts create mode 100644 frontend/src/components/tooltips/useSplitSettingsTips.ts delete mode 100644 frontend/src/components/tooltips/useSplitTips.ts diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index b7875c314..0fd88fb07 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -684,6 +684,13 @@ }, "splitPages": "Enter pages to split on:", "submit": "Split", + "steps": { + "chooseMethod": "Choose Method", + "settings": "Settings" + }, + "settings": { + "selectMethodFirst": "Please select a split method first" + }, "error": { "failed": "An error occurred while splitting the PDF." }, @@ -692,12 +699,45 @@ "placeholder": "Select how to split the PDF" }, "methods": { - "byPages": "Split at Page Numbers", - "bySections": "Split by Sections", - "bySize": "Split by File Size", - "byPageCount": "Split by Page Count", - "byDocCount": "Split by Document Count", - "byChapters": "Split by Chapters" + "prefix": { + "splitAt": "Split at", + "splitBy": "Split by" + }, + "byPages": { + "name": "Page Numbers", + "desc": "Extract specific pages (1,3,5-10)", + "tooltip": "Enter page numbers separated by commas or ranges with hyphens" + }, + "bySections": { + "name": "Sections", + "desc": "Divide pages into grid sections", + "tooltip": "Split each page into horizontal and vertical sections" + }, + "bySize": { + "name": "File Size", + "desc": "Limit maximum file size", + "tooltip": "Specify maximum file size (e.g. 10MB, 500KB)" + }, + "byPageCount": { + "name": "Page Count", + "desc": "Fixed pages per file", + "tooltip": "Enter the number of pages for each split file" + }, + "byDocCount": { + "name": "Document Count", + "desc": "Create specific number of files", + "tooltip": "Enter how many files you want to create" + }, + "byChapters": { + "name": "Chapters", + "desc": "Split at bookmark boundaries", + "tooltip": "Uses PDF bookmarks to determine split points" + }, + "byPageDivider": { + "name": "Page Divider", + "desc": "Auto-split with divider sheets", + "tooltip": "Use QR code divider sheets between documents when scanning" + } }, "value": { "fileSize": { diff --git a/frontend/src/components/shared/CardSelector.tsx b/frontend/src/components/shared/CardSelector.tsx new file mode 100644 index 000000000..a8e4a2725 --- /dev/null +++ b/frontend/src/components/shared/CardSelector.tsx @@ -0,0 +1,99 @@ +import { Stack, Card, Text, Flex } from '@mantine/core'; +import { Tooltip } from './Tooltip'; +import { useTranslation } from 'react-i18next'; + +export interface CardOption { + value: T; + prefixKey: string; + nameKey: string; + tooltipKey?: string; + tooltipContent?: any[]; +} + +export interface CardSelectorProps> { + options: K[]; + onSelect: (value: T) => void; + disabled?: boolean; + getTooltipContent?: (option: K) => any[]; +} + +const CardSelector = >({ + options, + onSelect, + disabled = false, + getTooltipContent +}: CardSelectorProps) => { + const { t } = useTranslation(); + + const handleOptionClick = (value: T) => { + if (!disabled) { + onSelect(value); + } + }; + + const getTooltips = (option: K) => { + if (getTooltipContent) { + return getTooltipContent(option); + } + return []; + }; + + return ( + + {options.map((option) => ( + + { + if (!disabled) { + e.currentTarget.style.backgroundColor = 'var(--mantine-color-gray-3)'; + e.currentTarget.style.transform = 'translateY(-1px)'; + e.currentTarget.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)'; + } + }} + onMouseLeave={(e) => { + if (!disabled) { + e.currentTarget.style.backgroundColor = 'var(--mantine-color-gray-2)'; + e.currentTarget.style.transform = 'translateY(0px)'; + e.currentTarget.style.boxShadow = 'none'; + } + }} + onClick={() => handleOptionClick(option.value)} + > + + + {t(option.prefixKey, "Prefix")} + + + {t(option.nameKey, "Option Name")} + + + + + ))} + + ); +}; + +export default CardSelector; diff --git a/frontend/src/components/tools/split/SplitSettings.tsx b/frontend/src/components/tools/split/SplitSettings.tsx index 98a612c41..dc7c02480 100644 --- a/frontend/src/components/tools/split/SplitSettings.tsx +++ b/frontend/src/components/tools/split/SplitSettings.tsx @@ -1,6 +1,7 @@ -import { Stack, TextInput, Select, Checkbox } from '@mantine/core'; +import { Stack, TextInput, Checkbox, Anchor, Text } from '@mantine/core'; +import LocalIcon from '../../shared/LocalIcon'; import { useTranslation } from 'react-i18next'; -import { isSplitMethod, SPLIT_METHODS } from '../../../constants/splitConstants'; +import { SPLIT_METHODS } from '../../../constants/splitConstants'; import { SplitParameters } from '../../../hooks/tools/split/useSplitParameters'; export interface SplitSettingsProps { @@ -113,32 +114,48 @@ const SplitSettings = ({ ); + const renderByPageDividerForm = () => ( + + + + {t("autoSplitPDF.dividerDownload2", "Download 'Auto Splitter Divider (with instructions).pdf'")} + + + onParameterChange('duplexMode', e.currentTarget.checked)} + disabled={disabled} + /> + + ); + + // Don't render anything if no method is selected + if (!parameters.method) { + return ( + + + {t("split.settings.selectMethodFirst", "Please select a split method first")} + + + ); + } + return ( - {/* Method Selector */} -