mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
Tidying up
This commit is contained in:
@@ -7849,7 +7849,6 @@ title = "All pipelines"
|
||||
[portal.pipelines.builder]
|
||||
activate = "Activate"
|
||||
back = "Back to pipelines"
|
||||
backToSimple = "Back to the simple view"
|
||||
cannotFollow = "Can't take {{produced}}"
|
||||
chooseAccount = "Choose an account"
|
||||
chooseDestination = "Choose a destination"
|
||||
@@ -7914,10 +7913,6 @@ helper = "Temporary raw view of the policy's metadata (run/output settings, sour
|
||||
invalid = "Not valid JSON. The last valid version is kept until this is fixed."
|
||||
label = "Policy metadata (advanced)"
|
||||
|
||||
[portal.pipelines.builder.required]
|
||||
desc = "Members can't skip, pause, or delete it, and it runs on their documents automatically."
|
||||
label = "Enforce as policy"
|
||||
|
||||
[portal.pipelines.composer]
|
||||
addTool = "Add a tool"
|
||||
create = "Create pipeline"
|
||||
@@ -7967,6 +7962,11 @@ connectSource = "Connect a source"
|
||||
description = "Create your first pipeline: pick the sources it runs over, chain the operations, and choose where output goes."
|
||||
title = "No pipelines yet"
|
||||
|
||||
[portal.pipelines.enforce]
|
||||
desc = "Runs automatically; members can't turn it off"
|
||||
info = "What enforcing as a policy means"
|
||||
label = "Enforce as policy"
|
||||
|
||||
[portal.pipelines.graph]
|
||||
addFirstTool = "Add a tool"
|
||||
dragHint = "Drop on a line to move it"
|
||||
@@ -8477,10 +8477,6 @@ label = "Apply a watermark"
|
||||
description = "Every uploaded document is classified against the built-in labels and tagged with the types that fit. The label set is shared across your whole team."
|
||||
labelsHeading = "Classification labels"
|
||||
|
||||
[portal.policies.wizard.enforcement]
|
||||
requiredDesc = "Run this on every document automatically. Members can't skip, pause, or delete it."
|
||||
requiredLabel = "Enforce as policy"
|
||||
|
||||
[portal.policies.wizard.errors]
|
||||
noTools = "Enable at least one tool in the workflow first."
|
||||
saveFailed = "Couldn't save the pipeline. Please try again."
|
||||
|
||||
@@ -19,29 +19,7 @@
|
||||
color: var(--color-section-label);
|
||||
}
|
||||
|
||||
/* An (i) affordance beside the label: the explanation lives in its tooltip
|
||||
rather than as permanent subtext under the control. */
|
||||
.sui-field__info {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--c-text-subtle);
|
||||
cursor: pointer;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.sui-field__info:hover {
|
||||
color: var(--c-text);
|
||||
}
|
||||
|
||||
.sui-field__info:focus-visible {
|
||||
outline: 2px solid var(--c-primary);
|
||||
outline-offset: 2px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
/* The label's (i) affordance is the shared InfoTooltip primitive (@app/ui/InfoTooltip). */
|
||||
|
||||
.sui-field__required {
|
||||
/* The base red is a fill colour; as text on the form background it only
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type ReactElement,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { Tooltip } from "@mantine/core";
|
||||
import { InfoTooltip } from "@app/ui/InfoTooltip";
|
||||
import "@app/ui/FormField.css";
|
||||
|
||||
export interface FormFieldProps {
|
||||
@@ -77,40 +77,7 @@ export function FormField({
|
||||
)}
|
||||
</label>
|
||||
)}
|
||||
{info && (
|
||||
<Tooltip
|
||||
label={info}
|
||||
multiline
|
||||
w={260}
|
||||
withArrow
|
||||
position="top"
|
||||
events={{ hover: true, focus: true, touch: true }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="sui-field__info"
|
||||
aria-label={
|
||||
typeof info === "string" ? info : "More information"
|
||||
}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
height="14"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="12" y1="16" x2="12" y2="12" />
|
||||
<line x1="12" y1="8" x2="12.01" y2="8" />
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{info && <InfoTooltip label={info} />}
|
||||
</div>
|
||||
)}
|
||||
<div className="sui-field__control">{child}</div>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
.sui-info {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--c-text-subtle);
|
||||
cursor: pointer;
|
||||
line-height: 0;
|
||||
}
|
||||
.sui-info:hover {
|
||||
color: var(--c-text);
|
||||
}
|
||||
.sui-info:focus-visible {
|
||||
outline: 2px solid var(--c-primary);
|
||||
outline-offset: 2px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { InfoTooltip } from "@app/ui/InfoTooltip";
|
||||
|
||||
const meta: Meta<typeof InfoTooltip> = {
|
||||
title: "Primitives/InfoTooltip",
|
||||
component: InfoTooltip,
|
||||
tags: ["autodocs"],
|
||||
parameters: { layout: "centered" },
|
||||
args: {
|
||||
label: "The folder (key prefix) within the bucket to watch.",
|
||||
position: "top",
|
||||
},
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof InfoTooltip>;
|
||||
|
||||
/** Hover or focus the (i) to reveal the explanation. */
|
||||
export const Default: Story = {};
|
||||
|
||||
/** Inline beside a label, the way FormField renders it. */
|
||||
export const BesideLabel: Story = {
|
||||
render: (args) => (
|
||||
<span
|
||||
style={{ display: "inline-flex", alignItems: "center", gap: "0.25rem" }}
|
||||
>
|
||||
<span style={{ fontSize: "0.8125rem", fontWeight: 600 }}>Folder</span>
|
||||
<InfoTooltip {...args} />
|
||||
</span>
|
||||
),
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { Tooltip, type FloatingPosition } from "@mantine/core";
|
||||
import "@app/ui/InfoTooltip.css";
|
||||
|
||||
export interface InfoTooltipProps {
|
||||
/** The explanation shown in the tooltip on hover/focus. */
|
||||
label: ReactNode;
|
||||
/** Accessible name for the button. Defaults to the label when it's a string. */
|
||||
ariaLabel?: string;
|
||||
/** Which side the tooltip opens on. Default "top". */
|
||||
position?: FloatingPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* The app's standard inline info affordance: a small, muted (i) that reveals supplementary text in a
|
||||
* hover/focus tooltip, without taking permanent space. Used behind form labels ({@link FormField})
|
||||
* and anywhere a control needs a hint - one implementation so every (i) reads and behaves the same.
|
||||
*/
|
||||
export function InfoTooltip({
|
||||
label,
|
||||
ariaLabel,
|
||||
position = "top",
|
||||
}: InfoTooltipProps) {
|
||||
return (
|
||||
<Tooltip
|
||||
label={label}
|
||||
multiline
|
||||
w={260}
|
||||
withArrow
|
||||
position={position}
|
||||
events={{ hover: true, focus: true, touch: true }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="sui-info"
|
||||
aria-label={
|
||||
ariaLabel ?? (typeof label === "string" ? label : "More information")
|
||||
}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
height="14"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="12" y1="16" x2="12" y2="12" />
|
||||
<line x1="12" y1="8" x2="12.01" y2="8" />
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export * from "@app/ui/NodeCard";
|
||||
export * from "@app/ui/OptionCard";
|
||||
export * from "@app/ui/CardRail";
|
||||
export * from "@app/ui/IconPicker";
|
||||
export * from "@app/ui/InfoTooltip";
|
||||
export * from "@app/ui/NavItem";
|
||||
export * from "@app/ui/NavSurface";
|
||||
export * from "@app/ui/Surface";
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.portal-enforce {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { InfoTooltip, ToggleSwitch } from "@app/ui";
|
||||
import "@portal/components/pipelines/EnforceAsPolicyControl.css";
|
||||
|
||||
export interface EnforceAsPolicyControlProps {
|
||||
/** Org-mandated policy (see Policy.required). */
|
||||
required: boolean;
|
||||
onRequiredChange: (required: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "Enforce as policy" switch plus the app's standard inline (i) info affordance explaining what
|
||||
* it means. Shared by the builder header and the simple wizard so the control reads and behaves
|
||||
* identically wherever a pipeline can be made org-mandated.
|
||||
*/
|
||||
export function EnforceAsPolicyControl({
|
||||
required,
|
||||
onRequiredChange,
|
||||
}: EnforceAsPolicyControlProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<span className="portal-enforce">
|
||||
<ToggleSwitch
|
||||
size="sm"
|
||||
checked={required}
|
||||
onChange={onRequiredChange}
|
||||
label={t("portal.pipelines.enforce.label")}
|
||||
/>
|
||||
<InfoTooltip
|
||||
label={t("portal.pipelines.enforce.desc")}
|
||||
ariaLabel={t("portal.pipelines.enforce.info")}
|
||||
position="bottom"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
|
||||
import { ActionIcon, Button, IconPicker, Input } from "@app/ui";
|
||||
import { PipelineBlockerTooltip } from "@portal/components/pipelines/PipelineBlockerTooltip";
|
||||
import { PIPELINE_ICON_OPTIONS } from "@portal/components/pipelines/pipelineIcon";
|
||||
import { PipelinePolicyControls } from "@portal/components/pipelines/PipelinePolicyControls";
|
||||
import { EnforceAsPolicyControl } from "@portal/components/pipelines/EnforceAsPolicyControl";
|
||||
import "@portal/components/pipelines/PipelineCreateHeader.css";
|
||||
|
||||
export interface PipelineCreateHeaderProps {
|
||||
@@ -12,10 +12,9 @@ export interface PipelineCreateHeaderProps {
|
||||
/** Row icon key (see pipelineIcon); chosen from the picker beside the name. */
|
||||
icon: string;
|
||||
onIconChange: (key: string) => void;
|
||||
/** Org-mandated policy toggle + return-to-simple, shown in the actions row. */
|
||||
/** "Enforce as policy" toggle, shown in the actions row. */
|
||||
required: boolean;
|
||||
onRequiredChange: (required: boolean) => void;
|
||||
onBackToSimple?: () => void;
|
||||
|
||||
canSave: boolean;
|
||||
/** Everything still owed before the pipeline can be created, shown on the disabled create button. */
|
||||
@@ -41,7 +40,6 @@ export function PipelineCreateHeader({
|
||||
onIconChange,
|
||||
required,
|
||||
onRequiredChange,
|
||||
onBackToSimple,
|
||||
canSave,
|
||||
blockers,
|
||||
saving,
|
||||
@@ -79,10 +77,9 @@ export function PipelineCreateHeader({
|
||||
/>
|
||||
|
||||
<div className="portal-pipeline-create-header__actions">
|
||||
<PipelinePolicyControls
|
||||
<EnforceAsPolicyControl
|
||||
required={required}
|
||||
onRequiredChange={onRequiredChange}
|
||||
onBackToSimple={onBackToSimple}
|
||||
/>
|
||||
|
||||
{/* The pair share one tooltip target because a disabled button swallows its own hover - the
|
||||
|
||||
@@ -11,7 +11,7 @@ import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
|
||||
import { ActionIcon, Button, Dropdown, IconPicker, Input } from "@app/ui";
|
||||
import { PipelineBlockerTooltip } from "@portal/components/pipelines/PipelineBlockerTooltip";
|
||||
import { PIPELINE_ICON_OPTIONS } from "@portal/components/pipelines/pipelineIcon";
|
||||
import { PipelinePolicyControls } from "@portal/components/pipelines/PipelinePolicyControls";
|
||||
import { EnforceAsPolicyControl } from "@portal/components/pipelines/EnforceAsPolicyControl";
|
||||
import "@portal/components/pipelines/PipelineEditHeader.css";
|
||||
|
||||
export interface PipelineEditHeaderProps {
|
||||
@@ -20,10 +20,9 @@ export interface PipelineEditHeaderProps {
|
||||
/** Row icon key (see pipelineIcon); chosen from the picker beside the name. */
|
||||
icon: string;
|
||||
onIconChange: (key: string) => void;
|
||||
/** Org-mandated policy toggle + return-to-simple, shown in the actions row. */
|
||||
/** "Enforce as policy" toggle, shown in the actions row. */
|
||||
required: boolean;
|
||||
onRequiredChange: (required: boolean) => void;
|
||||
onBackToSimple?: () => void;
|
||||
|
||||
/** The pipeline's live state. Toggling it takes effect immediately, not on save. */
|
||||
enabled: boolean;
|
||||
@@ -62,7 +61,6 @@ export function PipelineEditHeader({
|
||||
onIconChange,
|
||||
required,
|
||||
onRequiredChange,
|
||||
onBackToSimple,
|
||||
enabled,
|
||||
onTogglePause,
|
||||
togglingEnabled,
|
||||
@@ -166,10 +164,9 @@ export function PipelineEditHeader({
|
||||
</div>
|
||||
|
||||
<div className="portal-pipeline-edit-header__actions">
|
||||
<PipelinePolicyControls
|
||||
<EnforceAsPolicyControl
|
||||
required={required}
|
||||
onRequiredChange={onRequiredChange}
|
||||
onBackToSimple={onBackToSimple}
|
||||
/>
|
||||
|
||||
{/* Pause and Save both write the whole policy, so they are mutually exclusive: neither can
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "@mantine/core";
|
||||
import UndoRoundedIcon from "@mui/icons-material/UndoRounded";
|
||||
import { Button, ToggleSwitch } from "@app/ui";
|
||||
|
||||
export interface PipelinePolicyControlsProps {
|
||||
/** Org-mandated policy (see Policy.required). */
|
||||
required: boolean;
|
||||
onRequiredChange: (required: boolean) => void;
|
||||
/**
|
||||
* Return to the simple wizard. Present only while the pipeline still fits its template (so nothing
|
||||
* would be lost); omitted otherwise.
|
||||
*/
|
||||
onBackToSimple?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pipeline-level policy controls that sit in the builder header alongside the primary actions:
|
||||
* the "Enforce as policy" switch (its rule lives in a tooltip, not inline), and - when the chain is
|
||||
* still template-shaped - a way back to the simple view.
|
||||
*/
|
||||
export function PipelinePolicyControls({
|
||||
required,
|
||||
onRequiredChange,
|
||||
onBackToSimple,
|
||||
}: PipelinePolicyControlsProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
{onBackToSimple && (
|
||||
<Button
|
||||
variant="tertiary"
|
||||
size="sm"
|
||||
onClick={onBackToSimple}
|
||||
leftSection={<UndoRoundedIcon style={{ fontSize: "1.05rem" }} />}
|
||||
>
|
||||
{t("portal.pipelines.builder.backToSimple")}
|
||||
</Button>
|
||||
)}
|
||||
<Tooltip
|
||||
label={t("portal.pipelines.builder.required.desc")}
|
||||
position="bottom-end"
|
||||
withinPortal
|
||||
multiline
|
||||
w={260}
|
||||
>
|
||||
{/* Span wrapper so the tooltip has a ref-able hover target around the switch. */}
|
||||
<span>
|
||||
<ToggleSwitch
|
||||
size="sm"
|
||||
checked={required}
|
||||
onChange={onRequiredChange}
|
||||
label={t("portal.pipelines.builder.required.label")}
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
Banner,
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
FormField,
|
||||
Input,
|
||||
Modal,
|
||||
@@ -19,6 +18,7 @@ import {
|
||||
ToggleSwitch,
|
||||
} from "@app/ui";
|
||||
import { SettingsRow } from "@app/ui/SettingsRow";
|
||||
import { EnforceAsPolicyControl } from "@portal/components/pipelines/EnforceAsPolicyControl";
|
||||
import {
|
||||
humanizeEndpoint,
|
||||
type CatalogueEntry,
|
||||
@@ -788,11 +788,9 @@ function PolicySetupWizardBody({
|
||||
</div>
|
||||
|
||||
<div className="portal-policies__wizard-enforce">
|
||||
<Checkbox
|
||||
checked={required}
|
||||
onChange={(e) => setRequired(e.target.checked)}
|
||||
label={t("portal.policies.wizard.enforcement.requiredLabel")}
|
||||
description={t("portal.policies.wizard.enforcement.requiredDesc")}
|
||||
<EnforceAsPolicyControl
|
||||
required={required}
|
||||
onRequiredChange={setRequired}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,7 @@ import {
|
||||
uploadPipelineAsset,
|
||||
type PolicyAsset,
|
||||
} from "@portal/api/pipelineAssets";
|
||||
import { clearProcessedHistory, parseSimplePolicy } from "@portal/api/policies";
|
||||
import { clearProcessedHistory } from "@portal/api/policies";
|
||||
import { DestinationPicker } from "@portal/components/pipelines/DestinationPicker";
|
||||
import { PolicyMetadataDevEditor } from "@portal/components/pipelines/PolicyMetadataDevEditor";
|
||||
import { availableOutputModes } from "@portal/components/pipelines/outputModes";
|
||||
@@ -709,10 +709,6 @@ export function PipelineBuilder() {
|
||||
: [];
|
||||
const editorEnforced = enforcedSources.includes("editor");
|
||||
const enforcedRunOn = outputOptions.runOn === "export" ? "export" : "upload";
|
||||
const originCategoryId =
|
||||
typeof outputOptions.categoryId === "string"
|
||||
? outputOptions.categoryId
|
||||
: "";
|
||||
|
||||
// Each validity condition is defined exactly once here, then consumed both by the graph (which
|
||||
// flags each end) and by the blocker list below.
|
||||
@@ -722,29 +718,6 @@ export function PipelineBuilder() {
|
||||
const inputValid = sourceChosen && scheduleValid;
|
||||
const outputValid = outputIds.length === 1;
|
||||
|
||||
// The working pipeline as a wire record, for the representability check. Steps serialize
|
||||
// synchronously here (no asset upload) - parseSimplePolicy only reads their operation + params.
|
||||
const currentSimplePolicy: Policy = {
|
||||
id: policyState.data?.id ?? seedDraft?.id,
|
||||
name: name.trim(),
|
||||
enabled,
|
||||
required,
|
||||
icon,
|
||||
inputs:
|
||||
editorEnforced || !sourceChosen
|
||||
? []
|
||||
: [{ sourceId: input.sourceId, trigger: buildTriggerFor(input) }],
|
||||
steps: steps.map((step) => serializeToolStep(step, allTools)),
|
||||
output: { type: outputType, options: outputOptions },
|
||||
outputIds: editorEnforced ? [] : outputIds,
|
||||
};
|
||||
// "Back to simple" is offered only while the pipeline can still be shown by the wizard - governed
|
||||
// by representability, not by whether the builder was opened. It disappears the moment an edit
|
||||
// takes the chain out of its template's shape, and reappears if that edit is undone.
|
||||
const simpleEntry = originCategoryId
|
||||
? parseSimplePolicy(currentSimplePolicy)
|
||||
: null;
|
||||
|
||||
// The single source of truth for "can this be committed": every reason it can't be, in the order
|
||||
// they appear down the form, so a disabled Create / Save button can say exactly what is still owed.
|
||||
const blockers: string[] = [];
|
||||
@@ -782,16 +755,6 @@ export function PipelineBuilder() {
|
||||
navigate(listPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hand the pipeline back to the simple wizard, carrying the current (unsaved) state. Only reachable
|
||||
* while {@link simpleEntry} is non-null, i.e. the chain still fits its template - so nothing is
|
||||
* lost. The list route reopens the wizard from this record.
|
||||
*/
|
||||
function backToSimple() {
|
||||
if (!simpleEntry) return;
|
||||
navigate(listPath, { state: { reopenSimple: currentSimplePolicy } });
|
||||
}
|
||||
|
||||
// Leave the builder, but prompt first if there are unsaved edits (see the unsaved-changes modal).
|
||||
function attemptLeave(destination: string) {
|
||||
if (dirty) setPendingNav(destination);
|
||||
@@ -1383,7 +1346,6 @@ export function PipelineBuilder() {
|
||||
onIconChange={setIcon}
|
||||
required={required}
|
||||
onRequiredChange={setRequired}
|
||||
onBackToSimple={simpleEntry ? backToSimple : undefined}
|
||||
enabled={enabled}
|
||||
onTogglePause={handleTogglePause}
|
||||
togglingEnabled={togglingEnabled}
|
||||
@@ -1406,7 +1368,6 @@ export function PipelineBuilder() {
|
||||
onIconChange={setIcon}
|
||||
required={required}
|
||||
onRequiredChange={setRequired}
|
||||
onBackToSimple={simpleEntry ? backToSimple : undefined}
|
||||
canSave={canSave}
|
||||
blockers={blockers}
|
||||
saving={submitting}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AddRoundedIcon from "@mui/icons-material/AddRounded";
|
||||
import { Banner, Button, CardRail, EmptyState, Skeleton } from "@app/ui";
|
||||
@@ -44,7 +44,6 @@ import "@portal/views/Pipelines.css";
|
||||
export function Pipelines() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const queryClient = useQueryClient();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
@@ -130,17 +129,6 @@ export function Pipelines() {
|
||||
[navigate, listPath],
|
||||
);
|
||||
|
||||
// Reopen the simple wizard when the builder hands a pipeline back (still template-shaped). Cleared
|
||||
// from history state after so a back/reload doesn't reopen it.
|
||||
useEffect(() => {
|
||||
const reopen = (location.state as { reopenSimple?: Policy } | null)
|
||||
?.reopenSimple;
|
||||
if (!reopen) return;
|
||||
const entry = parseSimplePolicy(reopen);
|
||||
if (entry) setWizard(entry);
|
||||
navigate(location.pathname, { replace: true, state: null });
|
||||
}, [location.state, location.pathname, navigate]);
|
||||
|
||||
// ?setup=<categoryId> deep link (onboarding): open the wizard for that suggested policy, then
|
||||
// strip the param so back/reload doesn't re-open it.
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user