mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-15 19:10:47 +02:00
Desktop/remove hard requirement auth wall on desktop (#5956)
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
081b1ec49e
commit
2e2b55e87d
@@ -78,7 +78,8 @@ export const ONBOARDING_STEPS: OnboardingStep[] = [
|
||||
id: 'welcome',
|
||||
type: 'modal-slide',
|
||||
slideId: 'welcome',
|
||||
condition: () => true,
|
||||
// Desktop has its own onboarding modal (DesktopOnboardingModal)
|
||||
condition: (ctx) => !ctx.isDesktopApp,
|
||||
},
|
||||
{
|
||||
id: 'admin-overview',
|
||||
@@ -107,7 +108,7 @@ export const ONBOARDING_STEPS: OnboardingStep[] = [
|
||||
id: 'tour-overview',
|
||||
type: 'modal-slide',
|
||||
slideId: 'tour-overview',
|
||||
condition: (ctx) => !ctx.effectiveIsAdmin && ctx.tourType !== 'admin',
|
||||
condition: (ctx) => !ctx.effectiveIsAdmin && ctx.tourType !== 'admin' && !ctx.isDesktopApp,
|
||||
},
|
||||
{
|
||||
id: 'server-license',
|
||||
|
||||
@@ -12,6 +12,7 @@ import ToolButton from "@app/components/tools/toolPicker/ToolButton";
|
||||
import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext";
|
||||
import { ToolId } from "@app/types/toolId";
|
||||
import { getSubcategoryLabel } from "@app/data/toolsTaxonomy";
|
||||
import { ToolPickerFooterExtensions } from "@app/components/tools/toolPicker/ToolPickerFooterExtensions";
|
||||
|
||||
interface ToolPickerProps {
|
||||
selectedToolKey: string | null;
|
||||
@@ -150,6 +151,7 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<ToolPickerFooterExtensions />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,11 +3,14 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Tooltip } from '@app/components/shared/Tooltip';
|
||||
import { useBackendHealth } from '@app/hooks/useBackendHealth';
|
||||
import { CloudBadge } from '@app/components/shared/CloudBadge';
|
||||
import type { ExecuteDisabledReason } from '@app/hooks/tools/shared/toolOperationTypes';
|
||||
import { useToolActions } from '@app/contexts/ToolActionsContext';
|
||||
|
||||
export interface OperationButtonProps {
|
||||
onClick?: () => void;
|
||||
isLoading?: boolean;
|
||||
disabled?: boolean;
|
||||
disabledReason?: ExecuteDisabledReason;
|
||||
loadingText?: string;
|
||||
submitText?: string;
|
||||
variant?: 'filled' | 'outline' | 'subtle';
|
||||
@@ -24,6 +27,7 @@ const OperationButton = ({
|
||||
onClick,
|
||||
isLoading = false,
|
||||
disabled = false,
|
||||
disabledReason,
|
||||
loadingText,
|
||||
submitText,
|
||||
variant = 'filled',
|
||||
@@ -37,20 +41,32 @@ const OperationButton = ({
|
||||
}: OperationButtonProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { isOnline, message: backendMessage } = useBackendHealth();
|
||||
const { onEndpointUnavailableClick } = useToolActions();
|
||||
const blockedByBackend = !isOnline;
|
||||
const combinedDisabled = disabled || blockedByBackend;
|
||||
|
||||
const effectiveDisabled = disabled || disabledReason !== null && disabledReason !== undefined;
|
||||
const combinedDisabled = effectiveDisabled || blockedByBackend;
|
||||
|
||||
const reasonTooltip: Record<NonNullable<ExecuteDisabledReason>, string> = {
|
||||
endpointUnavailable: onEndpointUnavailableClick
|
||||
? t('tool.endpointUnavailableClickable', "Not available in this mode. Click to sign in.")
|
||||
: t('tool.endpointUnavailable', 'This tool is unavailable on your server.'),
|
||||
noFiles: t('tool.noFiles', 'Add a file to get started.'),
|
||||
invalidParams: t('tool.invalidParams', 'Fill in the required settings.'),
|
||||
};
|
||||
|
||||
const tooltipLabel = blockedByBackend
|
||||
? (backendMessage ?? t('backendHealth.checking', 'Checking backend status...'))
|
||||
: null;
|
||||
: (disabledReason ? (reasonTooltip[disabledReason] ?? null) : null);
|
||||
|
||||
const button = (
|
||||
<Button
|
||||
type={type}
|
||||
onClick={onClick}
|
||||
fullWidth={fullWidth}
|
||||
mr='md'
|
||||
ml='md'
|
||||
mt={mt}
|
||||
fullWidth={fullWidth || !!tooltipLabel}
|
||||
mr={tooltipLabel ? 0 : 'md'}
|
||||
ml={tooltipLabel ? 0 : 'md'}
|
||||
mt={tooltipLabel ? 0 : mt}
|
||||
loading={isLoading}
|
||||
disabled={combinedDisabled}
|
||||
variant={variant}
|
||||
@@ -72,9 +88,22 @@ const OperationButton = ({
|
||||
);
|
||||
|
||||
if (tooltipLabel) {
|
||||
// Disabled buttons suppress pointer events at the browser level, so the Tooltip's
|
||||
// cloneElement handlers would never fire. Wrap in a Box to capture them instead.
|
||||
// When endpointUnavailable and a click handler is provided (desktop), the Box
|
||||
// also acts as the click target to open the sign-in / connect modal.
|
||||
const boxClickHandler = disabledReason === 'endpointUnavailable' ? onEndpointUnavailableClick : undefined;
|
||||
return (
|
||||
<Tooltip content={tooltipLabel} position="top" arrow>
|
||||
{button}
|
||||
<Box
|
||||
mr="md"
|
||||
ml="md"
|
||||
mt={mt}
|
||||
style={{ display: 'block', cursor: boxClickHandler ? 'pointer' : undefined }}
|
||||
onClick={boxClickHandler}
|
||||
>
|
||||
{button}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ToolOperationHook } from '@app/hooks/tools/shared/useToolOperation';
|
||||
import { ToolWorkflowTitle, ToolWorkflowTitleProps } from '@app/components/tools/shared/ToolWorkflowTitle';
|
||||
import { StirlingFile } from '@app/types/fileContext';
|
||||
import type { TooltipTip } from '@app/types/tips';
|
||||
import type { ExecuteDisabledReason } from '@app/hooks/tools/shared/toolOperationTypes';
|
||||
|
||||
export interface FilesStepConfig {
|
||||
selectedFiles: StirlingFile[];
|
||||
@@ -36,6 +37,23 @@ export interface ExecuteButtonConfig {
|
||||
loadingText: string;
|
||||
onClick: () => Promise<void>;
|
||||
isVisible?: boolean;
|
||||
/**
|
||||
* Pass the raw endpoint-enabled flag from useEndpointEnabled / useBaseTool.
|
||||
* createToolFlow derives the correct disabled reason automatically.
|
||||
* Priority: endpointUnavailable > noFiles > invalidParams
|
||||
*/
|
||||
endpointEnabled?: boolean | null;
|
||||
/**
|
||||
* Pass the result of params.validateParameters().
|
||||
* createToolFlow uses this to show the 'invalidParams' disabled reason.
|
||||
*/
|
||||
paramsValid?: boolean;
|
||||
/**
|
||||
* Explicit disabled reason override — use when the automatic computation
|
||||
* from endpointEnabled + paramsValid is insufficient.
|
||||
*/
|
||||
disabledReason?: ExecuteDisabledReason;
|
||||
/** Raw override for tools with fully custom disable logic (e.g. Compare, ShowJS). */
|
||||
disabled?: boolean;
|
||||
testId?: string;
|
||||
showCloudBadge?: boolean;
|
||||
@@ -99,18 +117,30 @@ export function createToolFlow<TParams = unknown>(config: ToolFlowConfig<TParams
|
||||
{!config.review.isVisible && (config.files.selectedFiles?.length ?? 0) > 0 && config.preview}
|
||||
|
||||
{/* Execute Button */}
|
||||
{config.executeButton && config.executeButton.isVisible !== false && (
|
||||
<OperationButton
|
||||
onClick={config.executeButton.onClick}
|
||||
isLoading={config.review.operation.isLoading}
|
||||
disabled={config.executeButton.disabled}
|
||||
loadingText={config.executeButton.loadingText}
|
||||
submitText={config.executeButton.text}
|
||||
showCloudBadge={config.executeButton.showCloudBadge ?? config.review.operation.willUseCloud ?? false}
|
||||
data-testid={config.executeButton.testId}
|
||||
data-tour="run-button"
|
||||
/>
|
||||
)}
|
||||
{config.executeButton && config.executeButton.isVisible !== false && (() => {
|
||||
const eb = config.executeButton;
|
||||
const hasFiles = (config.files.selectedFiles?.length ?? 0) > 0;
|
||||
// Compute the disabled reason from structured fields; explicit disabledReason wins if set.
|
||||
const effectiveDisabledReason: ExecuteDisabledReason =
|
||||
eb.disabledReason !== undefined ? eb.disabledReason
|
||||
: eb.endpointEnabled === false ? 'endpointUnavailable'
|
||||
: !hasFiles ? 'noFiles'
|
||||
: eb.paramsValid === false ? 'invalidParams'
|
||||
: null;
|
||||
return (
|
||||
<OperationButton
|
||||
onClick={eb.onClick}
|
||||
isLoading={config.review.operation.isLoading}
|
||||
disabled={eb.disabled}
|
||||
disabledReason={effectiveDisabledReason}
|
||||
loadingText={eb.loadingText}
|
||||
submitText={eb.text}
|
||||
showCloudBadge={eb.showCloudBadge ?? config.review.operation.willUseCloud ?? false}
|
||||
data-testid={eb.testId}
|
||||
data-tour="run-button"
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Review Step */}
|
||||
{steps.createReviewStep({
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useHotkeys } from "@app/contexts/HotkeyContext";
|
||||
import HotkeyDisplay from "@app/components/hotkeys/HotkeyDisplay";
|
||||
import FavoriteStar from "@app/components/tools/toolPicker/FavoriteStar";
|
||||
import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext";
|
||||
import { ToolId } from "@app/types/toolId";
|
||||
import type { ToolId } from "@app/types/toolId";
|
||||
import { getToolDisabledReason, getDisabledLabel } from "@app/components/tools/fullscreen/shared";
|
||||
import { useAppConfig } from "@app/contexts/AppConfigContext";
|
||||
import { CloudBadge } from "@app/components/shared/CloudBadge";
|
||||
@@ -26,15 +26,21 @@ interface ToolButtonProps {
|
||||
disableNavigation?: boolean;
|
||||
matchedSynonym?: string;
|
||||
hasStars?: boolean;
|
||||
/** Called when an unavailable tool is clicked; if provided, overrides the default no-op */
|
||||
onUnavailableClick?: () => void;
|
||||
}
|
||||
|
||||
const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect, disableNavigation = false, matchedSynonym, hasStars = false }) => {
|
||||
const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect, disableNavigation = false, matchedSynonym, hasStars = false, onUnavailableClick }) => {
|
||||
const { t } = useTranslation();
|
||||
const { config } = useAppConfig();
|
||||
const premiumEnabled = config?.premiumEnabled;
|
||||
const { isFavorite, toggleFavorite, toolAvailability } = useToolWorkflow();
|
||||
const disabledReason = getToolDisabledReason(id, tool, toolAvailability, premiumEnabled);
|
||||
const isUnavailable = disabledReason !== null;
|
||||
// If onUnavailableClick is provided for a non-comingSoon tool, render as "cloud-available":
|
||||
// full opacity, cloud badge, normal tooltip — clicking still fires onUnavailableClick (e.g. sign-in).
|
||||
const showAsCloudAvailable = isUnavailable && !!onUnavailableClick && disabledReason !== 'comingSoon' && disabledReason !== 'selfHostedOffline';
|
||||
const visuallyUnavailable = isUnavailable && !showAsCloudAvailable;
|
||||
const { hotkeys } = useHotkeys();
|
||||
const binding = hotkeys[id];
|
||||
const { getToolNavigation } = useToolNavigation();
|
||||
@@ -46,7 +52,10 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
const usesCloud = useWillUseCloud(endpointString);
|
||||
|
||||
const handleClick = (id: ToolId) => {
|
||||
if (isUnavailable) return;
|
||||
if (isUnavailable) {
|
||||
onUnavailableClick?.();
|
||||
return;
|
||||
}
|
||||
if (tool.link) {
|
||||
// Open external link in new tab
|
||||
window.open(tool.link, '_blank', 'noopener,noreferrer');
|
||||
@@ -62,7 +71,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
const { key: disabledKey, fallback: disabledFallback } = getDisabledLabel(disabledReason);
|
||||
const disabledMessage = t(disabledKey, disabledFallback);
|
||||
|
||||
const tooltipContent = isUnavailable
|
||||
const tooltipContent = visuallyUnavailable
|
||||
? (<span><strong>{disabledMessage}</strong> {tool.description}</span>)
|
||||
: (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.35rem' }}>
|
||||
@@ -84,7 +93,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
<>
|
||||
<ToolIcon
|
||||
icon={tool.icon}
|
||||
opacity={isUnavailable ? 0.25 : 1}
|
||||
opacity={visuallyUnavailable ? 0.25 : 1}
|
||||
/>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', flex: 1, overflow: 'visible' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', width: '100%' }}>
|
||||
@@ -93,19 +102,19 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
lines={1}
|
||||
minimumFontScale={0.8}
|
||||
as="span"
|
||||
style={{ display: 'inline-block', maxWidth: '100%', opacity: isUnavailable ? 0.25 : 1 }}
|
||||
style={{ display: 'inline-block', maxWidth: '100%', opacity: visuallyUnavailable ? 0.25 : 1 }}
|
||||
/>
|
||||
{tool.versionStatus === 'alpha' && (
|
||||
<Badge
|
||||
size="xs"
|
||||
variant="light"
|
||||
color="orange"
|
||||
style={{ flexShrink: 0, opacity: isUnavailable ? 0.25 : 1 }}
|
||||
style={{ flexShrink: 0, opacity: visuallyUnavailable ? 0.25 : 1 }}
|
||||
>
|
||||
{t('toolPanel.alpha', 'Alpha')}
|
||||
</Badge>
|
||||
)}
|
||||
{usesCloud && !isUnavailable && (
|
||||
{(usesCloud && !visuallyUnavailable) && (
|
||||
<CloudBadge />
|
||||
)}
|
||||
</div>
|
||||
@@ -113,7 +122,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
<span style={{
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--mantine-color-dimmed)',
|
||||
opacity: isUnavailable ? 0.25 : 1,
|
||||
opacity: visuallyUnavailable ? 0.25 : 1,
|
||||
marginTop: '1px',
|
||||
overflow: 'visible',
|
||||
whiteSpace: 'nowrap'
|
||||
@@ -195,7 +204,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
root: {
|
||||
borderRadius: 0,
|
||||
color: "var(--tools-text-and-icon-color)",
|
||||
cursor: isUnavailable ? 'not-allowed' : undefined,
|
||||
cursor: visuallyUnavailable ? 'not-allowed' : undefined,
|
||||
overflow: 'visible'
|
||||
},
|
||||
label: { overflow: 'visible' }
|
||||
@@ -205,7 +214,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
</Button>
|
||||
);
|
||||
|
||||
const star = hasStars && !isUnavailable ? (
|
||||
const star = hasStars && !visuallyUnavailable ? (
|
||||
<FavoriteStar
|
||||
isFavorite={fav}
|
||||
onToggle={() => toggleFavorite(id as ToolId)}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Stub — returns null in core/web builds.
|
||||
* Desktop build shadows this with a sign-in prompt for local mode.
|
||||
*/
|
||||
export function ToolPickerFooterExtensions() {
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
interface ToolActionsContextValue {
|
||||
/**
|
||||
* Called when the user clicks the disabled execute button while the reason
|
||||
* is 'endpointUnavailable'. Desktop provides a sign-in modal dispatch;
|
||||
* web builds leave this undefined (button stays disabled with tooltip only).
|
||||
*/
|
||||
onEndpointUnavailableClick?: () => void;
|
||||
}
|
||||
|
||||
export const ToolActionsContext = createContext<ToolActionsContextValue>({});
|
||||
|
||||
export function useToolActions(): ToolActionsContextValue {
|
||||
return useContext(ToolActionsContext);
|
||||
}
|
||||
@@ -66,6 +66,10 @@ interface ToolWorkflowContextValue extends ToolWorkflowState {
|
||||
|
||||
// Workflow Actions (compound actions)
|
||||
handleToolSelect: (toolId: ToolId) => void;
|
||||
/** Like handleToolSelect but bypasses the availability guard — use when you want to
|
||||
* navigate to a tool's UI even if it's marked unavailable (e.g. to show a disabled
|
||||
* execute button with a sign-in prompt rather than blocking navigation entirely). */
|
||||
handleToolSelectForced: (toolId: ToolId) => void;
|
||||
handleBackToTools: () => void;
|
||||
handleReaderToggle: () => void;
|
||||
|
||||
@@ -321,6 +325,23 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
||||
setReaderMode(false); // Disable read mode when selecting tools
|
||||
}, [actions, getSelectedTool, navigationState.workbench, navigationState.hasUnsavedChanges, navigationState.selectedTool, setLeftPanelView, setReaderMode, setSearchQuery, toolAvailability]);
|
||||
|
||||
const handleToolSelectForced = useCallback((toolId: ToolId) => {
|
||||
const validToolId = isValidToolId(toolId) ? toolId : null;
|
||||
actions.setSelectedTool(validToolId);
|
||||
const tool = getSelectedTool(toolId);
|
||||
const wasInCustomWorkbench = !isBaseWorkbench(navigationState.workbench);
|
||||
if (wasInCustomWorkbench) {
|
||||
actions.setWorkbench(getDefaultWorkbench());
|
||||
} else if (tool && tool.workbench) {
|
||||
actions.setWorkbench(tool.workbench);
|
||||
} else {
|
||||
actions.setWorkbench(getDefaultWorkbench());
|
||||
}
|
||||
setSearchQuery('');
|
||||
setLeftPanelView('toolContent');
|
||||
setReaderMode(false);
|
||||
}, [actions, getSelectedTool, navigationState.workbench, setLeftPanelView, setReaderMode, setSearchQuery]);
|
||||
|
||||
const handleBackToTools = useCallback(() => {
|
||||
setLeftPanelView('toolPicker');
|
||||
setReaderMode(false);
|
||||
@@ -378,6 +399,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
||||
|
||||
// Workflow Actions
|
||||
handleToolSelect,
|
||||
handleToolSelectForced,
|
||||
handleBackToTools,
|
||||
handleReaderToggle,
|
||||
|
||||
|
||||
@@ -11,6 +11,16 @@ export enum ToolType {
|
||||
custom,
|
||||
}
|
||||
|
||||
/**
|
||||
* Reason the execute button is disabled. Resolved to a translated tooltip by OperationButton.
|
||||
* null means the button is enabled.
|
||||
*/
|
||||
export type ExecuteDisabledReason =
|
||||
| 'endpointUnavailable'
|
||||
| 'noFiles'
|
||||
| 'invalidParams'
|
||||
| null;
|
||||
|
||||
/**
|
||||
* Result from custom processor with optional metadata about input consumption.
|
||||
*/
|
||||
|
||||
@@ -152,7 +152,6 @@ export function useBaseTool<TParams, TParamsHook extends BaseParametersHook<TPar
|
||||
// Endpoint validation
|
||||
endpointEnabled,
|
||||
endpointLoading,
|
||||
|
||||
// Handlers
|
||||
handleExecute,
|
||||
handleThumbnailClick,
|
||||
|
||||
@@ -36,7 +36,7 @@ export const accountService = {
|
||||
* Get current user account data
|
||||
*/
|
||||
async getAccountData(): Promise<AccountData> {
|
||||
const response = await apiClient.get<AccountData>('/api/v1/proprietary/ui-data/account');
|
||||
const response = await apiClient.get<AccountData>('/api/v1/proprietary/ui-data/account', { suppressErrorToast: true });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ class SignatureStorageService {
|
||||
// Probe the proprietary signatures endpoint (requires authentication)
|
||||
await apiClient.get('/api/v1/proprietary/signatures', {
|
||||
timeout: 3000,
|
||||
suppressErrorToast: true,
|
||||
});
|
||||
|
||||
// 200 = Backend available and accessible (authenticated)
|
||||
|
||||
@@ -22,6 +22,9 @@ export const Z_INDEX_DRAG_BADGE = 1001;
|
||||
// Modal that appears on top of config modal (e.g., restart confirmation, update modal)
|
||||
export const Z_INDEX_OVER_CONFIG_MODAL = 2000;
|
||||
|
||||
// Sign-in modal — must appear above all app UI including config and analytics modals
|
||||
export const Z_INDEX_SIGN_IN_MODAL = 9000;
|
||||
|
||||
// Toast notifications and error displays - Always on top (higher than rainbow theme at 10000)
|
||||
export const Z_INDEX_TOAST = 10001;
|
||||
|
||||
|
||||
@@ -90,7 +90,8 @@ const AddAttachments = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =
|
||||
isVisible: !hasResults,
|
||||
loadingText: t('loading'),
|
||||
onClick: handleExecute,
|
||||
disabled: !params.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
endpointEnabled: endpointEnabled,
|
||||
paramsValid: params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: hasResults,
|
||||
|
||||
@@ -106,7 +106,8 @@ const AddPageNumbers = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =
|
||||
isVisible: !hasResults,
|
||||
loadingText: t('loading'),
|
||||
onClick: handleExecute,
|
||||
disabled: !params.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
endpointEnabled: endpointEnabled,
|
||||
paramsValid: params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: hasResults,
|
||||
|
||||
@@ -106,7 +106,8 @@ const AddPassword = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
isVisible: !hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: handleAddPassword,
|
||||
disabled: !addPasswordParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
endpointEnabled: endpointEnabled,
|
||||
paramsValid: addPasswordParams.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: hasResults,
|
||||
|
||||
@@ -167,7 +167,8 @@ const AddStamp = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
isVisible: !hasResults,
|
||||
loadingText: t('loading'),
|
||||
onClick: handleExecute,
|
||||
disabled: !params.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
endpointEnabled: endpointEnabled,
|
||||
paramsValid: params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: hasResults,
|
||||
|
||||
@@ -199,7 +199,8 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =>
|
||||
isVisible: !hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: handleAddWatermark,
|
||||
disabled: !watermarkParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
endpointEnabled: endpointEnabled,
|
||||
paramsValid: watermarkParams.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: hasResults,
|
||||
|
||||
@@ -103,7 +103,7 @@ const AdjustContrast = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t('loading'),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.hasFiles,
|
||||
paramsValid: true,
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -43,7 +43,8 @@ const AdjustPageScale = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -36,7 +36,8 @@ return createToolFlow({
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -44,7 +44,8 @@ const BookletImposition = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -113,7 +113,8 @@ const CertSign = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -141,7 +141,8 @@ const ChangeMetadata = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -43,7 +43,8 @@ const ChangePermissions = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -43,7 +43,8 @@ const Compress = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -152,7 +152,8 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
loadingText: t("convert.converting", "Converting..."),
|
||||
onClick: handleConvert,
|
||||
isVisible: !hasResults,
|
||||
disabled: !convertParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
endpointEnabled: endpointEnabled,
|
||||
paramsValid: convertParams.validateParameters(),
|
||||
testId: "convert-button",
|
||||
},
|
||||
review: {
|
||||
|
||||
@@ -44,7 +44,8 @@ const Crop = (props: BaseToolProps) => {
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
isVisible: !base.hasResults,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -40,7 +40,8 @@ const ExtractImages = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -45,7 +45,8 @@ const ExtractPages = (props: BaseToolProps) => {
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
isVisible: !base.hasResults,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -43,7 +43,8 @@ const Flatten = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -163,11 +163,8 @@ const GetPdfInfo = (props: BaseToolProps) => {
|
||||
text: t('getPdfInfo.submit', 'Generate'),
|
||||
loadingText: t('loading', 'Loading...'),
|
||||
onClick: base.handleExecute,
|
||||
disabled:
|
||||
!base.params.validateParameters() ||
|
||||
!base.hasFiles ||
|
||||
base.operation.isLoading ||
|
||||
!base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
isVisible: true,
|
||||
},
|
||||
review: {
|
||||
|
||||
@@ -134,7 +134,8 @@ const Merge = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -128,8 +128,9 @@ const OCR = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
text: t("ocr.operation.submit", "Process OCR and Review"),
|
||||
loadingText: t("loading"),
|
||||
onClick: handleOCR,
|
||||
isVisible: hasValidSettings && !hasResults,
|
||||
disabled: !ocrParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
isVisible: !hasResults,
|
||||
endpointEnabled: endpointEnabled,
|
||||
paramsValid: hasValidSettings,
|
||||
},
|
||||
review: {
|
||||
isVisible: hasResults,
|
||||
|
||||
@@ -43,7 +43,8 @@ const OverlayPdfs = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t('loading'),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -40,7 +40,8 @@ const PageLayout = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t('loading'),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -37,7 +37,7 @@ const RemoveAnnotations = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading", "Processing..."),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -51,7 +51,8 @@ const RemoveBlanks = (props: BaseToolProps) => {
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
isVisible: !base.hasResults,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -26,7 +26,8 @@ const RemoveCertificateSign = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -26,7 +26,8 @@ const RemoveImage = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -46,7 +46,8 @@ const RemovePages = (props: BaseToolProps) => {
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
isVisible: !base.hasResults,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -43,7 +43,8 @@ const RemovePassword = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -82,7 +82,8 @@ const ReorganizePages = ({ onPreviewFile, onComplete, onError }: BaseToolProps)
|
||||
isVisible: !hasResults,
|
||||
loadingText: t('loading'),
|
||||
onClick: handleExecute,
|
||||
disabled: !params.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
endpointEnabled: endpointEnabled,
|
||||
paramsValid: params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: hasResults,
|
||||
|
||||
@@ -26,7 +26,8 @@ const Repair = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -43,7 +43,8 @@ const ReplaceColor = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -42,7 +42,8 @@ const Rotate = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -40,7 +40,8 @@ const Sanitize = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -43,7 +43,8 @@ const ScannerImageSplit = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -26,7 +26,8 @@ const SingleLargePage = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -89,7 +89,8 @@ const Split = (props: BaseToolProps) => {
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
isVisible: !base.hasResults,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -26,7 +26,8 @@ const UnlockPdfForms = (props: BaseToolProps) => {
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
|
||||
@@ -142,11 +142,8 @@ const ValidateSignature = (props: BaseToolProps) => {
|
||||
text: t('validateSignature.submit', 'Validate Signatures'),
|
||||
loadingText: t('loading', 'Loading...'),
|
||||
onClick: base.handleExecute,
|
||||
disabled:
|
||||
!base.params.validateParameters() ||
|
||||
!base.hasFiles ||
|
||||
base.operation.isLoading ||
|
||||
!base.endpointEnabled,
|
||||
endpointEnabled: base.endpointEnabled,
|
||||
paramsValid: base.params.validateParameters(),
|
||||
isVisible: true,
|
||||
},
|
||||
review: {
|
||||
|
||||
Reference in New Issue
Block a user