mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 14:19:24 +00:00
Redesign merge sorting to happen in the frontend
This commit is contained in:
parent
86831928c7
commit
391d283fcd
73
frontend/src/components/tools/merge/MergeFileSorter.tsx
Normal file
73
frontend/src/components/tools/merge/MergeFileSorter.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Group, Button, Text, ActionIcon, Stack } from '@mantine/core';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import SortIcon from '@mui/icons-material/Sort';
|
||||||
|
import SortByAlphaIcon from '@mui/icons-material/SortByAlpha';
|
||||||
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||||
|
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
|
||||||
|
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
|
||||||
|
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
||||||
|
|
||||||
|
interface MergeFileSorterProps {
|
||||||
|
onSortFiles: (sortType: 'filename' | 'dateModified', ascending: boolean) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MergeFileSorter: React.FC<MergeFileSorterProps> = ({
|
||||||
|
onSortFiles,
|
||||||
|
disabled = false,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [ascending, setAscending] = useState(true);
|
||||||
|
|
||||||
|
const sortButtons = [
|
||||||
|
{
|
||||||
|
key: 'filename' as const,
|
||||||
|
icon: <SortByAlphaIcon/>,
|
||||||
|
label: t('merge.sortBy.filename', 'Sort by Filename'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dateModified' as const,
|
||||||
|
icon: <AccessTimeIcon/>,
|
||||||
|
label: t('merge.sortBy.dateModified', 'Sort by Date Modified'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleSortDirectionToggle = () => {
|
||||||
|
setAscending(!ascending);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{t('merge.sortBy.description', "Files will be merged in the order they're selected. Drag to reorder or use the buttons below to sort.")}
|
||||||
|
</Text>
|
||||||
|
<Group gap="xs">
|
||||||
|
{sortButtons.map(({ key, icon, label }) => (
|
||||||
|
<Button
|
||||||
|
key={key}
|
||||||
|
variant="light"
|
||||||
|
size="xs"
|
||||||
|
leftSection={icon}
|
||||||
|
onClick={() => onSortFiles(key, ascending)}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<ActionIcon
|
||||||
|
variant="light"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleSortDirectionToggle}
|
||||||
|
disabled={disabled}
|
||||||
|
title={ascending ? t('merge.sortBy.ascending', 'Ascending') : t('merge.sortBy.descending', 'Descending')}
|
||||||
|
>
|
||||||
|
{ascending ? <ArrowUpwardIcon/> : <ArrowDownwardIcon/>}
|
||||||
|
</ActionIcon>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MergeFileSorter;
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Stack, Select, Checkbox, Text } from '@mantine/core';
|
import { Stack, Checkbox } from '@mantine/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { MergeParameters } from '../../../hooks/tools/merge/useMergeParameters';
|
import { MergeParameters } from '../../../hooks/tools/merge/useMergeParameters';
|
||||||
|
|
||||||
@ -16,29 +16,8 @@ const MergeSettings: React.FC<MergeSettingsProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const mergeOrderOptions = [
|
|
||||||
{ value: 'orderProvided', label: t('merge.orderBy.orderProvided', 'Dragging Files') },
|
|
||||||
{ value: 'byFileName', label: t('merge.orderBy.byFileName', 'By File Name') },
|
|
||||||
{ value: 'byDateModified', label: t('merge.orderBy.byDateModified', 'By Date Modified') },
|
|
||||||
{ value: 'byDateCreated', label: t('merge.orderBy.byDateCreated', 'By Date Created') },
|
|
||||||
{ value: 'byPDFTitle', label: t('merge.orderBy.byPDFTitle', 'By PDF Title') },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<div>
|
|
||||||
<Text size="sm" fw={500} mb="xs">
|
|
||||||
{t('merge.orderBy.title', 'Merge Order')}
|
|
||||||
</Text>
|
|
||||||
<Select
|
|
||||||
data={mergeOrderOptions}
|
|
||||||
value={parameters.mergeOrder}
|
|
||||||
onChange={(value) => onParameterChange('mergeOrder', value as MergeParameters['mergeOrder'])}
|
|
||||||
disabled={disabled}
|
|
||||||
placeholder={t('merge.orderBy.placeholder', 'Select merge order')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={t('merge.removeDigitalSignature', 'Remove digital signature in the merged file?')}
|
label={t('merge.removeDigitalSignature', 'Remove digital signature in the merged file?')}
|
||||||
checked={parameters.removeDigitalSignature}
|
checked={parameters.removeDigitalSignature}
|
||||||
|
@ -9,7 +9,7 @@ const buildFormData = (parameters: MergeParameters, files: File[]): FormData =>
|
|||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
formData.append("fileInput", file);
|
formData.append("fileInput", file);
|
||||||
});
|
});
|
||||||
formData.append("sortType", parameters.mergeOrder);
|
formData.append("sortType", "orderProvided"); // Always use orderProvided since UI handles sorting
|
||||||
formData.append("removeCertSign", parameters.removeDigitalSignature.toString());
|
formData.append("removeCertSign", parameters.removeDigitalSignature.toString());
|
||||||
formData.append("generateToc", parameters.generateTableOfContents.toString());
|
formData.append("generateToc", parameters.generateTableOfContents.toString());
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
|
||||||
export interface MergeParameters {
|
export interface MergeParameters {
|
||||||
mergeOrder: 'orderProvided' | 'byFileName' | 'byDateModified' | 'byDateCreated' | 'byPDFTitle';
|
|
||||||
removeDigitalSignature: boolean;
|
removeDigitalSignature: boolean;
|
||||||
generateTableOfContents: boolean;
|
generateTableOfContents: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultMergeParameters: MergeParameters = {
|
export const defaultMergeParameters: MergeParameters = {
|
||||||
mergeOrder: 'orderProvided',
|
|
||||||
removeDigitalSignature: false,
|
removeDigitalSignature: false,
|
||||||
generateTableOfContents: false,
|
generateTableOfContents: false,
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useCallback, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
||||||
import { useFileContext } from "../contexts/FileContext";
|
import { useFileContext } from "../contexts/FileContext";
|
||||||
import { useToolFileSelection } from "../contexts/FileSelectionContext";
|
import { useToolFileSelection, useFileSelectionActions } from "../contexts/FileSelectionContext";
|
||||||
|
|
||||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||||
import MergeSettings from "../components/tools/merge/MergeSettings";
|
import MergeSettings from "../components/tools/merge/MergeSettings";
|
||||||
|
import MergeFileSorter from "../components/tools/merge/MergeFileSorter";
|
||||||
|
|
||||||
import { useMergeParameters } from "../hooks/tools/merge/useMergeParameters";
|
import { useMergeParameters } from "../hooks/tools/merge/useMergeParameters";
|
||||||
import { useMergeOperation } from "../hooks/tools/merge/useMergeOperation";
|
import { useMergeOperation } from "../hooks/tools/merge/useMergeOperation";
|
||||||
@ -15,6 +16,7 @@ const Merge = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { setCurrentMode } = useFileContext();
|
const { setCurrentMode } = useFileContext();
|
||||||
const { selectedFiles } = useToolFileSelection();
|
const { selectedFiles } = useToolFileSelection();
|
||||||
|
const { setSelectedFiles } = useFileSelectionActions();
|
||||||
|
|
||||||
const mergeParams = useMergeParameters();
|
const mergeParams = useMergeParameters();
|
||||||
const mergeOperation = useMergeOperation();
|
const mergeOperation = useMergeOperation();
|
||||||
@ -52,6 +54,27 @@ const Merge = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
setCurrentMode("merge");
|
setCurrentMode("merge");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sortFiles = useCallback((sortType: 'filename' | 'dateModified', ascending: boolean = true) => {
|
||||||
|
setSelectedFiles(((prevFiles: File[]) => {
|
||||||
|
const sortedFiles = [...prevFiles].sort((a, b) => {
|
||||||
|
let comparison = 0;
|
||||||
|
|
||||||
|
switch (sortType) {
|
||||||
|
case 'filename':
|
||||||
|
comparison = a.name.localeCompare(b.name);
|
||||||
|
break;
|
||||||
|
case 'dateModified':
|
||||||
|
comparison = a.lastModified - b.lastModified;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ascending ? comparison : -comparison;
|
||||||
|
});
|
||||||
|
|
||||||
|
return sortedFiles;
|
||||||
|
}) as any /* FIX ME: Parameter type is wrong on setSelectedFiles */);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const hasFiles = selectedFiles.length > 1; // Merge requires at least 2 files
|
const hasFiles = selectedFiles.length > 1; // Merge requires at least 2 files
|
||||||
const hasResults = mergeOperation.files.length > 0 || mergeOperation.downloadUrl !== null;
|
const hasResults = mergeOperation.files.length > 0 || mergeOperation.downloadUrl !== null;
|
||||||
const settingsCollapsed = !hasFiles || hasResults;
|
const settingsCollapsed = !hasFiles || hasResults;
|
||||||
@ -63,6 +86,16 @@ const Merge = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
placeholder: "Select multiple PDF files to merge",
|
placeholder: "Select multiple PDF files to merge",
|
||||||
},
|
},
|
||||||
steps: [
|
steps: [
|
||||||
|
{
|
||||||
|
title: "Sort Files",
|
||||||
|
isCollapsed: settingsCollapsed,
|
||||||
|
content: (
|
||||||
|
<MergeFileSorter
|
||||||
|
onSortFiles={sortFiles}
|
||||||
|
disabled={!hasFiles || endpointLoading}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Settings",
|
title: "Settings",
|
||||||
isCollapsed: settingsCollapsed,
|
isCollapsed: settingsCollapsed,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user