From d45ce418d87441346110d7b23150a18b14e771fb Mon Sep 17 00:00:00 2001
From: Paolo Scattolin
Date: Fri, 16 Oct 2026 13:48:43 +0200
Subject: [PATCH 1/9] Remove Info and Secondary variants and adjust warning
color
---
src/components/Badge.tsx | 8 ++++----
src/components/Button.tsx | 4 ++--
src/components/Icon.tsx | 2 +-
src/components/Modal.tsx | 2 +-
src/components/NotificationCard.tsx | 10 +++++-----
src/components/Spinner.tsx | 2 +-
src/components/cards/Card.tsx | 2 +-
src/components/utils.test.ts | 14 +-------------
src/components/utils.ts | 4 ----
.../timeline-tab-view/TimelineTabView.tsx | 4 ++--
.../playbook-detail-page/PlaybookDetailCards.tsx | 6 +++---
.../playbook-detail-page/PlaybookTimeline.tsx | 2 +-
src/theme/theme.ts | 10 +++++-----
13 files changed, 27 insertions(+), 43 deletions(-)
diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx
index 7e694ec..343afbb 100644
--- a/src/components/Badge.tsx
+++ b/src/components/Badge.tsx
@@ -9,7 +9,7 @@ interface BadgeProps extends React.HTMLAttributes {
/**
* Badge component for compact status/label pills.
- * Displays colored badges with different variants (success, warning, error, info) and shapes.
+ * Displays colored badges with different variants (primary, success, warning, error, accent) and shapes.
* Supports interactive mode with hover effects for clickable badges.
* @param $variant - Visual variant using ThemeVariant enum.
* @param $shape - Shape of the badge: BadgeShape.Pill (fully rounded ends) or BadgeShape.Rounded (medium border radius). Defaults to Pill.
@@ -31,15 +31,15 @@ export const Badge = styled.span`
border-radius: ${({ $shape = BadgeShape.Pill, theme }) =>
$shape === BadgeShape.Rounded ? theme.radius.md : theme.radius.full};
border: 1px solid
- ${({ $variant = ThemeVariant.Info, theme }) =>
+ ${({ $variant = ThemeVariant.Primary, theme }) =>
getVariantColors(theme, $variant).border};
font: ${({ theme }) => theme.typography.small.font};
line-height: ${({ theme }) => theme.fonts.lineHeight.mini};
- background: ${({ $variant = ThemeVariant.Info, theme }) =>
+ background: ${({ $variant = ThemeVariant.Primary, theme }) =>
getVariantColors(theme, $variant).bg};
- color: ${({ $variant = ThemeVariant.Info, theme }) =>
+ color: ${({ $variant = ThemeVariant.Primary, theme }) =>
getVariantColors(theme, $variant).text};
${({ $isInteractive, theme }) =>
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 529e611..342986c 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -20,7 +20,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes(({ children }) => ({
border-radius: ${({ theme }) => theme.radius.md};
- ${({ $variant = ThemeVariant.Info, $ghost, theme }) => {
+ ${({ $variant = ThemeVariant.Primary, $ghost, theme }) => {
const colors = getVariantColors(theme, $variant);
return `
diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx
index e91ece3..97d3505 100644
--- a/src/components/Icon.tsx
+++ b/src/components/Icon.tsx
@@ -61,7 +61,7 @@ const IconWrapper = styled.span<{
* Renders Tabler icons with theme sizes and optional round containers.
* @param $icon - Tabler icon component from @tabler/icons-react
* @param $size - ThemeSize enum (default: ThemeSize.Large, corrisponding to 16px)
- * @param $variant - "primary" | "secondary" | "success" | "warning" | "error" | "info" (only used with $round)
+ * @param $variant - "primary" | "success" | "warning" | "error" | "accent" (only used with $round)
* @param $ghost - boolean (default: false) - renders transparent background with colored border (only used with $round)
* @param $round - boolean (default: false) - renders icon in a round container with variant colors
* @param $stroke - number (default: 2) - stroke width of the icon
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx
index 7c23e74..4619337 100644
--- a/src/components/Modal.tsx
+++ b/src/components/Modal.tsx
@@ -87,7 +87,7 @@ const Body = styled.div`
* Typically contains action buttons like Cancel/Confirm.
* @example
*
- *
+ *
*
*
*/
diff --git a/src/components/NotificationCard.tsx b/src/components/NotificationCard.tsx
index b702e12..d0d7d94 100644
--- a/src/components/NotificationCard.tsx
+++ b/src/components/NotificationCard.tsx
@@ -10,7 +10,7 @@ export interface NotificationCardProps extends React.HTMLAttributesSomething went wrong. Please try again.
*
* @example
- * // Info notification (default)
+ * // Primary notification (default)
*
* This is an informational message.
*
@@ -38,12 +38,12 @@ export const NotificationCard = styled.div`
border-radius: ${({ theme }) => theme.radius.md};
border: 1px solid
- ${({ $variant = ThemeVariant.Info, theme }) =>
+ ${({ $variant = ThemeVariant.Primary, theme }) =>
getVariantColors(theme, $variant).border};
- background: ${({ $variant = ThemeVariant.Info, theme }) =>
+ background: ${({ $variant = ThemeVariant.Primary, theme }) =>
getVariantColors(theme, $variant).bg};
- color: ${({ $variant = ThemeVariant.Info, theme }) =>
+ color: ${({ $variant = ThemeVariant.Primary, theme }) =>
getVariantColors(theme, $variant).text};
font: ${({ theme }) => theme.typography.body.font};
diff --git a/src/components/Spinner.tsx b/src/components/Spinner.tsx
index c9132fb..54c8a3b 100644
--- a/src/components/Spinner.tsx
+++ b/src/components/Spinner.tsx
@@ -63,7 +63,7 @@ export interface SpinnerProps extends React.HTMLAttributes {
* @param $size - Size of the spinning element
* @param $color - Custom hex/rgb color string for the spinning accent color. Overrides $variant if provided.
* @param $trackColor - Custom hex/rgb color string for the background ring track. Uses theme colors if not specified.
- * @param $variant - Theme color variant: "primary", "success", "error", "warning", "info", "accent", "secondary". Determines accent color from theme. Defaults to "primary".
+ * @param $variant - Theme color variant: "primary", "success", "error", "warning", "accent". Determines accent color from theme. Defaults to "primary".
* @param $withContainer - When true, wraps the spinner in a centered flex container with padding. Defaults to false.
* @param className - Optional CSS class name for custom positioning or styling overrides.
* @example
diff --git a/src/components/cards/Card.tsx b/src/components/cards/Card.tsx
index 3504ff9..d29bc50 100644
--- a/src/components/cards/Card.tsx
+++ b/src/components/cards/Card.tsx
@@ -74,7 +74,7 @@ export const CardBody = styled.div`
* Card footer section for actions, aligned to the right.
* @example
*
- *
+ *
*
*
*/
diff --git a/src/components/utils.test.ts b/src/components/utils.test.ts
index 2981f14..299c7c8 100644
--- a/src/components/utils.test.ts
+++ b/src/components/utils.test.ts
@@ -20,18 +20,12 @@ describe("Component utility functions", () => {
expect(getVariantColors(theme, ThemeVariant.Error)).toEqual(
theme.colors.error,
);
- expect(getVariantColors(theme, ThemeVariant.Info)).toEqual(
- theme.colors.info,
- );
expect(getVariantColors(theme, ThemeVariant.Primary)).toEqual(
theme.colors.info,
);
expect(getVariantColors(theme, ThemeVariant.Accent)).toEqual(
theme.colors.accent,
);
- expect(getVariantColors(theme, ThemeVariant.Secondary)).toEqual(
- theme.colors.accent,
- );
});
test("returns correct colors for all variants with dark theme", () => {
@@ -44,18 +38,12 @@ describe("Component utility functions", () => {
expect(getVariantColors(darkTheme, ThemeVariant.Error)).toEqual(
darkTheme.colors.error,
);
- expect(getVariantColors(darkTheme, ThemeVariant.Info)).toEqual(
- darkTheme.colors.info,
- );
expect(getVariantColors(darkTheme, ThemeVariant.Primary)).toEqual(
darkTheme.colors.info,
);
expect(getVariantColors(darkTheme, ThemeVariant.Accent)).toEqual(
darkTheme.colors.accent,
);
- expect(getVariantColors(darkTheme, ThemeVariant.Secondary)).toEqual(
- darkTheme.colors.accent,
- );
});
test("returns different colors for light and dark themes", () => {
@@ -72,7 +60,7 @@ describe("Component utility functions", () => {
ThemeVariant.Success,
ThemeVariant.Warning,
ThemeVariant.Error,
- ThemeVariant.Info,
+ ThemeVariant.Primary,
ThemeVariant.Accent,
];
diff --git a/src/components/utils.ts b/src/components/utils.ts
index 8f3fe7c..c3b6413 100644
--- a/src/components/utils.ts
+++ b/src/components/utils.ts
@@ -14,10 +14,8 @@ export const enum ThemeVariant {
Success = "success",
Warning = "warning",
Error = "error",
- Info = "info",
Accent = "accent",
Primary = "primary",
- Secondary = "secondary",
}
/**
@@ -128,10 +126,8 @@ export const getVariantColors = (
[ThemeVariant.Success]: "success",
[ThemeVariant.Warning]: "warning",
[ThemeVariant.Error]: "error",
- [ThemeVariant.Info]: "info",
[ThemeVariant.Primary]: "info",
[ThemeVariant.Accent]: "accent",
- [ThemeVariant.Secondary]: "accent",
};
return themeObj.colors[variantMap[variant]] as VariantColors;
};
diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx
index 309aa9f..efbda1b 100644
--- a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx
+++ b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx
@@ -157,7 +157,7 @@ export const TimelineTabView: React.FC = ({
@@ -290,7 +290,7 @@ const UserPendingActionCard: React.FC = ({ onClick }) => {
return (
-
+
User action required
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailCards.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailCards.tsx
index c97043d..ad0e935 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailCards.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailCards.tsx
@@ -51,7 +51,7 @@ export const PlaybookMainDetails: React.FC = ({
Type
{playbook_types?.map((type, idx) => (
-
+
{type}
)) || "N/A"}
@@ -121,7 +121,7 @@ export const PlaybookAdditionalDetails: React.FC<
Impact
- {impact}
+ {impact}
)}
@@ -144,7 +144,7 @@ export const PlaybookAdditionalDetails: React.FC<
{labels.map((label, idx) => (
{label}
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.tsx
index 0338313..2df75c9 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.tsx
@@ -69,7 +69,7 @@ export const PlaybookTimeline: React.FC = ({
Type
-
+
{step.type}
diff --git a/src/theme/theme.ts b/src/theme/theme.ts
index 0616af3..1b64187 100644
--- a/src/theme/theme.ts
+++ b/src/theme/theme.ts
@@ -69,9 +69,9 @@ export const theme = {
},
warning: {
bg: "#fff7a3",
- border: "#facc15",
+ border: "#eab308",
text: "#ca8a04",
- solidBg: "#facc15",
+ solidBg: "#eab308",
solidText: "#ffffff",
},
accent: {
@@ -278,10 +278,10 @@ export const darkTheme: Theme = {
},
warning: {
bg: "#2e2a1a",
- border: "#facc15",
+ border: "#eab308",
text: "#fde68a",
- solidBg: "#facc15",
- solidText: "#1f2937",
+ solidBg: "#eab308",
+ solidText: "#ffffff",
},
accent: {
bg: "#2a1f3d",
From bd4f61c208a76868b07fbdad84595dcd3a31b656 Mon Sep 17 00:00:00 2001
From: Paolo Scattolin
Date: Fri, 16 Oct 2026 13:49:58 +0200
Subject: [PATCH 2/9] Match button borders to their fill in every state
---
src/components/Button.tsx | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 342986c..24a8f68 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -71,27 +71,31 @@ export const Button = styled.button.attrs(({ children }) => ({
${({ $variant = ThemeVariant.Primary, $ghost, theme }) => {
const colors = getVariantColors(theme, $variant);
-
+ const darken = (amount: string) =>
+ `color-mix(in srgb, ${colors.solidBg}, ${theme.colors.shade} ${amount})`;
+ const hoverBg = $ghost
+ ? colors.solidBg
+ : darken(theme.interactions.hoverDarken);
+ const activeBg = darken(theme.interactions.activeDarken);
+ const disabledBorder = $ghost
+ ? theme.colors.gray[500]
+ : theme.colors.gray[300];
+
+ // A filled button always has a border in the same color as its fill.
return `
- border: 1px solid ${colors.border};
+ border: 1px solid ${$ghost ? colors.border : colors.solidBg};
background-color: ${$ghost ? "transparent" : colors.solidBg};
color: ${$ghost ? colors.border : colors.solidText};
&:hover:not(:disabled) {
- background-color: color-mix(
- in srgb,
- ${colors.solidBg},
- ${theme.colors.shade} ${theme.interactions.hoverDarken}
- );
+ border-color: ${hoverBg};
+ background-color: ${hoverBg};
}
&:active:not(:disabled) {
- background-color: color-mix(
- in srgb,
- ${colors.solidBg},
- ${theme.colors.shade} ${theme.interactions.activeDarken}
- );
+ border-color: ${activeBg};
+ background-color: ${activeBg};
}
${
@@ -105,7 +109,7 @@ export const Button = styled.button.attrs(({ children }) => ({
}
&:disabled {
- border-color: ${theme.colors.gray[500]};
+ border-color: ${disabledBorder};
background-color: ${$ghost ? "transparent" : theme.colors.gray[300]};
opacity: ${theme.interactions.disabledOpacity};
From eaaf5f734e7d12938ca67ff0f85a4575cb83cf95 Mon Sep 17 00:00:00 2001
From: Paolo Scattolin
Date: Fri, 16 Oct 2026 13:51:09 +0200
Subject: [PATCH 3/9] Add shared heights for buttons, inputs and selects
---
src/components/Button.tsx | 39 +++++-------------
src/components/CopyButton.tsx | 2 -
src/components/FileDrop.tsx | 1 -
src/components/Form.tsx | 41 ++++++++++++++-----
src/components/Icon.tsx | 6 ---
src/components/Popover.tsx | 7 +---
src/components/Tabs.tsx | 5 +--
src/components/Typography.tsx | 4 --
src/components/utils.ts | 33 +++++++++++++++
src/pages/main-page/MainPage.tsx | 6 +--
.../monitoring-page/ExecutionDetailPage.tsx | 5 +--
.../playbooks-page/PlaybooksPage.tsx | 1 -
.../PlaybookDetailPage.tsx | 4 --
src/theme/theme.ts | 6 +++
14 files changed, 87 insertions(+), 73 deletions(-)
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 24a8f68..2bf8a45 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -1,9 +1,7 @@
-import { Children } from "react";
import styled from "styled-components";
-import { ControlLabel } from "./Typography";
import {
ButtonWidth,
- getThemeSizeValuesByThemeSize,
+ getControlSize,
getVariantColors,
ThemeSize,
ThemeVariant,
@@ -22,7 +20,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributesComplete Task
*/
-export const Button = styled.button.attrs(({ children }) => ({
- children: Children.map(children, (child) =>
- typeof child === "string" || typeof child === "number"
- ? {child}
- : child,
- ),
-}))`
+export const Button = styled.button`
display: inline-flex;
align-items: center;
justify-content: center;
- gap: ${({ $size = ThemeSize.Medium }) =>
- getThemeSizeValuesByThemeSize($size)};
${({ $width }) => ($width === ButtonWidth.Full ? "width: 100%;" : "")}
${({ $size = ThemeSize.Medium, theme }) => {
- switch ($size) {
- case ThemeSize.Small:
- return `
- padding: ${theme.spacing.xs} ${theme.spacing.sm};
- `;
- case ThemeSize.Medium:
- return `
- padding: ${theme.spacing.sm} ${theme.spacing.md};
- `;
- case ThemeSize.Large:
- default:
- return `
- padding: ${theme.spacing.md} ${theme.spacing.lg};
- `;
- }
+ const { height, paddingX, gap } = getControlSize(theme, $size);
+ return `
+ gap: ${gap};
+ height: ${height};
+ padding: 0 ${paddingX};
+ `;
}}
border-radius: ${({ theme }) => theme.radius.md};
@@ -121,6 +102,8 @@ export const Button = styled.button.attrs(({ children }) => ({
}}
font: ${({ theme }) => theme.typography.bodyMedium.font};
+ font-size: ${({ $size = ThemeSize.Medium, theme }) =>
+ getControlSize(theme, $size).fontSize};
cursor: pointer;
transition:
diff --git a/src/components/CopyButton.tsx b/src/components/CopyButton.tsx
index b71340c..8880dc7 100644
--- a/src/components/CopyButton.tsx
+++ b/src/components/CopyButton.tsx
@@ -5,7 +5,6 @@ import toast from "react-hot-toast";
import { formatErrorForToast } from "@/api/utils";
import { Button } from "./Button";
import Icon from "./Icon";
-import { ThemeSize } from "./utils";
export interface CopyButtonProps extends React.ButtonHTMLAttributes {
$text: string;
@@ -40,7 +39,6 @@ export const CopyButton: React.FC = ({
}
@@ -125,7 +125,7 @@ export const DetailsTabView: React.FC = ({ steps }) => {
{status}
diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx
index efbda1b..01861b4 100644
--- a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx
+++ b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.tsx
@@ -131,7 +131,7 @@ export const TimelineTabView: React.FC = ({
{status === StepExecutionStatus.Running ? (
) : status === StepExecutionStatus.Executed ? (
diff --git a/src/pages/main-page/playbooks-page/PlaybookEditPage.tsx b/src/pages/main-page/playbooks-page/PlaybookEditPage.tsx
index 3bf9a57..e331726 100644
--- a/src/pages/main-page/playbooks-page/PlaybookEditPage.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybookEditPage.tsx
@@ -102,7 +102,7 @@ export const PlaybookEditPage: React.FC = () => {
{isLoading ? (
-
+
) : isError || !playbook ? (
diff --git a/src/pages/main-page/playbooks-page/PlaybooksPage.tsx b/src/pages/main-page/playbooks-page/PlaybooksPage.tsx
index cc5ee33..13f14da 100644
--- a/src/pages/main-page/playbooks-page/PlaybooksPage.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybooksPage.tsx
@@ -158,7 +158,7 @@ export const PlaybooksPage: React.FC = () => {
if (isLoading)
return (
-
+
);
if (isError)
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
index 32d4165..10a8acf 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
@@ -127,7 +127,7 @@ export const PlaybookDetailPage: React.FC = () => {
-
+
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.tsx
index 2df75c9..c7efb27 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.tsx
@@ -52,7 +52,7 @@ export const PlaybookTimeline: React.FC = ({
{isExpanded && (
diff --git a/src/theme/theme.ts b/src/theme/theme.ts
index 1519a50..23de124 100644
--- a/src/theme/theme.ts
+++ b/src/theme/theme.ts
@@ -139,31 +139,16 @@ export const theme = {
"2xl": "1.5rem", // 24px
"3xl": "2rem", // 32px
},
- size: {
- xs: "0.25rem", // 4px
- sm: "0.5rem", // 8px
- md: "0.75rem", // 12px
- lg: "1rem", // 16px
- xl: "1.25rem", // 20px
- "2xl": "1.5rem", // 24px
- "3xl": "2rem", // 32px
- "4xl": "3rem", // 48px
- },
// Height of buttons, inputs and selects, so controls of the same size line up.
control_height: {
sm: "1.75rem", // 28px
md: "2.125rem", // 34px
lg: "2.625rem", // 42px
},
- size_pixels: {
- xs: 4,
- sm: 8,
- md: 12,
- lg: 16,
- xl: 20,
- "2xl": 24,
- "3xl": 32,
- "4xl": 48,
+ icon_size: {
+ sm: 12,
+ md: 16,
+ lg: 20,
},
radius: {
sm: "0.25rem",
From 9f49a3101b87d8d9122bd05f38d636e2844e557d Mon Sep 17 00:00:00 2001
From: Paolo Scattolin
Date: Fri, 16 Oct 2026 13:53:49 +0200
Subject: [PATCH 5/9] Trim typography tokens and fix heading levels
---
src/components/CodeEditor.tsx | 2 +-
src/components/ConfirmDialog.tsx | 2 +-
src/components/Modal.tsx | 26 ++++++++++++++-----
src/components/cards/Card.tsx | 2 +-
.../ManualActionModal.styles.tsx | 3 +--
.../timeline-tab-view/ManualActionModal.tsx | 2 +-
.../TimelineTabView.styles.tsx | 3 +--
.../playbooks-page/PlaybooksPage.styles.tsx | 4 +--
.../PlaybookTimeline.styles.tsx | 4 +--
.../playbook-detail-page/RunPlaybookModal.tsx | 2 +-
.../playbooks-page/shared.styles.tsx | 2 +-
src/theme/theme.ts | 18 +++----------
12 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/src/components/CodeEditor.tsx b/src/components/CodeEditor.tsx
index 4f39af8..de8a27b 100644
--- a/src/components/CodeEditor.tsx
+++ b/src/components/CodeEditor.tsx
@@ -24,7 +24,7 @@ const EditorTextarea = styled.textarea<{
background-color: ${({ theme }) => theme.colors.background.secondary};
color: ${({ theme }) => theme.colors.text.primary};
- font: ${({ theme }) => theme.typography.code_small.font};
+ font: ${({ theme }) => theme.typography.code.font};
resize: vertical;
diff --git a/src/components/ConfirmDialog.tsx b/src/components/ConfirmDialog.tsx
index 8ed44f6..0ea3290 100644
--- a/src/components/ConfirmDialog.tsx
+++ b/src/components/ConfirmDialog.tsx
@@ -52,7 +52,7 @@ export const ConfirmDialog: React.FC = ({
{...rest}
>
- {$title}
+ {$title}
{$description}
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx
index 4619337..fcdae0b 100644
--- a/src/components/Modal.tsx
+++ b/src/components/Modal.tsx
@@ -42,12 +42,11 @@ const Dialog = styled.div`
/**
* Modal header section with info-colored background and consistent padding.
- * Displays at the top of the modal with a bottom border separator.
- * Commonly contains a title and optional close button.
+ * Contains a Modal.Title and optional close button.
* @example
*
- * Confirm Action
- *
+ * Confirm action
+ *
*
*/
const Header = styled.div`
@@ -60,7 +59,15 @@ const Header = styled.div`
background-color: ${({ theme }) => theme.colors.info.solidBg};
color: ${({ theme }) => theme.colors.info.solidText};
- font: ${({ theme }) => theme.typography.h3.font};
+`;
+
+/**
+ * Modal title, rendered as an h2.
+ * @example
+ * Run playbook
+ */
+const Title = styled.h2`
+ font: ${({ theme }) => theme.typography.h2.font};
`;
/**
@@ -150,16 +157,20 @@ interface ModalProps extends React.HTMLAttributes {
* Renders with `showModal()` when `$isOpen` is true and handles Escape and backdrop clicks.
* @param $isOpen - Controls whether the modal is visible.
* @param $onClose - Callback invoked when the user requests to close the modal (Escape, backdrop click, or CloseButton).
- * @param children - Use `Modal.Header`, `Modal.Body`, and `Modal.Footer` to structure content.
+ * @param children - Use `Modal.Header` (with a `Modal.Title`), `Modal.Body`, and `Modal.Footer` to structure content.
* @example
*
- * Title
+ *
+ * Title
+ *
+ *
* Content
* Close
*
*/
export const Modal: React.FC & {
Header: typeof Header;
+ Title: typeof Title;
Body: typeof Body;
Footer: typeof Footer;
CloseButton: typeof ModalCloseButton;
@@ -208,6 +219,7 @@ export const Modal: React.FC & {
};
Modal.Header = Header;
+Modal.Title = Title;
Modal.Body = Body;
Modal.Footer = Footer;
Modal.CloseButton = ModalCloseButton;
diff --git a/src/components/cards/Card.tsx b/src/components/cards/Card.tsx
index d29bc50..6486974 100644
--- a/src/components/cards/Card.tsx
+++ b/src/components/cards/Card.tsx
@@ -57,7 +57,7 @@ export const CardHeader = styled.div`
export const CardTitle = styled.h2`
margin: 0;
- font: ${({ theme }) => theme.typography.bodyMedium.font};
+ font: ${({ theme }) => theme.typography.h2.font};
color: ${({ theme }) => theme.colors.text.primary};
`;
/**
diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.styles.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.styles.tsx
index d225ccf..37e89b1 100644
--- a/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.styles.tsx
+++ b/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.styles.tsx
@@ -37,9 +37,8 @@ export const Section = styled.div`
`;
export const SectionTitle = styled.h4`
- font: ${({ theme }) => theme.typography.bodyMedium.font};
+ font: ${({ theme }) => theme.typography.h4.font};
color: ${({ theme }) => theme.colors.text.primary};
- font-weight: 600;
margin: 0 0 ${({ theme }) => theme.spacing.md} 0;
`;
diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.tsx
index 8563c3a..5ea3135 100644
--- a/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.tsx
+++ b/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.tsx
@@ -134,7 +134,7 @@ export const ManualActionModal: React.FC = ({
aria-label="Manual action required"
>
- Manual action required
+ Manual action required
diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.styles.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.styles.tsx
index 42fc25b..b9274b1 100644
--- a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.styles.tsx
+++ b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.styles.tsx
@@ -122,9 +122,8 @@ export const TimelineHeader = styled.div`
export const TimelineTitle = styled.h3`
margin: 0;
- font: ${({ theme }) => theme.typography.body.font};
+ font: ${({ theme }) => theme.typography.h3.font};
color: ${({ theme }) => theme.colors.text.primary};
- font-weight: 500;
`;
export const TimelineMetaBadge = styled.div`
diff --git a/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx b/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
index 8d2f5f1..ff5fba0 100644
--- a/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
@@ -34,9 +34,9 @@ export const PlaybookInfo = styled.div`
margin-right: ${({ theme }) => theme.spacing.lg};
`;
-export const PlaybookName = styled.h4`
+export const PlaybookName = styled.h3`
color: ${({ theme }) => theme.colors.text.primary};
- font: ${({ theme }) => theme.typography.h4.font};
+ font: ${({ theme }) => theme.typography.h3.font};
`;
export const PlaybookDescription = styled.p`
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.styles.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.styles.tsx
index 5bf7ca2..d68c45b 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.styles.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.styles.tsx
@@ -104,9 +104,9 @@ export const StepCardHeader = styled.div<{ $expanded: boolean }>`
}
`;
-export const StepCardTitle = styled.h5`
+export const StepCardTitle = styled.h3`
margin: 0;
- font: ${({ theme }) => theme.typography.bodyMedium.font};
+ font: ${({ theme }) => theme.typography.h3.font};
color: ${({ theme }) => theme.colors.text.primary};
`;
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/RunPlaybookModal.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/RunPlaybookModal.tsx
index ad82832..ca98c73 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/RunPlaybookModal.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/RunPlaybookModal.tsx
@@ -78,7 +78,7 @@ export const RunPlaybookModal: React.FC = ({
aria-label="Run playbook"
>
- Run playbook
+ Run playbook
!mutation.isPending && onClose()}
disabled={mutation.isPending}
diff --git a/src/pages/main-page/playbooks-page/shared.styles.tsx b/src/pages/main-page/playbooks-page/shared.styles.tsx
index 0373380..17b43a1 100644
--- a/src/pages/main-page/playbooks-page/shared.styles.tsx
+++ b/src/pages/main-page/playbooks-page/shared.styles.tsx
@@ -30,7 +30,7 @@ export const EditorTextarea = styled.textarea<{ $hasError: boolean }>`
$hasError ? theme.colors.error.border : theme.colors.border.medium};
background-color: ${({ theme }) => theme.colors.background.secondary};
color: ${({ theme }) => theme.colors.text.primary};
- font: ${({ theme }) => theme.typography.code_medium.font};
+ font: ${({ theme }) => theme.typography.code.font};
resize: vertical;
transition: all ${({ theme }) => theme.transitions.base};
diff --git a/src/theme/theme.ts b/src/theme/theme.ts
index 23de124..eac9020 100644
--- a/src/theme/theme.ts
+++ b/src/theme/theme.ts
@@ -9,10 +9,6 @@ const fonts = {
sm: "0.875rem", // 14px
base: "1rem", // 16px
lg: "1.125rem", // 18px
- xl: "1.25rem", // 20px
- "2xl": "1.5rem", // 24px
- "3xl": "1.875rem", // 30px
- "4xl": "2.25rem", // 36px
},
weight: {
normal: 400,
@@ -188,17 +184,14 @@ export const theme = {
info: 3000,
},
typography: {
- h1: {
- font: `${fonts.weight.bold} ${fonts.size["4xl"]}/${fonts.lineHeight.tight} ${fonts.family.primary}`,
- },
h2: {
- font: `${fonts.weight.semibold} ${fonts.size["2xl"]}/${fonts.lineHeight.tight} ${fonts.family.primary}`,
+ font: `${fonts.weight.semibold} ${fonts.size.lg}/${fonts.lineHeight.tight} ${fonts.family.primary}`,
},
h3: {
- font: `${fonts.weight.semibold} ${fonts.size.xl}/${fonts.lineHeight.tight} ${fonts.family.primary}`,
+ font: `${fonts.weight.semibold} ${fonts.size.base}/${fonts.lineHeight.tight} ${fonts.family.primary}`,
},
h4: {
- font: `${fonts.weight.semibold} ${fonts.size.lg}/${fonts.lineHeight.tight} ${fonts.family.primary}`,
+ font: `${fonts.weight.semibold} ${fonts.size.sm}/${fonts.lineHeight.tight} ${fonts.family.primary}`,
},
body: {
font: `${fonts.weight.normal} ${fonts.size.base}/${fonts.lineHeight.normal} ${fonts.family.primary}`,
@@ -212,12 +205,9 @@ export const theme = {
small: {
font: `${fonts.weight.normal} ${fonts.size.xs}/${fonts.lineHeight.tight} ${fonts.family.primary}`,
},
- code_small: {
+ code: {
font: `${fonts.weight.normal} ${fonts.size.sm}/${fonts.lineHeight.relaxed} ${fonts.family.mono}`,
},
- code_medium: {
- font: `${fonts.weight.normal} ${fonts.size.base}/${fonts.lineHeight.relaxed} ${fonts.family.mono}`,
- },
},
interactions: {
subtleMix: "12%",
From f250fe98d38a342509245e769f084d5a0e807eee Mon Sep 17 00:00:00 2001
From: Paolo Scattolin
Date: Fri, 16 Oct 2026 13:55:02 +0200
Subject: [PATCH 6/9] Add global CSS reset and remove redundant styles
---
src/components/Button.tsx | 1 -
src/components/CodeEditor.tsx | 1 -
src/components/Containers.tsx | 2 -
src/components/FileDrop.tsx | 1 -
src/components/Form.tsx | 2 -
src/components/Icon.tsx | 4 +-
src/components/Modal.tsx | 5 --
src/components/Popover.tsx | 12 -----
src/components/Sidebar.tsx | 1 -
src/components/Table.tsx | 2 -
src/components/Tabs.tsx | 4 --
src/components/Typography.tsx | 6 ---
src/components/cards/Card.tsx | 3 --
src/components/cards/ExpandableCard.tsx | 5 --
src/pages/NotFoundPage.tsx | 5 +-
src/pages/main-page/MainPage.styles.tsx | 5 +-
.../ExecutionDetailPage.styles.tsx | 1 -
.../DetailsTabView.styles.tsx | 12 -----
.../ManualActionModal.styles.tsx | 15 ------
.../TimelineTabView.styles.tsx | 34 ------------
.../PlaybookCreatePage.styles.tsx | 52 -------------------
.../playbooks-page/PlaybooksPage.styles.tsx | 4 +-
.../PlaybookTimeline.styles.tsx | 5 +-
.../playbooks-page/shared.styles.tsx | 1 -
src/theme/GlobalStyles.ts | 31 +++++++++++
25 files changed, 39 insertions(+), 175 deletions(-)
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 2bf8a45..3a2ee6e 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -105,7 +105,6 @@ export const Button = styled.button`
font-size: ${({ $size = ThemeSize.Medium, theme }) =>
getControlSize(theme, $size).fontSize};
- cursor: pointer;
transition:
background-color ${({ theme }) => theme.transitions.fast},
border-color ${({ theme }) => theme.transitions.fast},
diff --git a/src/components/CodeEditor.tsx b/src/components/CodeEditor.tsx
index de8a27b..1e6a745 100644
--- a/src/components/CodeEditor.tsx
+++ b/src/components/CodeEditor.tsx
@@ -12,7 +12,6 @@ const EditorTextarea = styled.textarea<{
$minHeight?: string;
}>`
width: 100%;
- box-sizing: border-box;
min-height: ${({ $minHeight = "400px" }) => $minHeight};
padding: ${({ theme }) => theme.spacing.md};
diff --git a/src/components/Containers.tsx b/src/components/Containers.tsx
index 31d90bc..97109ad 100644
--- a/src/components/Containers.tsx
+++ b/src/components/Containers.tsx
@@ -31,8 +31,6 @@ export const PageContainer = styled.div`
background-position: center;
background-repeat: no-repeat;
`}
-
- padding: 0;
`;
export const CenteredCardContent = styled.div`
diff --git a/src/components/FileDrop.tsx b/src/components/FileDrop.tsx
index 7c19bf9..0aed70d 100644
--- a/src/components/FileDrop.tsx
+++ b/src/components/FileDrop.tsx
@@ -39,7 +39,6 @@ const DropZone = styled.div<{
`;
const DropZoneText = styled.p`
- margin: 0;
font: ${({ theme }) => theme.typography.body.font};
color: ${({ theme }) => theme.colors.text.secondary};
`;
diff --git a/src/components/Form.tsx b/src/components/Form.tsx
index 393f325..ecb85cd 100644
--- a/src/components/Form.tsx
+++ b/src/components/Form.tsx
@@ -17,7 +17,6 @@ interface ControlSizeProps {
*/
export const Input = styled.input`
width: 100%;
- box-sizing: border-box;
border: 1px solid ${({ theme }) => theme.colors.border.medium};
border-radius: ${({ theme }) => theme.radius.md};
@@ -66,7 +65,6 @@ export const Input = styled.input`
*/
const StyledSelect = styled.select`
width: 100%;
- box-sizing: border-box;
appearance: none;
-webkit-appearance: none;
diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx
index 2981827..cd44317 100644
--- a/src/components/Icon.tsx
+++ b/src/components/Icon.tsx
@@ -29,9 +29,9 @@ const IconWrapper = styled.span<{
vertical-align: middle;
height: ${({ $size, $round }) =>
- $round ? `${$size + $size * 0.5}px` : `${$size}px`};
+ $round ? `${$size * 1.5}px` : `${$size}px`};
width: ${({ $size, $round }) =>
- $round ? `${$size + $size * 0.5}px` : `${$size}px`};
+ $round ? `${$size * 1.5}px` : `${$size}px`};
${({ $round }) => $round ? "border-radius: 50%;" : ""}
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx
index fcdae0b..62a6ed9 100644
--- a/src/components/Modal.tsx
+++ b/src/components/Modal.tsx
@@ -118,12 +118,7 @@ const CloseButton = styled.button`
display: flex;
align-items: center;
- border: none;
- background: none;
-
color: ${({ theme }) => theme.colors.primary.text};
-
- cursor: pointer;
`;
/**
diff --git a/src/components/Popover.tsx b/src/components/Popover.tsx
index 685985a..637c3de 100644
--- a/src/components/Popover.tsx
+++ b/src/components/Popover.tsx
@@ -12,13 +12,7 @@ const PopoverTrigger = styled.button<{ $anchorName: string }>`
padding: ${({ theme }) => `${theme.spacing.sm} ${theme.spacing.md}`};
- border: 0;
border-radius: ${({ theme }) => theme.radius.md};
- background: transparent;
-
- color: inherit;
-
- cursor: pointer;
&:hover {
background: ${({ theme }) => theme.colors.info.bg};
@@ -37,7 +31,6 @@ const PopoverContent = styled.div<{ $anchorName: string }>`
position-area: block-end span-inline-end;
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
- box-sizing: border-box;
inline-size: max-content;
min-inline-size: min(10rem, calc(100vw - 1rem));
max-inline-size: min(18rem, calc(100vw - 1rem));
@@ -74,21 +67,16 @@ const PopoverItemButton = styled.button`
align-items: center;
gap: ${({ theme }) => theme.spacing.sm};
- box-sizing: border-box;
inline-size: 100%;
padding: ${({ theme }) => `${theme.spacing.sm} ${theme.spacing.md}`};
- border: 0;
border-radius: ${({ theme }) => theme.radius.md};
- background: transparent;
color: ${({ theme }) => theme.colors.info.border};
font: ${({ theme }) => theme.typography.caption.font};
text-align: start;
- cursor: pointer;
-
&:hover:not(:disabled) {
background: color-mix(
in srgb,
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
index 355eb3f..f73212e 100644
--- a/src/components/Sidebar.tsx
+++ b/src/components/Sidebar.tsx
@@ -91,7 +91,6 @@ export const NavItem = styled.a<{ $isActive?: boolean }>`
border-radius: ${({ theme }) => theme.radius.md};
color: ${({ theme }) => theme.colors.text.secondary};
- border: none;
padding: ${({ theme }) => `${theme.spacing.sm} ${theme.spacing.lg}`};
gap: ${({ theme }) => theme.spacing.md};
diff --git a/src/components/Table.tsx b/src/components/Table.tsx
index a1ba760..d8aa5da 100644
--- a/src/components/Table.tsx
+++ b/src/components/Table.tsx
@@ -33,8 +33,6 @@ export const Table = styled.table`
font: ${({ theme }) => theme.typography.body.font};
color: ${({ theme }) => theme.colors.table.rowText};
-
- background: transparent;
`;
/**
diff --git a/src/components/Tabs.tsx b/src/components/Tabs.tsx
index 999402e..423e134 100644
--- a/src/components/Tabs.tsx
+++ b/src/components/Tabs.tsx
@@ -67,9 +67,6 @@ const TabButton = styled.button<{ $active: boolean }>`
padding: ${({ theme }) => `${theme.spacing.sm} ${theme.spacing.md}`};
- background: none;
- border: none;
-
border-bottom: 2px solid
${({ theme, $active }) =>
$active ? theme.colors.primary.main : "transparent"};
@@ -78,7 +75,6 @@ const TabButton = styled.button<{ $active: boolean }>`
$active ? theme.colors.primary.main : theme.colors.text.secondary};
font: ${({ theme }) => theme.typography.body.font};
- cursor: pointer;
transition: all ${({ theme }) => theme.transitions.base};
&:hover {
diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx
index 07aa6a2..e1066ef 100644
--- a/src/components/Typography.tsx
+++ b/src/components/Typography.tsx
@@ -16,7 +16,6 @@ export interface TextProps extends React.HTMLAttributes {
export const Text = styled.p`
font: ${({ theme }) => theme.typography.body.font};
color: ${({ $color, theme }) => $color || theme.colors.text.secondary};
- margin: 0;
`;
interface ExpandableTextContainerProps extends React.HTMLAttributes {
@@ -40,13 +39,8 @@ const ExpandableTextContainer = styled.div`
`;
const ExpandToggle = styled.button`
- background: none;
- border: none;
-
color: ${({ theme }) => theme.colors.primary.main};
font: ${({ theme }) => theme.typography.small.font};
- cursor: pointer;
- padding: 0;
margin-top: ${({ theme }) => theme.spacing.xs};
text-decoration: underline;
diff --git a/src/components/cards/Card.tsx b/src/components/cards/Card.tsx
index 6486974..5d2a28a 100644
--- a/src/components/cards/Card.tsx
+++ b/src/components/cards/Card.tsx
@@ -11,7 +11,6 @@ import styled from "styled-components";
*
*/
export const CardContainer = styled.div`
- box-sizing: border-box;
width: 100%;
background: ${({ theme }) => theme.colors.background.primary};
@@ -55,8 +54,6 @@ export const CardHeader = styled.div`
* Execution Report
*/
export const CardTitle = styled.h2`
- margin: 0;
-
font: ${({ theme }) => theme.typography.h2.font};
color: ${({ theme }) => theme.colors.text.primary};
`;
diff --git a/src/components/cards/ExpandableCard.tsx b/src/components/cards/ExpandableCard.tsx
index 0020f13..420414c 100644
--- a/src/components/cards/ExpandableCard.tsx
+++ b/src/components/cards/ExpandableCard.tsx
@@ -9,11 +9,6 @@ const ToggleButton = styled.span`
display: inline-flex;
align-items: center;
justify-content: center;
-
- padding: 0;
- border: none;
- background: transparent;
- color: inherit;
`;
const ToggleIconWrapper = styled.span<{ $expanded: boolean }>`
diff --git a/src/pages/NotFoundPage.tsx b/src/pages/NotFoundPage.tsx
index c254474..72b9602 100644
--- a/src/pages/NotFoundPage.tsx
+++ b/src/pages/NotFoundPage.tsx
@@ -64,7 +64,7 @@ const ErrorContainer = styled.div`
box-shadow: ${({ theme }) => theme.shadows.base};
/* Sizing */
- width: 28rem;
+ width: 32rem;
text-align: center;
`;
@@ -73,14 +73,12 @@ const ErrorCode = styled.h1`
font-size: 5rem;
font-weight: ${({ theme }) => theme.fonts.weight.bold};
color: ${({ theme }) => theme.colors.primary.main};
- margin: 0;
line-height: 1;
`;
const ErrorTitle = styled.h2`
/* Typography */
color: ${({ theme }) => theme.colors.text.primary};
- margin: 0;
font: ${({ theme }) => theme.typography.h2.font};
`;
@@ -88,7 +86,6 @@ const ErrorDescription = styled.p`
/* Typography & Layout */
color: ${({ theme }) => theme.colors.text.secondary};
max-width: 400px;
- margin: 0;
font: ${({ theme }) => theme.typography.body.font};
`;
diff --git a/src/pages/main-page/MainPage.styles.tsx b/src/pages/main-page/MainPage.styles.tsx
index 253ea76..5fab7ab 100644
--- a/src/pages/main-page/MainPage.styles.tsx
+++ b/src/pages/main-page/MainPage.styles.tsx
@@ -21,7 +21,7 @@ export const MainHeader = styled.header`
background-color: ${({ theme }) => theme.colors.background.primary};
border-bottom: 1px solid ${({ theme }) => theme.colors.border.light};
padding: ${({ theme }) => theme.spacing.lg} ${({ theme }) => theme.spacing.xl};
- min-height: 2rem;
+ min-height: 4rem;
@media (max-width: 768px) {
.mobile-logo {
@@ -46,11 +46,8 @@ export const MainContent = styled.main`
export const MobileMenuButton = styled.button`
display: none;
- background: none;
- border: none;
color: ${({ theme }) => theme.colors.text.primary};
- cursor: pointer;
@media (max-width: 768px) {
display: flex;
diff --git a/src/pages/main-page/monitoring-page/ExecutionDetailPage.styles.tsx b/src/pages/main-page/monitoring-page/ExecutionDetailPage.styles.tsx
index 01a32a8..d7152c4 100644
--- a/src/pages/main-page/monitoring-page/ExecutionDetailPage.styles.tsx
+++ b/src/pages/main-page/monitoring-page/ExecutionDetailPage.styles.tsx
@@ -2,7 +2,6 @@ import styled from "styled-components";
export const DetailsGrid = styled.div`
width: 100%;
- box-sizing: border-box;
display: grid;
/* minmax(0, ...) stops long unbreakable values (IDs, commands) from
widening a track and pushing content out of the grid */
diff --git a/src/pages/main-page/monitoring-page/details-tab-view/DetailsTabView.styles.tsx b/src/pages/main-page/monitoring-page/details-tab-view/DetailsTabView.styles.tsx
index 77063d0..2bea185 100644
--- a/src/pages/main-page/monitoring-page/details-tab-view/DetailsTabView.styles.tsx
+++ b/src/pages/main-page/monitoring-page/details-tab-view/DetailsTabView.styles.tsx
@@ -6,13 +6,6 @@ export const DetailedContainer = styled.div`
gap: ${({ theme }) => theme.spacing.md};
`;
-export const DetailedItemHeaderRow = styled.div`
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: ${({ theme }) => theme.spacing.md};
-`;
-
export const DetailedItemSummary = styled.div`
display: flex;
gap: ${({ theme }) => theme.spacing.md};
@@ -23,8 +16,6 @@ export const DetailedItemSummary = styled.div`
export const CodeBlock = styled.pre`
width: 100%;
- box-sizing: border-box;
- margin: 0;
background: ${({ theme }) => theme.colors.background.secondary};
border: 1px solid ${({ theme }) => theme.colors.border.light};
border-radius: ${({ theme }) => theme.radius.md};
@@ -37,7 +28,6 @@ export const CodeBlock = styled.pre`
export const VariableListContainer = styled.div`
width: 100%;
- box-sizing: border-box;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing.sm};
@@ -45,7 +35,6 @@ export const VariableListContainer = styled.div`
export const VariableItemRow = styled.div`
width: 100%;
- box-sizing: border-box;
display: flex;
gap: ${({ theme }) => theme.spacing.md};
padding: ${({ theme }) => theme.spacing.sm} ${({ theme }) => theme.spacing.md};
@@ -58,7 +47,6 @@ export const VariableItemRow = styled.div`
export const VariableCellValue = styled.div`
width: 100%;
min-width: 0;
- box-sizing: border-box;
font: ${({ theme }) => theme.typography.body.font};
color: ${({ theme }) => theme.colors.text.primary};
word-break: break-word;
diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.styles.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.styles.tsx
index 37e89b1..f78a0eb 100644
--- a/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.styles.tsx
+++ b/src/pages/main-page/monitoring-page/timeline-tab-view/ManualActionModal.styles.tsx
@@ -1,20 +1,6 @@
import { IconInfoCircle } from "@tabler/icons-react";
import styled from "styled-components";
-import { ModalCloseButton } from "@/components";
-
-export const StyledCloseButton = styled(ModalCloseButton)`
- color: #ffffff;
- opacity: 0.9;
- font-size: 24px;
- font-weight: bold;
- transition: opacity 0.2s;
-
- &:hover {
- opacity: 1;
- }
-`;
-
export const StepSummary = styled.div`
margin-bottom: ${({ theme }) => theme.spacing.lg};
`;
@@ -28,7 +14,6 @@ export const StepName = styled.h3`
export const StepDescription = styled.p`
font: ${({ theme }) => theme.typography.body.font};
color: ${({ theme }) => theme.colors.text.secondary};
- margin: 0;
line-height: 1.5;
`;
diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.styles.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.styles.tsx
index b9274b1..8f7f92c 100644
--- a/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.styles.tsx
+++ b/src/pages/main-page/monitoring-page/timeline-tab-view/TimelineTabView.styles.tsx
@@ -6,7 +6,6 @@ export const TimelineContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing.lg};
- padding: 0;
`;
export const TimelineRow = styled.div<{ $delay?: number }>`
@@ -37,26 +36,6 @@ export const TimelineIconContainer = styled.div`
z-index: 2;
`;
-export const SpinnerIconContainer = styled.div<{
- $variant: "primary" | "warning";
-}>`
- display: inline-flex;
- align-items: center;
- justify-content: center;
- height: 24px;
- width: 24px;
- border-radius: 50%;
- background-color: ${({ theme, $variant }) =>
- $variant === "warning"
- ? theme.colors.warning.solidBg
- : theme.colors.primary.main};
- border: 1px solid
- ${({ theme, $variant }) =>
- $variant === "warning"
- ? theme.colors.warning.border
- : theme.colors.primary.main};
-`;
-
export const TimelineLeftCell = styled.div`
display: flex;
justify-content: flex-end;
@@ -80,18 +59,12 @@ export const TimelineItemWrapper = styled.div<{ $side: "left" | "right" }>`
export const TimelineItem = styled.div`
position: relative;
width: fit-content;
- padding: 0;
- border: none;
- background: transparent;
box-shadow: none;
`;
export const UserReplyItem = styled.div`
position: relative;
width: fit-content;
- padding: 0;
- border: none;
- background: transparent;
box-shadow: none;
`;
@@ -115,13 +88,9 @@ export const TimelineHeader = styled.div`
align-items: center;
justify-content: space-between;
gap: ${({ theme }) => theme.spacing.md};
- padding: 0;
- border: none;
- background: transparent;
`;
export const TimelineTitle = styled.h3`
- margin: 0;
font: ${({ theme }) => theme.typography.h3.font};
color: ${({ theme }) => theme.colors.text.primary};
`;
@@ -130,9 +99,6 @@ export const TimelineMetaBadge = styled.div`
display: inline-flex;
align-items: center;
gap: ${({ theme }) => theme.spacing.sm};
- padding: 0;
- background: transparent;
- border: none;
border-radius: 0;
font: ${({ theme }) => theme.typography.small.font};
color: ${({ theme }) => theme.colors.text.secondary};
diff --git a/src/pages/main-page/playbooks-page/PlaybookCreatePage.styles.tsx b/src/pages/main-page/playbooks-page/PlaybookCreatePage.styles.tsx
index 9afec1f..ebcba65 100644
--- a/src/pages/main-page/playbooks-page/PlaybookCreatePage.styles.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybookCreatePage.styles.tsx
@@ -21,7 +21,6 @@ export const MethodButton = styled.button<{ $active: boolean }>`
$active ? theme.colors.info.bg : theme.colors.background.primary};
color: ${({ theme }) => theme.colors.text.primary};
font: ${({ theme }) => theme.typography.bodyMedium.font};
- cursor: pointer;
transition: all ${({ theme }) => theme.transitions.base};
&:hover {
@@ -34,54 +33,3 @@ export const MethodButton = styled.button<{ $active: boolean }>`
cursor: not-allowed;
}
`;
-
-export const DropZone = styled.div<{
- $isDragActive: boolean;
- $hasError: boolean;
-}>`
- border: 2px dashed
- ${({ theme, $isDragActive, $hasError }) =>
- $hasError
- ? theme.colors.error.border
- : $isDragActive
- ? theme.colors.info.border
- : theme.colors.border.medium};
- border-radius: ${({ theme }) => theme.radius.md};
- padding: ${({ theme }) => theme.spacing["3xl"]};
- text-align: center;
- background-color: ${({ theme, $isDragActive }) =>
- $isDragActive ? theme.colors.info.bg : theme.colors.background.secondary};
- cursor: pointer;
- transition: all ${({ theme }) => theme.transitions.base};
- min-height: 300px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: ${({ theme }) => theme.spacing.md};
-
- &:hover {
- border-color: ${({ theme }) => theme.colors.info.border};
- background-color: ${({ theme }) => theme.colors.info.bg};
- }
-`;
-
-export const DropZoneText = styled.p`
- margin: 0;
- font: ${({ theme }) => theme.typography.body.font};
- color: ${({ theme }) => theme.colors.text.secondary};
-`;
-
-export const DropZoneHint = styled.span`
- font: ${({ theme }) => theme.typography.small.font};
- color: ${({ theme }) => theme.colors.text.tertiary};
-`;
-
-export const FileName = styled.div`
- font: ${({ theme }) => theme.typography.bodyMedium.font};
- color: ${({ theme }) => theme.colors.text.primary};
- margin-top: ${({ theme }) => theme.spacing.md};
- display: flex;
- align-items: center;
- gap: ${({ theme }) => theme.spacing.sm};
-`;
diff --git a/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx b/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
index ff5fba0..a3ffdd5 100644
--- a/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
@@ -94,8 +94,8 @@ export const TimelineStep = styled.div<{ $position: "top" | "bottom" }>`
`;
export const TimelineDot = styled.div<{ $type: string }>`
- width: 12px;
- height: 12px;
+ width: 16px;
+ height: 16px;
border: 2px solid ${({ theme }) => theme.colors.background.primary};
border-radius: ${({ theme }) => theme.radius.full};
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.styles.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.styles.tsx
index d68c45b..8cfba9e 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.styles.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookTimeline.styles.tsx
@@ -52,8 +52,8 @@ export const TimelineDotCenter = styled.div<{ $type: string }>`
left: 50%;
top: 24px;
transform: translate(-50%, -50%);
- width: 16px;
- height: 16px;
+ width: 22px;
+ height: 22px;
border-radius: ${({ theme }) => theme.radius.full};
background-color: ${({ theme, $type }) => {
switch ($type) {
@@ -105,7 +105,6 @@ export const StepCardHeader = styled.div<{ $expanded: boolean }>`
`;
export const StepCardTitle = styled.h3`
- margin: 0;
font: ${({ theme }) => theme.typography.h3.font};
color: ${({ theme }) => theme.colors.text.primary};
`;
diff --git a/src/pages/main-page/playbooks-page/shared.styles.tsx b/src/pages/main-page/playbooks-page/shared.styles.tsx
index 17b43a1..17f1866 100644
--- a/src/pages/main-page/playbooks-page/shared.styles.tsx
+++ b/src/pages/main-page/playbooks-page/shared.styles.tsx
@@ -21,7 +21,6 @@ export const EditorContainer = styled.div`
export const EditorTextarea = styled.textarea<{ $hasError: boolean }>`
width: 100%;
- box-sizing: border-box;
min-height: 500px;
padding: ${({ theme }) => theme.spacing.md};
border-radius: ${({ theme }) => theme.radius.md};
diff --git a/src/theme/GlobalStyles.ts b/src/theme/GlobalStyles.ts
index 9710227..948c4a7 100644
--- a/src/theme/GlobalStyles.ts
+++ b/src/theme/GlobalStyles.ts
@@ -2,10 +2,41 @@ import { createGlobalStyle } from "styled-components";
export const GlobalStyles = createGlobalStyle`
*, *::before, *::after {
+ box-sizing: border-box;
margin: 0;
padding: 0;
}
+ input, button, textarea, select {
+ font: inherit;
+ color: inherit;
+ }
+
+ button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ }
+
+ /* Headings are chosen for document structure; their look comes from theme.typography. */
+ h1, h2, h3, h4, h5, h6 {
+ font-size: inherit;
+ font-weight: inherit;
+ }
+
+ p, h1, h2, h3, h4, h5, h6 {
+ overflow-wrap: break-word;
+ }
+
+ p {
+ text-wrap: pretty;
+ }
+
+ img {
+ display: block;
+ max-width: 100%;
+ }
+
html, body, #root {
height: 100%;
width: 100%;
From 174e41c7acb68c31ee1e8b9496ed8b0f750309de Mon Sep 17 00:00:00 2001
From: Paolo Scattolin
Date: Fri, 16 Oct 2026 13:56:07 +0200
Subject: [PATCH 7/9] Fix FileDrop and ExportButton defaults and small layout
details
---
src/components/ExportButton.tsx | 8 +-------
src/components/FileDrop.tsx | 11 ++++++++++-
src/pages/main-page/MainPage.styles.tsx | 1 +
.../playbook-detail-page/PlaybookDetailPage.tsx | 1 +
src/theme/theme.ts | 2 +-
5 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/src/components/ExportButton.tsx b/src/components/ExportButton.tsx
index 769ebe9..b7e93c0 100644
--- a/src/components/ExportButton.tsx
+++ b/src/components/ExportButton.tsx
@@ -1,6 +1,5 @@
import React from "react";
import { Button } from "./Button";
-import { ButtonWidth } from "./utils";
import { downloadExport, ExportFormat } from "./export";
export type { ExportFormat } from "./export";
@@ -43,12 +42,7 @@ export const ExportButton: React.FC = ({
};
return (
-
+
{children}
);
diff --git a/src/components/FileDrop.tsx b/src/components/FileDrop.tsx
index 0aed70d..2d188e2 100644
--- a/src/components/FileDrop.tsx
+++ b/src/components/FileDrop.tsx
@@ -21,7 +21,7 @@ const DropZone = styled.div<{
text-align: center;
cursor: pointer;
- min-height: 400px;
+ min-height: 12rem;
display: flex;
flex-direction: column;
align-items: center;
@@ -36,6 +36,15 @@ const DropZone = styled.div<{
border-color: ${({ theme }) => theme.colors.info.border};
background-color: ${({ theme }) => theme.colors.info.bg};
}
+
+ /* Disabled state comes from the hidden file input right before the label. */
+ input:disabled + label & {
+ opacity: ${({ theme }) => theme.interactions.disabledOpacity};
+ cursor: not-allowed;
+
+ border-color: ${({ theme }) => theme.colors.border.medium};
+ background-color: ${({ theme }) => theme.colors.background.secondary};
+ }
`;
const DropZoneText = styled.p`
diff --git a/src/pages/main-page/MainPage.styles.tsx b/src/pages/main-page/MainPage.styles.tsx
index 5fab7ab..113244a 100644
--- a/src/pages/main-page/MainPage.styles.tsx
+++ b/src/pages/main-page/MainPage.styles.tsx
@@ -39,6 +39,7 @@ export const MainHeader = styled.header`
export const MainContent = styled.main`
flex: 1;
overflow-y: auto;
+ scrollbar-gutter: stable;
padding: ${({ theme }) => theme.spacing.lg};
min-width: 550px;
background-color: ${({ theme }) => theme.colors.secondary.bg};
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
index 10a8acf..4a5dc6d 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
@@ -208,6 +208,7 @@ export const PlaybookDetailPage: React.FC = () => {
Date: Fri, 16 Oct 2026 13:57:13 +0200
Subject: [PATCH 8/9] Make component prop names consistent
---
src/components/Badge.tsx | 10 ++---
src/components/Button.tsx | 13 +++----
src/components/CodeEditor.tsx | 18 ++++-----
src/components/CopyButton.tsx | 12 +++---
src/components/ExportButton.tsx | 10 ++---
src/components/FileDrop.tsx | 28 +++++++-------
src/components/Link.tsx | 37 ++++++++-----------
src/components/RadioGroup.tsx | 20 ++++++----
src/components/Tabs.tsx | 26 ++++++-------
src/components/Typography.tsx | 20 +++++-----
src/components/utils.ts | 5 ---
src/pages/LoginPage.tsx | 9 ++---
src/pages/NotFoundPage.tsx | 5 +--
src/pages/main-page/MainPage.tsx | 5 +--
.../monitoring-page/ExecutionDetailPage.tsx | 4 +-
.../details-tab-view/DetailsTabView.tsx | 4 +-
.../timeline-tab-view/VariableInputs.tsx | 4 +-
.../playbooks-page/PlaybookCreatePage.tsx | 8 ++--
.../playbooks-page/PlaybookEditPage.tsx | 4 +-
.../PlaybookDetailPage.tsx | 11 +++---
20 files changed, 120 insertions(+), 133 deletions(-)
diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx
index 343afbb..68260a9 100644
--- a/src/components/Badge.tsx
+++ b/src/components/Badge.tsx
@@ -4,7 +4,7 @@ import { BadgeShape, ThemeVariant, getVariantColors } from "./utils";
interface BadgeProps extends React.HTMLAttributes {
$variant?: ThemeVariant;
$shape?: BadgeShape;
- $isInteractive?: boolean;
+ $isClickable?: boolean;
}
/**
@@ -13,13 +13,13 @@ interface BadgeProps extends React.HTMLAttributes {
* Supports interactive mode with hover effects for clickable badges.
* @param $variant - Visual variant using ThemeVariant enum.
* @param $shape - Shape of the badge: BadgeShape.Pill (fully rounded ends) or BadgeShape.Rounded (medium border radius). Defaults to Pill.
- * @param $isInteractive - When true, adds hover/active states including translateY animation and shadow for clickable badges. Defaults to false.
+ * @param $isClickable - When true, adds hover/active states including translateY animation and shadow for clickable badges. Defaults to false.
* @example
* // Simple success badge
* Active
* @example
* // Interactive warning badge with rounded shape
- * Click me
+ * Click me
*/
export const Badge = styled.span`
display: inline-flex;
@@ -42,8 +42,8 @@ export const Badge = styled.span`
color: ${({ $variant = ThemeVariant.Primary, theme }) =>
getVariantColors(theme, $variant).text};
- ${({ $isInteractive, theme }) =>
- $isInteractive
+ ${({ $isClickable, theme }) =>
+ $isClickable
? `
cursor: pointer;
transition: transform ${theme.transitions.base},
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 3a2ee6e..0df0780 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -1,6 +1,5 @@
import styled from "styled-components";
import {
- ButtonWidth,
getControlSize,
getVariantColors,
ThemeSize,
@@ -11,7 +10,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributesSubmit
+ * Submit
* @example
- * // Ghost error button with auto width
- * Delete
+ * // Ghost error button
+ * Delete
* @example
* // Large success button
* Complete Task
@@ -37,7 +36,7 @@ export const Button = styled.button`
align-items: center;
justify-content: center;
- ${({ $width }) => ($width === ButtonWidth.Full ? "width: 100%;" : "")}
+ ${({ $fullWidth }) => ($fullWidth ? "width: 100%;" : "")}
${({ $size = ThemeSize.Medium, theme }) => {
const { height, paddingX, gap } = getControlSize(theme, $size);
diff --git a/src/components/CodeEditor.tsx b/src/components/CodeEditor.tsx
index 1e6a745..c645b13 100644
--- a/src/components/CodeEditor.tsx
+++ b/src/components/CodeEditor.tsx
@@ -43,8 +43,8 @@ const EditorTextarea = styled.textarea<{
export interface CodeEditorProps extends React.HTMLAttributes {
$value: string;
$onChange: (value: string) => void;
- $placeholder?: string;
- $disabled?: boolean;
+ placeholder?: string;
+ disabled?: boolean;
$hasError?: boolean;
$minHeight?: string;
}
@@ -54,22 +54,22 @@ export interface CodeEditorProps extends React.HTMLAttributes {
* It is a controlled component and relies on `$value` / `$onChange` for state.
* @param $value - Current text content of the editor.
* @param $onChange - Callback invoked with new text when the content changes.
- * @param $placeholder - Optional placeholder text when editor is empty.
- * @param $disabled - When true the editor is non-editable and shows disabled visuals.
+ * @param placeholder - Optional placeholder text when editor is empty.
+ * @param disabled - When true the editor is non-editable and shows disabled visuals.
* @param $hasError - When true the editor displays an error border state.
* @param $minHeight - Optional CSS min-height for the editor area (defaults to `400px`).
* @example:
*
*/
export const CodeEditor: React.FC = ({
$value,
$onChange,
- $placeholder,
- $disabled,
+ placeholder,
+ disabled,
$hasError,
$minHeight,
...rest
@@ -79,8 +79,8 @@ export const CodeEditor: React.FC = ({
$onChange(e.target.value)}
- placeholder={$placeholder}
- disabled={$disabled}
+ placeholder={placeholder}
+ disabled={disabled}
$hasError={$hasError}
$minHeight={$minHeight}
/>
diff --git a/src/components/CopyButton.tsx b/src/components/CopyButton.tsx
index 8880dc7..5aa0851 100644
--- a/src/components/CopyButton.tsx
+++ b/src/components/CopyButton.tsx
@@ -7,28 +7,28 @@ import { Button } from "./Button";
import Icon from "./Icon";
export interface CopyButtonProps extends React.ButtonHTMLAttributes {
- $text: string;
+ $value: string;
}
/**
* Small icon-only button to copy text to clipboard.
- * @param $text - The text to copy to clipboard.
+ * @param $value - The text to copy to the clipboard.
* @param title - The tooltip/title for the button (default: "Copy").
* @param disabled - Whether the button is disabled (default: false).
* @example
- *
+ *
*/
export const CopyButton: React.FC = ({
- $text,
+ $value,
disabled = false,
...rest
}) => {
const handleClick = async (e: React.MouseEvent) => {
e.stopPropagation();
- if (disabled || !$text) return;
+ if (disabled || !$value) return;
try {
- await navigator.clipboard.writeText($text);
+ await navigator.clipboard.writeText($value);
toast.success("Copied to clipboard");
} catch (err) {
toast.error(formatErrorForToast(err, "Failed to copy to clipboard"));
diff --git a/src/components/ExportButton.tsx b/src/components/ExportButton.tsx
index b7e93c0..01cf1f2 100644
--- a/src/components/ExportButton.tsx
+++ b/src/components/ExportButton.tsx
@@ -7,7 +7,7 @@ export type { ExportFormat } from "./export";
export interface ExportButtonProps extends React.ComponentProps {
$data: unknown;
$format: ExportFormat;
- $filename?: string;
+ $fileName?: string;
$onSuccess?: () => void;
$onError?: (err: unknown) => void;
}
@@ -16,17 +16,17 @@ export interface ExportButtonProps extends React.ComponentProps {
* ExportButton component to export data as a file in various formats.
*
* @param $data - The value to export. Can be any serializable value for json or a string for txt.
- * @param $filename - Optional suggested filename. If not provided, defaults to export-..
+ * @param $fileName - Optional suggested filename. If not provided, defaults to export-..
* @param $format - **(required)** Selects how to transform data into a Blob. Can be 'json', 'txt', or a custom function returning { blob, filename? }.
* @param $onSuccess - Optional callback invoked after successful export.
* @param $onError - Optional callback invoked if export fails.
* @example
- * Export
+ * Export
*/
export const ExportButton: React.FC = ({
$data,
$format,
- $filename,
+ $fileName,
$onSuccess,
$onError,
children,
@@ -34,7 +34,7 @@ export const ExportButton: React.FC = ({
}) => {
const handleClick = () => {
try {
- downloadExport($data, $format, $filename);
+ downloadExport($data, $format, $fileName);
if ($onSuccess) $onSuccess();
} catch (err) {
if ($onError) $onError(err);
diff --git a/src/components/FileDrop.tsx b/src/components/FileDrop.tsx
index 2d188e2..fa7f2d4 100644
--- a/src/components/FileDrop.tsx
+++ b/src/components/FileDrop.tsx
@@ -72,8 +72,8 @@ export interface FileDropProps extends React.HTMLAttributes {
$onChange: (content: string, filename?: string) => void;
$onRemove?: () => void;
$accept?: string;
- $disabled?: boolean;
- $error?: string;
+ disabled?: boolean;
+ $hasError?: boolean;
}
/**
@@ -85,8 +85,8 @@ export interface FileDropProps extends React.HTMLAttributes {
* @param $onChange - Called with (content, filename) when a file is selected or dropped.
* @param $onRemove - Optional callback when the remove button is clicked.
* @param $accept - File extension filter (e.g. ".json"). Defaults to ".json".
- * @param $disabled - When true, disables interactions.
- * @param $error - Optional error message to display under the drop zone.
+ * @param disabled - When true, disables interactions.
+ * @param $hasError - When true the drop zone shows an error border. The page shows the error message itself.
* @example
* setFile(content)} $accept=".json" />
*/
@@ -95,8 +95,8 @@ export const FileDrop: React.FC = ({
$onChange,
$onRemove,
$accept = ".json",
- $disabled,
- $error,
+ disabled,
+ $hasError = false,
...rest
}) => {
const [isDragActive, setIsDragActive] = useState(false);
@@ -122,7 +122,7 @@ export const FileDrop: React.FC = ({
(e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
- if ($disabled) return;
+ if (disabled) return;
setIsDragActive(false);
const files = e.dataTransfer.files;
if (files && files.length > 0) {
@@ -134,17 +134,17 @@ export const FileDrop: React.FC = ({
readFile(file);
}
},
- [$accept, $disabled, $onChange, readFile],
+ [$accept, disabled, $onChange, readFile],
);
const handleDragOver = useCallback(
(e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
- if ($disabled) return;
+ if (disabled) return;
setIsDragActive(true);
},
- [$disabled],
+ [disabled],
);
const handleDragLeave = useCallback(
@@ -179,18 +179,18 @@ export const FileDrop: React.FC = ({
accept={$accept}
onChange={handleFileSelect}
style={{ display: "none" }}
- disabled={$disabled}
+ disabled={disabled}
/>
} />
@@ -79,8 +79,8 @@ export interface ExpandableTextProps {
export const ExpandableText: React.FC = ({
$text,
$lineClamp,
- $showMoreText,
- $showLessText,
+ $showMoreLabel,
+ $showLessLabel,
}) => {
const [expanded, setExpanded] = useState(false);
const [isClamped, setIsClamped] = useState(false);
@@ -114,8 +114,8 @@ export const ExpandableText: React.FC = ({
{isClamped && (
setExpanded(!expanded)}>
{expanded
- ? $showLessText || "Show less"
- : $showMoreText || "Show more"}
+ ? $showLessLabel || "Show less"
+ : $showMoreLabel || "Show more"}
)}
diff --git a/src/components/utils.ts b/src/components/utils.ts
index 84efce0..64935f4 100644
--- a/src/components/utils.ts
+++ b/src/components/utils.ts
@@ -39,11 +39,6 @@ export const enum BadgeShape {
Rounded = "rounded",
}
-export const enum ButtonWidth {
- Full = "full",
- Auto = "auto",
-}
-
export const TabsContext = createContext(
undefined,
);
diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx
index 6032158..a52eedd 100644
--- a/src/pages/LoginPage.tsx
+++ b/src/pages/LoginPage.tsx
@@ -7,7 +7,6 @@ import logo from "@/assets/soarca-logo-cropped.svg";
import {
Button,
- ButtonWidth,
FormContainer,
FormGroup,
FormLabel,
@@ -47,14 +46,14 @@ export const LoginPage: React.FC = () => {
Log in
Log in with OIDC
@@ -67,11 +66,11 @@ export const LoginPage: React.FC = () => {
$align="center"
$justify="space-between"
>
-
+
Forgot password
-
+
Help
diff --git a/src/pages/NotFoundPage.tsx b/src/pages/NotFoundPage.tsx
index 72b9602..8792845 100644
--- a/src/pages/NotFoundPage.tsx
+++ b/src/pages/NotFoundPage.tsx
@@ -4,7 +4,6 @@ import { useNavigate } from "react-router";
import soarcaBackground from "@/assets/soarca-background.jpg";
import {
Button,
- ButtonWidth,
Icon,
PageContainer,
ThemeVariant,
@@ -27,14 +26,14 @@ export const NotFoundPage: React.FC = () => {
navigate(-1)}
>
Go Back
navigate(PATHS.BASE)}
>
Take me home
diff --git a/src/pages/main-page/MainPage.tsx b/src/pages/main-page/MainPage.tsx
index 6be6a6f..bb3b09b 100644
--- a/src/pages/main-page/MainPage.tsx
+++ b/src/pages/main-page/MainPage.tsx
@@ -19,7 +19,6 @@ import { PATHS, SOARCA_DOC_URL } from "@/utils";
import {
Button,
- ButtonWidth,
Icon,
ImageContainer,
Link,
@@ -90,7 +89,7 @@ export const MainPage: React.FC = () => {
handleNavigation(PATHS.PLAYBOOKS.NEW)}
>
@@ -117,7 +116,7 @@ export const MainPage: React.FC = () => {
{
No steps found for this execution.
) : (
-
+
= ({ steps }) => {
{Array.isArray(step.commands_b64) &&
step.commands_b64.length > 0 && (
decodeBase64(c) || c)
.join("\n")}
title="Copy commands"
@@ -222,7 +222,7 @@ export const DetailsTabView: React.FC = ({ steps }) => {
{step.variables &&
Object.keys(step.variables).length > 0 && (
`${v?.name || k} (${v?.type || "unknown"}): ${v?.value ?? ""}`,
diff --git a/src/pages/main-page/monitoring-page/timeline-tab-view/VariableInputs.tsx b/src/pages/main-page/monitoring-page/timeline-tab-view/VariableInputs.tsx
index b5ec3e9..54eb915 100644
--- a/src/pages/main-page/monitoring-page/timeline-tab-view/VariableInputs.tsx
+++ b/src/pages/main-page/monitoring-page/timeline-tab-view/VariableInputs.tsx
@@ -35,8 +35,8 @@ const DictionaryInput: React.FC<{
diff --git a/src/pages/main-page/playbooks-page/PlaybookCreatePage.tsx b/src/pages/main-page/playbooks-page/PlaybookCreatePage.tsx
index c350d90..8ec3d59 100644
--- a/src/pages/main-page/playbooks-page/PlaybookCreatePage.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybookCreatePage.tsx
@@ -120,8 +120,8 @@ export const PlaybookCreatePage: React.FC = () => {
setError("");
}}
$accept=".json"
- $disabled={createMutation.isPending}
- $error={error}
+ disabled={createMutation.isPending}
+ $hasError={!!error}
/>
>
) : (
@@ -136,8 +136,8 @@ export const PlaybookCreatePage: React.FC = () => {
setError(validation.error || "Invalid JSON");
}
}}
- $placeholder="Paste or write your CACAO playbook JSON here..."
- $disabled={createMutation.isPending}
+ placeholder="Paste or write your CACAO playbook JSON here..."
+ disabled={createMutation.isPending}
$hasError={!!error}
$minHeight="400px"
/>
diff --git a/src/pages/main-page/playbooks-page/PlaybookEditPage.tsx b/src/pages/main-page/playbooks-page/PlaybookEditPage.tsx
index e331726..3671132 100644
--- a/src/pages/main-page/playbooks-page/PlaybookEditPage.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybookEditPage.tsx
@@ -128,8 +128,8 @@ export const PlaybookEditPage: React.FC = () => {
setError(validation.error || "Invalid JSON");
}
}}
- $placeholder="Edit your CACAO playbook JSON here..."
- $disabled={updateMutation.isPending}
+ placeholder="Edit your CACAO playbook JSON here..."
+ disabled={updateMutation.isPending}
$hasError={!!error}
$minHeight="500px"
/>
diff --git a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
index 4a5dc6d..c6f8150 100644
--- a/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
+++ b/src/pages/main-page/playbooks-page/playbook-detail-page/PlaybookDetailPage.tsx
@@ -15,7 +15,6 @@ import { triggerPlaybookById } from "@/api/trigger";
import { formatErrorForToast, getErrorFromApiResponse } from "@/api/utils";
import {
Button,
- ButtonWidth,
CardBody,
CardContainer,
CardHeader,
@@ -174,7 +173,7 @@ export const PlaybookDetailPage: React.FC = () => {
navigate(PATHS.PLAYBOOKS.EDIT.replace(":playbookId", playbookId!))
@@ -185,7 +184,7 @@ export const PlaybookDetailPage: React.FC = () => {
setOpenDeleteConfirm(true)}
disabled={deleteMutation.isPending}
@@ -195,7 +194,7 @@ export const PlaybookDetailPage: React.FC = () => {
hasExternalVars ? setOpenRunModal(true) : setOpenRunConfirm(true)
@@ -208,10 +207,10 @@ export const PlaybookDetailPage: React.FC = () => {
toast.success("Playbook exported")}
$onError={(err) =>
From d73ddb34c47de7e11d239df58ab7bde1a92db635 Mon Sep 17 00:00:00 2001
From: Paul2803k <44923416+Paul2803k@users.noreply.github.com>
Date: Wed, 21 Oct 2026 15:38:55 +0200
Subject: [PATCH 9/9] Feature/116 add search and filter functions to playbooks
(#123)
* Add playbook search by name, ID and type
* Restyle playbook card with a left accent bar
---
.../playbooks-page/PlaybooksPage.styles.tsx | 31 ++++++++++---
.../playbooks-page/PlaybooksPage.tsx | 44 ++++++++++++++-----
src/pages/main-page/playbooks-page/utils.ts | 16 +++++++
3 files changed, 74 insertions(+), 17 deletions(-)
diff --git a/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx b/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
index a3ffdd5..7486651 100644
--- a/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybooksPage.styles.tsx
@@ -1,6 +1,8 @@
import styled from "styled-components";
export const PlaybookListItem = styled.div`
+ position: relative;
+
display: flex;
align-items: center;
gap: ${({ theme }) => theme.spacing["2xl"]};
@@ -8,20 +10,30 @@ export const PlaybookListItem = styled.div`
padding-left: ${({ theme }) => theme.spacing.lg};
padding-right: ${({ theme }) => theme.spacing.lg};
- border: 2px solid ${({ theme }) => theme.colors.palette.primary};
- border-radius: ${({ theme }) => theme.radius.lg};
+ border: 1px solid ${({ theme }) => theme.colors.border.medium};
+ border-radius: ${({ theme }) => `0 ${theme.radius.sm} ${theme.radius.sm} 0`};
background-color: ${({ theme }) => theme.colors.background.primary};
+ &::before {
+ content: "";
+ position: absolute;
+ top: -1px;
+ bottom: -1px;
+ left: -1px;
+
+ width: 4px;
+
+ background-color: ${({ theme }) => theme.colors.palette.primary};
+ }
+
cursor: pointer;
transition:
background-color ${({ theme }) => theme.transitions.base},
- border-color ${({ theme }) => theme.transitions.base},
- box-shadow ${({ theme }) => theme.transitions.base};
+ border-color ${({ theme }) => theme.transitions.base};
&:hover {
border-color: ${({ theme }) => theme.colors.info.border};
background-color: ${({ theme }) => theme.colors.info.bg};
- box-shadow: ${({ theme }) => theme.shadows.md};
}
`;
@@ -144,6 +156,15 @@ export const PlaybookActions = styled.div`
padding-left: ${({ theme }) => theme.spacing.md};
`;
+export const HeaderActions = styled.div`
+ display: flex;
+ align-items: center;
+ gap: ${({ theme }) => theme.spacing.md};
+
+ width: 100%;
+ max-width: 400px;
+`;
+
export const PlaybookList = styled.div`
display: flex;
flex-direction: column;
diff --git a/src/pages/main-page/playbooks-page/PlaybooksPage.tsx b/src/pages/main-page/playbooks-page/PlaybooksPage.tsx
index 13f14da..45d9f16 100644
--- a/src/pages/main-page/playbooks-page/PlaybooksPage.tsx
+++ b/src/pages/main-page/playbooks-page/PlaybooksPage.tsx
@@ -1,6 +1,6 @@
import { ThemeSize, ThemeVariant } from "@/components";
-import { useQuery } from "@tanstack/react-query";
import { IconFilePlus } from "@tabler/icons-react";
+import { useQuery } from "@tanstack/react-query";
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router";
@@ -14,13 +14,16 @@ import {
CardTitle,
CenteredCardContent,
Icon,
+ Input,
Spinner,
Text,
} from "@/components";
import { Playbook, Step } from "@/types";
import { PATHS, sortByString } from "@/utils";
+import { PlaybookActionsMenu } from "./PlaybookActionsMenu";
import {
+ HeaderActions,
PlaybookActions,
PlaybookDescription,
PlaybookInfo,
@@ -34,8 +37,7 @@ import {
TimelineStep,
TimelineSteps,
} from "./PlaybooksPage.styles";
-import { getOrderedSteps } from "./utils";
-import { PlaybookActionsMenu } from "./PlaybookActionsMenu";
+import { filterPlaybooks, getOrderedSteps } from "./utils";
interface PlaybookTimelineProps {
workflow: Record;
@@ -135,6 +137,7 @@ const PlaybookItem: React.FC = ({ playbook, onClick }) => {
export const PlaybooksPage: React.FC = () => {
const navigate = useNavigate();
+ const [search, setSearch] = useState("");
const { data, isLoading, isError, error } = useQuery({
queryKey: ["playbooks"],
queryFn: getPlaybooks,
@@ -142,7 +145,7 @@ export const PlaybooksPage: React.FC = () => {
});
const sortedPlaybooks = sortByString(
- [...(data || [])],
+ [...filterPlaybooks(data || [], search)],
(p) => p.name || "",
true,
false,
@@ -164,7 +167,13 @@ export const PlaybooksPage: React.FC = () => {
if (isError)
return {parsedError?.message || "Could not load playbooks"};
if (sortedPlaybooks.length === 0)
- return No playbooks available;
+ return (
+
+ {search.trim()
+ ? `No playbooks match "${search.trim()}"`
+ : "No playbooks available"}
+
+ );
return (
{sortedPlaybooks.map((playbook) => (
@@ -182,13 +191,24 @@ export const PlaybooksPage: React.FC = () => {
Playbooks
- navigate(PATHS.PLAYBOOKS.NEW)}
- >
-
- New
-
+
+ setSearch(e.target.value)}
+ />
+ navigate(PATHS.PLAYBOOKS.NEW)}
+ >
+
+ New
+
+
{renderBody()}
diff --git a/src/pages/main-page/playbooks-page/utils.ts b/src/pages/main-page/playbooks-page/utils.ts
index 5af8a5a..d145401 100644
--- a/src/pages/main-page/playbooks-page/utils.ts
+++ b/src/pages/main-page/playbooks-page/utils.ts
@@ -101,6 +101,22 @@ export const getOrderedSteps = (
return steps;
};
+/**
+ * Case-insensitive search on name, id and playbook types.
+ */
+export const filterPlaybooks = (
+ playbooks: Playbook[],
+ query: string,
+): Playbook[] => {
+ const q = query.trim().toLowerCase();
+ if (!q) return playbooks;
+ return playbooks.filter((p) =>
+ [p.name, p.id, ...(p.playbook_types ?? [])].some((field) =>
+ field?.toLowerCase().includes(q),
+ ),
+ );
+};
+
/**
* Generates a safe filename for exporting a playbook.
* Format: {id}-{modified-date}.json