mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 22:29:24 +00:00
formatting
This commit is contained in:
parent
973e6704e8
commit
bd86a58d9a
@ -37,30 +37,27 @@ const AddPassword = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
const handleAddPassword = async () => {
|
const handleAddPassword = async () => {
|
||||||
try {
|
try {
|
||||||
await addPasswordOperation.executeOperation(
|
await addPasswordOperation.executeOperation(addPasswordParams.fullParameters, selectedFiles);
|
||||||
addPasswordParams.fullParameters,
|
|
||||||
selectedFiles
|
|
||||||
);
|
|
||||||
if (addPasswordOperation.files && onComplete) {
|
if (addPasswordOperation.files && onComplete) {
|
||||||
onComplete(addPasswordOperation.files);
|
onComplete(addPasswordOperation.files);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error instanceof Error ? error.message : t('addPassword.error.failed', 'Add password operation failed'));
|
onError(error instanceof Error ? error.message : t("addPassword.error.failed", "Add password operation failed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThumbnailClick = (file: File) => {
|
const handleThumbnailClick = (file: File) => {
|
||||||
onPreviewFile?.(file);
|
onPreviewFile?.(file);
|
||||||
sessionStorage.setItem('previousMode', 'addPassword');
|
sessionStorage.setItem("previousMode", "addPassword");
|
||||||
setCurrentMode('viewer');
|
setCurrentMode("viewer");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSettingsReset = () => {
|
const handleSettingsReset = () => {
|
||||||
addPasswordOperation.resetResults();
|
addPasswordOperation.resetResults();
|
||||||
onPreviewFile?.(null);
|
onPreviewFile?.(null);
|
||||||
setCurrentMode('addPassword');
|
setCurrentMode("addPassword");
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasFiles = selectedFiles.length > 0;
|
const hasFiles = selectedFiles.length > 0;
|
||||||
@ -75,7 +72,7 @@ const AddPassword = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
},
|
},
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
title: t('addPassword.passwords.stepTitle', 'Passwords & Encryption'),
|
title: t("addPassword.passwords.stepTitle", "Passwords & Encryption"),
|
||||||
isCollapsed: passwordsCollapsed,
|
isCollapsed: passwordsCollapsed,
|
||||||
onCollapsedClick: hasResults ? handleSettingsReset : undefined,
|
onCollapsedClick: hasResults ? handleSettingsReset : undefined,
|
||||||
tooltip: addPasswordTips,
|
tooltip: addPasswordTips,
|
||||||
@ -85,10 +82,10 @@ const AddPassword = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
onParameterChange={addPasswordParams.updateParameter}
|
onParameterChange={addPasswordParams.updateParameter}
|
||||||
disabled={endpointLoading}
|
disabled={endpointLoading}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('changePermissions.title', 'Document Permissions'),
|
title: t("changePermissions.title", "Document Permissions"),
|
||||||
isCollapsed: permissionsCollapsed,
|
isCollapsed: permissionsCollapsed,
|
||||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedPermissions(!collapsedPermissions),
|
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedPermissions(!collapsedPermissions),
|
||||||
content: (
|
content: (
|
||||||
@ -97,23 +94,23 @@ const AddPassword = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
onParameterChange={addPasswordParams.permissions.updateParameter}
|
onParameterChange={addPasswordParams.permissions.updateParameter}
|
||||||
disabled={endpointLoading}
|
disabled={endpointLoading}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
executeButton: {
|
executeButton: {
|
||||||
text: t('addPassword.submit', 'Encrypt'),
|
text: t("addPassword.submit", "Encrypt"),
|
||||||
isVisible: !hasResults,
|
isVisible: !hasResults,
|
||||||
loadingText: t('loading'),
|
loadingText: t("loading"),
|
||||||
onClick: handleAddPassword,
|
onClick: handleAddPassword,
|
||||||
disabled: !addPasswordParams.validateParameters() || !hasFiles || !endpointEnabled
|
disabled: !addPasswordParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||||
},
|
},
|
||||||
review: {
|
review: {
|
||||||
isVisible: hasResults,
|
isVisible: hasResults,
|
||||||
operation: addPasswordOperation,
|
operation: addPasswordOperation,
|
||||||
title: t('addPassword.results.title', 'Encrypted PDFs'),
|
title: t("addPassword.results.title", "Encrypted PDFs"),
|
||||||
onFileClick: handleThumbnailClick
|
onFileClick: handleThumbnailClick,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export default AddPassword;
|
export default AddPassword;
|
||||||
|
@ -32,30 +32,29 @@ const ChangePermissions = ({ onPreviewFile, onComplete, onError }: BaseToolProps
|
|||||||
|
|
||||||
const handleChangePermissions = async () => {
|
const handleChangePermissions = async () => {
|
||||||
try {
|
try {
|
||||||
await changePermissionsOperation.executeOperation(
|
await changePermissionsOperation.executeOperation(changePermissionsParams.parameters, selectedFiles);
|
||||||
changePermissionsParams.parameters,
|
|
||||||
selectedFiles
|
|
||||||
);
|
|
||||||
if (changePermissionsOperation.files && onComplete) {
|
if (changePermissionsOperation.files && onComplete) {
|
||||||
onComplete(changePermissionsOperation.files);
|
onComplete(changePermissionsOperation.files);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error instanceof Error ? error.message : t('changePermissions.error.failed', 'Change permissions operation failed'));
|
onError(
|
||||||
|
error instanceof Error ? error.message : t("changePermissions.error.failed", "Change permissions operation failed")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThumbnailClick = (file: File) => {
|
const handleThumbnailClick = (file: File) => {
|
||||||
onPreviewFile?.(file);
|
onPreviewFile?.(file);
|
||||||
sessionStorage.setItem('previousMode', 'changePermissions');
|
sessionStorage.setItem("previousMode", "changePermissions");
|
||||||
setCurrentMode('viewer');
|
setCurrentMode("viewer");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSettingsReset = () => {
|
const handleSettingsReset = () => {
|
||||||
changePermissionsOperation.resetResults();
|
changePermissionsOperation.resetResults();
|
||||||
onPreviewFile?.(null);
|
onPreviewFile?.(null);
|
||||||
setCurrentMode('changePermissions');
|
setCurrentMode("changePermissions");
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasFiles = selectedFiles.length > 0;
|
const hasFiles = selectedFiles.length > 0;
|
||||||
@ -67,33 +66,35 @@ const ChangePermissions = ({ onPreviewFile, onComplete, onError }: BaseToolProps
|
|||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: hasFiles || hasResults,
|
isCollapsed: hasFiles || hasResults,
|
||||||
},
|
},
|
||||||
steps: [{
|
steps: [
|
||||||
title: t('changePermissions.title', 'Document Permissions'),
|
{
|
||||||
isCollapsed: settingsCollapsed,
|
title: t("changePermissions.title", "Document Permissions"),
|
||||||
onCollapsedClick: settingsCollapsed ? handleSettingsReset : undefined,
|
isCollapsed: settingsCollapsed,
|
||||||
tooltip: changePermissionsTips,
|
onCollapsedClick: settingsCollapsed ? handleSettingsReset : undefined,
|
||||||
content: (
|
tooltip: changePermissionsTips,
|
||||||
<ChangePermissionsSettings
|
content: (
|
||||||
parameters={changePermissionsParams.parameters}
|
<ChangePermissionsSettings
|
||||||
onParameterChange={changePermissionsParams.updateParameter}
|
parameters={changePermissionsParams.parameters}
|
||||||
disabled={endpointLoading}
|
onParameterChange={changePermissionsParams.updateParameter}
|
||||||
/>
|
disabled={endpointLoading}
|
||||||
)
|
/>
|
||||||
}],
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
executeButton: {
|
executeButton: {
|
||||||
text: t('changePermissions.submit', 'Change Permissions'),
|
text: t("changePermissions.submit", "Change Permissions"),
|
||||||
isVisible: !hasResults,
|
isVisible: !hasResults,
|
||||||
loadingText: t('loading'),
|
loadingText: t("loading"),
|
||||||
onClick: handleChangePermissions,
|
onClick: handleChangePermissions,
|
||||||
disabled: !changePermissionsParams.validateParameters() || !hasFiles || !endpointEnabled
|
disabled: !changePermissionsParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||||
},
|
},
|
||||||
review: {
|
review: {
|
||||||
isVisible: hasResults,
|
isVisible: hasResults,
|
||||||
operation: changePermissionsOperation,
|
operation: changePermissionsOperation,
|
||||||
title: t('changePermissions.results.title', 'Modified PDFs'),
|
title: t("changePermissions.results.title", "Modified PDFs"),
|
||||||
onFileClick: handleThumbnailClick
|
onFileClick: handleThumbnailClick,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export default ChangePermissions;
|
export default ChangePermissions;
|
||||||
|
@ -32,30 +32,27 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
const handleCompress = async () => {
|
const handleCompress = async () => {
|
||||||
try {
|
try {
|
||||||
await compressOperation.executeOperation(
|
await compressOperation.executeOperation(compressParams.parameters, selectedFiles);
|
||||||
compressParams.parameters,
|
|
||||||
selectedFiles
|
|
||||||
);
|
|
||||||
if (compressOperation.files && onComplete) {
|
if (compressOperation.files && onComplete) {
|
||||||
onComplete(compressOperation.files);
|
onComplete(compressOperation.files);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error instanceof Error ? error.message : 'Compress operation failed');
|
onError(error instanceof Error ? error.message : "Compress operation failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThumbnailClick = (file: File) => {
|
const handleThumbnailClick = (file: File) => {
|
||||||
onPreviewFile?.(file);
|
onPreviewFile?.(file);
|
||||||
sessionStorage.setItem('previousMode', 'compress');
|
sessionStorage.setItem("previousMode", "compress");
|
||||||
setCurrentMode('viewer');
|
setCurrentMode("viewer");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSettingsReset = () => {
|
const handleSettingsReset = () => {
|
||||||
compressOperation.resetResults();
|
compressOperation.resetResults();
|
||||||
onPreviewFile?.(null);
|
onPreviewFile?.(null);
|
||||||
setCurrentMode('compress');
|
setCurrentMode("compress");
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasFiles = selectedFiles.length > 0;
|
const hasFiles = selectedFiles.length > 0;
|
||||||
@ -63,38 +60,39 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const settingsCollapsed = !hasFiles || hasResults;
|
const settingsCollapsed = !hasFiles || hasResults;
|
||||||
|
|
||||||
return createToolFlow({
|
return createToolFlow({
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: hasFiles && !hasResults,
|
isCollapsed: hasFiles && !hasResults,
|
||||||
},
|
},
|
||||||
steps: [{
|
steps: [
|
||||||
title: "Settings",
|
{
|
||||||
isCollapsed: settingsCollapsed,
|
title: "Settings",
|
||||||
onCollapsedClick: settingsCollapsed ? handleSettingsReset : undefined,
|
isCollapsed: settingsCollapsed,
|
||||||
tooltip: compressTips,
|
onCollapsedClick: settingsCollapsed ? handleSettingsReset : undefined,
|
||||||
content: (
|
tooltip: compressTips,
|
||||||
<CompressSettings
|
content: (
|
||||||
parameters={compressParams.parameters}
|
<CompressSettings
|
||||||
onParameterChange={compressParams.updateParameter}
|
parameters={compressParams.parameters}
|
||||||
disabled={endpointLoading}
|
onParameterChange={compressParams.updateParameter}
|
||||||
/>
|
disabled={endpointLoading}
|
||||||
)
|
/>
|
||||||
}],
|
),
|
||||||
executeButton: {
|
},
|
||||||
text: t("compress.submit", "Compress"),
|
],
|
||||||
isVisible: !hasResults,
|
executeButton: {
|
||||||
loadingText: t("loading"),
|
text: t("compress.submit", "Compress"),
|
||||||
onClick: handleCompress,
|
isVisible: !hasResults,
|
||||||
disabled: !compressParams.validateParameters() || !hasFiles || !endpointEnabled
|
loadingText: t("loading"),
|
||||||
},
|
onClick: handleCompress,
|
||||||
review: {
|
disabled: !compressParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||||
isVisible: hasResults,
|
},
|
||||||
operation: compressOperation,
|
review: {
|
||||||
title: t("compress.title", "Compression Results"),
|
isVisible: hasResults,
|
||||||
onFileClick: handleThumbnailClick
|
operation: compressOperation,
|
||||||
}
|
title: t("compress.title", "Compression Results"),
|
||||||
});
|
onFileClick: handleThumbnailClick,
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default Compress;
|
export default Compress;
|
||||||
|
@ -21,15 +21,13 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const convertParams = useConvertParameters();
|
const convertParams = useConvertParameters();
|
||||||
const convertOperation = useConvertOperation();
|
const convertOperation = useConvertOperation();
|
||||||
|
|
||||||
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(
|
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(convertParams.getEndpointName());
|
||||||
convertParams.getEndpointName()
|
|
||||||
);
|
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
if (scrollContainerRef.current) {
|
if (scrollContainerRef.current) {
|
||||||
scrollContainerRef.current.scrollTo({
|
scrollContainerRef.current.scrollTo({
|
||||||
top: scrollContainerRef.current.scrollHeight,
|
top: scrollContainerRef.current.scrollHeight,
|
||||||
behavior: 'smooth'
|
behavior: "smooth",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -73,68 +71,67 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
const handleConvert = async () => {
|
const handleConvert = async () => {
|
||||||
try {
|
try {
|
||||||
await convertOperation.executeOperation(
|
await convertOperation.executeOperation(convertParams.parameters, selectedFiles);
|
||||||
convertParams.parameters,
|
|
||||||
selectedFiles
|
|
||||||
);
|
|
||||||
if (convertOperation.files && onComplete) {
|
if (convertOperation.files && onComplete) {
|
||||||
onComplete(convertOperation.files);
|
onComplete(convertOperation.files);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error instanceof Error ? error.message : 'Convert operation failed');
|
onError(error instanceof Error ? error.message : "Convert operation failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThumbnailClick = (file: File) => {
|
const handleThumbnailClick = (file: File) => {
|
||||||
onPreviewFile?.(file);
|
onPreviewFile?.(file);
|
||||||
sessionStorage.setItem('previousMode', 'convert');
|
sessionStorage.setItem("previousMode", "convert");
|
||||||
setCurrentMode('viewer');
|
setCurrentMode("viewer");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSettingsReset = () => {
|
const handleSettingsReset = () => {
|
||||||
convertOperation.resetResults();
|
convertOperation.resetResults();
|
||||||
onPreviewFile?.(null);
|
onPreviewFile?.(null);
|
||||||
setCurrentMode('convert');
|
setCurrentMode("convert");
|
||||||
};
|
};
|
||||||
|
|
||||||
return createToolFlow({
|
return createToolFlow({
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: filesCollapsed,
|
isCollapsed: filesCollapsed,
|
||||||
placeholder: t("convert.selectFilesPlaceholder", "Select files in the main view to get started")
|
placeholder: t("convert.selectFilesPlaceholder", "Select files in the main view to get started"),
|
||||||
},
|
},
|
||||||
steps: [{
|
steps: [
|
||||||
title: t("convert.settings", "Settings"),
|
{
|
||||||
isCollapsed: settingsCollapsed,
|
title: t("convert.settings", "Settings"),
|
||||||
onCollapsedClick: settingsCollapsed ? handleSettingsReset : undefined,
|
isCollapsed: settingsCollapsed,
|
||||||
content: (
|
onCollapsedClick: settingsCollapsed ? handleSettingsReset : undefined,
|
||||||
<ConvertSettings
|
content: (
|
||||||
parameters={convertParams.parameters}
|
<ConvertSettings
|
||||||
onParameterChange={convertParams.updateParameter}
|
parameters={convertParams.parameters}
|
||||||
getAvailableToExtensions={convertParams.getAvailableToExtensions}
|
onParameterChange={convertParams.updateParameter}
|
||||||
selectedFiles={selectedFiles}
|
getAvailableToExtensions={convertParams.getAvailableToExtensions}
|
||||||
disabled={endpointLoading}
|
selectedFiles={selectedFiles}
|
||||||
/>
|
disabled={endpointLoading}
|
||||||
)
|
/>
|
||||||
}],
|
),
|
||||||
executeButton: {
|
},
|
||||||
text: t("convert.convertFiles", "Convert Files"),
|
],
|
||||||
loadingText: t("convert.converting", "Converting..."),
|
executeButton: {
|
||||||
onClick: handleConvert,
|
text: t("convert.convertFiles", "Convert Files"),
|
||||||
isVisible: !hasResults,
|
loadingText: t("convert.converting", "Converting..."),
|
||||||
disabled: !convertParams.validateParameters() || !hasFiles || !endpointEnabled,
|
onClick: handleConvert,
|
||||||
testId: "convert-button"
|
isVisible: !hasResults,
|
||||||
},
|
disabled: !convertParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||||
review: {
|
testId: "convert-button",
|
||||||
isVisible: hasResults,
|
},
|
||||||
operation: convertOperation,
|
review: {
|
||||||
title: t("convert.conversionResults", "Conversion Results"),
|
isVisible: hasResults,
|
||||||
onFileClick: handleThumbnailClick,
|
operation: convertOperation,
|
||||||
testId: "conversion-results"
|
title: t("convert.conversionResults", "Conversion Results"),
|
||||||
}
|
onFileClick: handleThumbnailClick,
|
||||||
});
|
testId: "conversion-results",
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Convert;
|
export default Convert;
|
||||||
|
@ -15,12 +15,7 @@ export interface MergePdfPanelProps {
|
|||||||
updateParams: (newParams: Partial<MergePdfPanelProps["params"]>) => void;
|
updateParams: (newParams: Partial<MergePdfPanelProps["params"]>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MergePdfPanel: React.FC<MergePdfPanelProps> = ({
|
const MergePdfPanel: React.FC<MergePdfPanelProps> = ({ files, setDownloadUrl, params, updateParams }) => {
|
||||||
files,
|
|
||||||
setDownloadUrl,
|
|
||||||
params,
|
|
||||||
updateParams,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [selectedFiles, setSelectedFiles] = useState<boolean[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<boolean[]>([]);
|
||||||
const [downloadUrl, setLocalDownloadUrl] = useState<string | null>(null);
|
const [downloadUrl, setLocalDownloadUrl] = useState<string | null>(null);
|
||||||
@ -51,7 +46,7 @@ const MergePdfPanel: React.FC<MergePdfPanelProps> = ({
|
|||||||
const blob = new Blob([storedFile.data], { type: storedFile.type });
|
const blob = new Blob([storedFile.data], { type: storedFile.type });
|
||||||
const actualFile = new File([blob], storedFile.name, {
|
const actualFile = new File([blob], storedFile.name, {
|
||||||
type: storedFile.type,
|
type: storedFile.type,
|
||||||
lastModified: storedFile.lastModified
|
lastModified: storedFile.lastModified,
|
||||||
});
|
});
|
||||||
formData.append("fileInput", actualFile);
|
formData.append("fileInput", actualFile);
|
||||||
}
|
}
|
||||||
@ -83,9 +78,7 @@ const MergePdfPanel: React.FC<MergePdfPanelProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCheckboxChange = (index: number) => {
|
const handleCheckboxChange = (index: number) => {
|
||||||
setSelectedFiles((prev) =>
|
setSelectedFiles((prev) => prev.map((selected, i) => (i === index ? !selected : selected)));
|
||||||
prev.map((selected, i) => (i === index ? !selected : selected))
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectedCount = selectedFiles.filter(Boolean).length;
|
const selectedCount = selectedFiles.filter(Boolean).length;
|
||||||
@ -96,7 +89,9 @@ const MergePdfPanel: React.FC<MergePdfPanelProps> = ({
|
|||||||
return (
|
return (
|
||||||
<Stack align="center" justify="center" h={200}>
|
<Stack align="center" justify="center" h={200}>
|
||||||
<Loader size="md" />
|
<Loader size="md" />
|
||||||
<Text size="sm" c="dimmed">{t("loading", "Loading...")}</Text>
|
<Text size="sm" c="dimmed">
|
||||||
|
{t("loading", "Loading...")}
|
||||||
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -112,55 +107,42 @@ const MergePdfPanel: React.FC<MergePdfPanelProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text fw={500} size="lg">{t("merge.header")}</Text>
|
<Text fw={500} size="lg">
|
||||||
<Stack gap={4}>
|
{t("merge.header")}
|
||||||
{files.map((file, index) => (
|
</Text>
|
||||||
<Group key={index} gap="xs">
|
<Stack gap={4}>
|
||||||
<Checkbox
|
{files.map((file, index) => (
|
||||||
checked={selectedFiles[index] || false}
|
<Group key={index} gap="xs">
|
||||||
onChange={() => handleCheckboxChange(index)}
|
<Checkbox checked={selectedFiles[index] || false} onChange={() => handleCheckboxChange(index)} />
|
||||||
/>
|
<Text size="sm">{file.name}</Text>
|
||||||
<Text size="sm">{file.name}</Text>
|
</Group>
|
||||||
</Group>
|
))}
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
{selectedCount < 2 && (
|
|
||||||
<Text size="sm" c="red">
|
|
||||||
{t("multiPdfPrompt")}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
onClick={handleMerge}
|
|
||||||
loading={isLoading}
|
|
||||||
disabled={selectedCount < 2 || isLoading}
|
|
||||||
mt="md"
|
|
||||||
>
|
|
||||||
{t("merge.submit")}
|
|
||||||
</Button>
|
|
||||||
{errorMessage && (
|
|
||||||
<Alert color="red" mt="sm">
|
|
||||||
{errorMessage}
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
{downloadUrl && (
|
|
||||||
<Button
|
|
||||||
component="a"
|
|
||||||
href={downloadUrl}
|
|
||||||
download="merged.pdf"
|
|
||||||
color="green"
|
|
||||||
variant="light"
|
|
||||||
mt="md"
|
|
||||||
>
|
|
||||||
{t("downloadPdf")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Checkbox
|
|
||||||
label={t("merge.removeCertSign")}
|
|
||||||
checked={removeDuplicates}
|
|
||||||
onChange={() => updateParams({ removeDuplicates: !removeDuplicates })}
|
|
||||||
/>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
|
{selectedCount < 2 && (
|
||||||
|
<Text size="sm" c="red">
|
||||||
|
{t("multiPdfPrompt")}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<Button onClick={handleMerge} loading={isLoading} disabled={selectedCount < 2 || isLoading} mt="md">
|
||||||
|
{t("merge.submit")}
|
||||||
|
</Button>
|
||||||
|
{errorMessage && (
|
||||||
|
<Alert color="red" mt="sm">
|
||||||
|
{errorMessage}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
{downloadUrl && (
|
||||||
|
<Button component="a" href={downloadUrl} download="merged.pdf" color="green" variant="light" mt="md">
|
||||||
|
{t("downloadPdf")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Checkbox
|
||||||
|
label={t("merge.removeCertSign")}
|
||||||
|
checked={removeDuplicates}
|
||||||
|
onChange={() => updateParams({ removeDuplicates: !removeDuplicates })}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ const OCR = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const ocrTips = useOCRTips();
|
const ocrTips = useOCRTips();
|
||||||
|
|
||||||
// Step expansion state management
|
// Step expansion state management
|
||||||
const [expandedStep, setExpandedStep] = useState<'files' | 'settings' | 'advanced' | null>('files');
|
const [expandedStep, setExpandedStep] = useState<"files" | "settings" | "advanced" | null>("files");
|
||||||
|
|
||||||
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled("ocr-pdf");
|
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled("ocr-pdf");
|
||||||
|
|
||||||
@ -38,8 +38,8 @@ const OCR = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
}, [ocrParams.parameters]);
|
}, [ocrParams.parameters]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedFiles.length > 0 && expandedStep === 'files') {
|
if (selectedFiles.length > 0 && expandedStep === "files") {
|
||||||
setExpandedStep('settings');
|
setExpandedStep("settings");
|
||||||
}
|
}
|
||||||
}, [selectedFiles.length, expandedStep]);
|
}, [selectedFiles.length, expandedStep]);
|
||||||
|
|
||||||
@ -52,88 +52,88 @@ const OCR = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
const handleOCR = async () => {
|
const handleOCR = async () => {
|
||||||
try {
|
try {
|
||||||
await ocrOperation.executeOperation(
|
await ocrOperation.executeOperation(ocrParams.parameters, selectedFiles);
|
||||||
ocrParams.parameters,
|
|
||||||
selectedFiles
|
|
||||||
);
|
|
||||||
if (ocrOperation.files && onComplete) {
|
if (ocrOperation.files && onComplete) {
|
||||||
onComplete(ocrOperation.files);
|
onComplete(ocrOperation.files);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error instanceof Error ? error.message : 'OCR operation failed');
|
onError(error instanceof Error ? error.message : "OCR operation failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThumbnailClick = (file: File) => {
|
const handleThumbnailClick = (file: File) => {
|
||||||
onPreviewFile?.(file);
|
onPreviewFile?.(file);
|
||||||
sessionStorage.setItem('previousMode', 'ocr');
|
sessionStorage.setItem("previousMode", "ocr");
|
||||||
setCurrentMode('viewer');
|
setCurrentMode("viewer");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSettingsReset = () => {
|
const handleSettingsReset = () => {
|
||||||
ocrOperation.resetResults();
|
ocrOperation.resetResults();
|
||||||
onPreviewFile?.(null);
|
onPreviewFile?.(null);
|
||||||
setCurrentMode('ocr');
|
setCurrentMode("ocr");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const settingsCollapsed = expandedStep !== "settings";
|
||||||
const settingsCollapsed = expandedStep !== 'settings';
|
|
||||||
|
|
||||||
return createToolFlow({
|
return createToolFlow({
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: hasFiles || hasResults,
|
isCollapsed: hasFiles || hasResults,
|
||||||
},
|
},
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
title: "Settings",
|
title: "Settings",
|
||||||
isCollapsed: !hasFiles || settingsCollapsed,
|
isCollapsed: !hasFiles || settingsCollapsed,
|
||||||
onCollapsedClick: hasResults ? handleSettingsReset : () => {
|
onCollapsedClick: hasResults
|
||||||
|
? handleSettingsReset
|
||||||
|
: () => {
|
||||||
if (!hasFiles) return; // Only allow if files are selected
|
if (!hasFiles) return; // Only allow if files are selected
|
||||||
setExpandedStep(expandedStep === 'settings' ? null : 'settings');
|
setExpandedStep(expandedStep === "settings" ? null : "settings");
|
||||||
},
|
},
|
||||||
tooltip: ocrTips,
|
tooltip: ocrTips,
|
||||||
content: (
|
content: (
|
||||||
<OCRSettings
|
<OCRSettings
|
||||||
parameters={ocrParams.parameters}
|
parameters={ocrParams.parameters}
|
||||||
onParameterChange={ocrParams.updateParameter}
|
onParameterChange={ocrParams.updateParameter}
|
||||||
disabled={endpointLoading}
|
disabled={endpointLoading}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Advanced",
|
title: "Advanced",
|
||||||
isCollapsed: expandedStep !== 'advanced',
|
isCollapsed: expandedStep !== "advanced",
|
||||||
onCollapsedClick: hasResults ? handleSettingsReset : () => {
|
onCollapsedClick: hasResults
|
||||||
|
? handleSettingsReset
|
||||||
|
: () => {
|
||||||
if (!hasFiles) return; // Only allow if files are selected
|
if (!hasFiles) return; // Only allow if files are selected
|
||||||
setExpandedStep(expandedStep === 'advanced' ? null : 'advanced');
|
setExpandedStep(expandedStep === "advanced" ? null : "advanced");
|
||||||
},
|
},
|
||||||
content: (
|
content: (
|
||||||
<AdvancedOCRSettings
|
<AdvancedOCRSettings
|
||||||
advancedOptions={ocrParams.parameters.additionalOptions}
|
advancedOptions={ocrParams.parameters.additionalOptions}
|
||||||
ocrRenderType={ocrParams.parameters.ocrRenderType}
|
ocrRenderType={ocrParams.parameters.ocrRenderType}
|
||||||
onParameterChange={ocrParams.updateParameter}
|
onParameterChange={ocrParams.updateParameter}
|
||||||
disabled={endpointLoading}
|
disabled={endpointLoading}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
executeButton: {
|
executeButton: {
|
||||||
text: t("ocr.operation.submit", "Process OCR and Review"),
|
text: t("ocr.operation.submit", "Process OCR and Review"),
|
||||||
loadingText: t("loading"),
|
loadingText: t("loading"),
|
||||||
onClick: handleOCR,
|
onClick: handleOCR,
|
||||||
isVisible: hasValidSettings && !hasResults,
|
isVisible: hasValidSettings && !hasResults,
|
||||||
disabled: !ocrParams.validateParameters() || !hasFiles || !endpointEnabled
|
disabled: !ocrParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||||
},
|
},
|
||||||
review: {
|
review: {
|
||||||
isVisible: hasResults,
|
isVisible: hasResults,
|
||||||
operation: ocrOperation,
|
operation: ocrOperation,
|
||||||
title: t("ocr.results.title", "OCR Results"),
|
title: t("ocr.results.title", "OCR Results"),
|
||||||
onFileClick: handleThumbnailClick
|
onFileClick: handleThumbnailClick,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export default OCR;
|
export default OCR;
|
||||||
|
@ -21,9 +21,7 @@ const Sanitize = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const sanitizeOperation = useSanitizeOperation();
|
const sanitizeOperation = useSanitizeOperation();
|
||||||
|
|
||||||
// Endpoint validation
|
// Endpoint validation
|
||||||
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(
|
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(sanitizeParams.getEndpointName());
|
||||||
sanitizeParams.getEndpointName()
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sanitizeOperation.resetResults();
|
sanitizeOperation.resetResults();
|
||||||
@ -32,16 +30,13 @@ const Sanitize = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
const handleSanitize = async () => {
|
const handleSanitize = async () => {
|
||||||
try {
|
try {
|
||||||
await sanitizeOperation.executeOperation(
|
await sanitizeOperation.executeOperation(sanitizeParams.parameters, selectedFiles);
|
||||||
sanitizeParams.parameters,
|
|
||||||
selectedFiles,
|
|
||||||
);
|
|
||||||
if (sanitizeOperation.files && onComplete) {
|
if (sanitizeOperation.files && onComplete) {
|
||||||
onComplete(sanitizeOperation.files);
|
onComplete(sanitizeOperation.files);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error instanceof Error ? error.message : t('sanitize.error.generic', 'Sanitization failed'));
|
onError(error instanceof Error ? error.message : t("sanitize.error.generic", "Sanitization failed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -49,13 +44,13 @@ const Sanitize = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const handleSettingsReset = () => {
|
const handleSettingsReset = () => {
|
||||||
sanitizeOperation.resetResults();
|
sanitizeOperation.resetResults();
|
||||||
onPreviewFile?.(null);
|
onPreviewFile?.(null);
|
||||||
setCurrentMode('sanitize');
|
setCurrentMode("sanitize");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThumbnailClick = (file: File) => {
|
const handleThumbnailClick = (file: File) => {
|
||||||
onPreviewFile?.(file);
|
onPreviewFile?.(file);
|
||||||
sessionStorage.setItem('previousMode', 'sanitize');
|
sessionStorage.setItem("previousMode", "sanitize");
|
||||||
setCurrentMode('viewer');
|
setCurrentMode("viewer");
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasFiles = selectedFiles.length > 0;
|
const hasFiles = selectedFiles.length > 0;
|
||||||
@ -64,37 +59,39 @@ const Sanitize = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const settingsCollapsed = !hasFiles || hasResults;
|
const settingsCollapsed = !hasFiles || hasResults;
|
||||||
|
|
||||||
return createToolFlow({
|
return createToolFlow({
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: filesCollapsed,
|
isCollapsed: filesCollapsed,
|
||||||
placeholder: t('sanitize.files.placeholder', 'Select a PDF file in the main view to get started')
|
placeholder: t("sanitize.files.placeholder", "Select a PDF file in the main view to get started"),
|
||||||
},
|
},
|
||||||
steps: [{
|
steps: [
|
||||||
title: t('sanitize.steps.settings', 'Settings'),
|
{
|
||||||
isCollapsed: settingsCollapsed,
|
title: t("sanitize.steps.settings", "Settings"),
|
||||||
onCollapsedClick: settingsCollapsed ? handleSettingsReset : undefined,
|
isCollapsed: settingsCollapsed,
|
||||||
content: (
|
onCollapsedClick: settingsCollapsed ? handleSettingsReset : undefined,
|
||||||
<SanitizeSettings
|
content: (
|
||||||
parameters={sanitizeParams.parameters}
|
<SanitizeSettings
|
||||||
onParameterChange={sanitizeParams.updateParameter}
|
parameters={sanitizeParams.parameters}
|
||||||
disabled={endpointLoading}
|
onParameterChange={sanitizeParams.updateParameter}
|
||||||
/>
|
disabled={endpointLoading}
|
||||||
)
|
/>
|
||||||
}],
|
),
|
||||||
executeButton: {
|
},
|
||||||
text: t("sanitize.submit", "Sanitize PDF"),
|
],
|
||||||
isVisible: !hasResults,
|
executeButton: {
|
||||||
loadingText: t("loading"),
|
text: t("sanitize.submit", "Sanitize PDF"),
|
||||||
onClick: handleSanitize,
|
isVisible: !hasResults,
|
||||||
disabled: !sanitizeParams.validateParameters() || !hasFiles || !endpointEnabled
|
loadingText: t("loading"),
|
||||||
},
|
onClick: handleSanitize,
|
||||||
review: {
|
disabled: !sanitizeParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||||
isVisible: hasResults,
|
},
|
||||||
operation: sanitizeOperation,
|
review: {
|
||||||
title: t("sanitize.sanitizationResults", "Sanitization Results"),
|
isVisible: hasResults,
|
||||||
onFileClick: handleThumbnailClick
|
operation: sanitizeOperation,
|
||||||
}
|
title: t("sanitize.sanitizationResults", "Sanitization Results"),
|
||||||
});
|
onFileClick: handleThumbnailClick,
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default Sanitize;
|
export default Sanitize;
|
||||||
|
@ -20,9 +20,7 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const splitOperation = useSplitOperation();
|
const splitOperation = useSplitOperation();
|
||||||
|
|
||||||
// Endpoint validation
|
// Endpoint validation
|
||||||
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(
|
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled(splitParams.getEndpointName());
|
||||||
splitParams.getEndpointName()
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
splitOperation.resetResults();
|
splitOperation.resetResults();
|
||||||
@ -31,30 +29,27 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
const handleSplit = async () => {
|
const handleSplit = async () => {
|
||||||
try {
|
try {
|
||||||
await splitOperation.executeOperation(
|
await splitOperation.executeOperation(splitParams.parameters, selectedFiles);
|
||||||
splitParams.parameters,
|
|
||||||
selectedFiles
|
|
||||||
);
|
|
||||||
if (splitOperation.files && onComplete) {
|
if (splitOperation.files && onComplete) {
|
||||||
onComplete(splitOperation.files);
|
onComplete(splitOperation.files);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error instanceof Error ? error.message : 'Split operation failed');
|
onError(error instanceof Error ? error.message : "Split operation failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThumbnailClick = (file: File) => {
|
const handleThumbnailClick = (file: File) => {
|
||||||
onPreviewFile?.(file);
|
onPreviewFile?.(file);
|
||||||
sessionStorage.setItem('previousMode', 'split');
|
sessionStorage.setItem("previousMode", "split");
|
||||||
setCurrentMode('viewer');
|
setCurrentMode("viewer");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSettingsReset = () => {
|
const handleSettingsReset = () => {
|
||||||
splitOperation.resetResults();
|
splitOperation.resetResults();
|
||||||
onPreviewFile?.(null);
|
onPreviewFile?.(null);
|
||||||
setCurrentMode('split');
|
setCurrentMode("split");
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasFiles = selectedFiles.length > 0;
|
const hasFiles = selectedFiles.length > 0;
|
||||||
@ -63,37 +58,39 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const settingsCollapsed = !hasFiles || hasResults;
|
const settingsCollapsed = !hasFiles || hasResults;
|
||||||
|
|
||||||
return createToolFlow({
|
return createToolFlow({
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: filesCollapsed,
|
isCollapsed: filesCollapsed,
|
||||||
placeholder: "Select a PDF file in the main view to get started"
|
placeholder: "Select a PDF file in the main view to get started",
|
||||||
},
|
},
|
||||||
steps: [{
|
steps: [
|
||||||
title: "Settings",
|
{
|
||||||
isCollapsed: settingsCollapsed,
|
title: "Settings",
|
||||||
onCollapsedClick: hasResults ? handleSettingsReset : undefined,
|
isCollapsed: settingsCollapsed,
|
||||||
content: (
|
onCollapsedClick: hasResults ? handleSettingsReset : undefined,
|
||||||
<SplitSettings
|
content: (
|
||||||
parameters={splitParams.parameters}
|
<SplitSettings
|
||||||
onParameterChange={splitParams.updateParameter}
|
parameters={splitParams.parameters}
|
||||||
disabled={endpointLoading}
|
onParameterChange={splitParams.updateParameter}
|
||||||
/>
|
disabled={endpointLoading}
|
||||||
)
|
/>
|
||||||
}],
|
),
|
||||||
executeButton: {
|
},
|
||||||
text: t("split.submit", "Split PDF"),
|
],
|
||||||
loadingText: t("loading"),
|
executeButton: {
|
||||||
onClick: handleSplit,
|
text: t("split.submit", "Split PDF"),
|
||||||
isVisible: !hasResults,
|
loadingText: t("loading"),
|
||||||
disabled: !splitParams.validateParameters() || !hasFiles || !endpointEnabled
|
onClick: handleSplit,
|
||||||
},
|
isVisible: !hasResults,
|
||||||
review: {
|
disabled: !splitParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||||
isVisible: hasResults,
|
},
|
||||||
operation: splitOperation,
|
review: {
|
||||||
title: "Split Results",
|
isVisible: hasResults,
|
||||||
onFileClick: handleThumbnailClick
|
operation: splitOperation,
|
||||||
}
|
title: "Split Results",
|
||||||
});
|
onFileClick: handleThumbnailClick,
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default Split;
|
export default Split;
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from "react";
|
||||||
import { BaseToolProps } from '../types/tool';
|
import { BaseToolProps } from "../types/tool";
|
||||||
|
|
||||||
const SwaggerUI: React.FC<BaseToolProps> = () => {
|
const SwaggerUI: React.FC<BaseToolProps> = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Redirect to Swagger UI
|
// Redirect to Swagger UI
|
||||||
window.open('/swagger-ui/5.21.0/index.html', '_blank');
|
window.open("/swagger-ui/5.21.0/index.html", "_blank");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ textAlign: 'center', padding: '2rem' }}>
|
<div style={{ textAlign: "center", padding: "2rem" }}>
|
||||||
<p>Opening Swagger UI in a new tab...</p>
|
<p>Opening Swagger UI in a new tab...</p>
|
||||||
<p>If it didn't open automatically, <a href="/swagger-ui/5.21.0/index.html" target="_blank" rel="noopener noreferrer">click here</a></p>
|
<p>
|
||||||
|
If it didn't open automatically,{" "}
|
||||||
|
<a href="/swagger-ui/5.21.0/index.html" target="_blank" rel="noopener noreferrer">
|
||||||
|
click here
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SwaggerUI;
|
export default SwaggerUI;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user