From cac958e094f9dbc7ef2fdd2e0622b424889553e6 Mon Sep 17 00:00:00 2001 From: Zbigniew Sobiecki Date: Sat, 18 Apr 2026 19:21:45 +0000 Subject: [PATCH 1/5] feat(ui): add NativeSelect primitive + DataProps helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SSR-safe ``. Matches the visual language of shadcn + * `SelectTrigger` (border, height, focus ring, dark-mode) but renders a + * native ` + {children} + + ); +} + +export { NativeSelect }; diff --git a/web/src/lib/data-props.ts b/web/src/lib/data-props.ts new file mode 100644 index 00000000..307896e2 --- /dev/null +++ b/web/src/lib/data-props.ts @@ -0,0 +1,20 @@ +/** + * `data-*` index-signature widener. + * + * @types/react 19 dropped the implicit `[key: ` + "`data-${string}`" + `]: unknown` + * index signature from `HTMLAttributes`. JSX has a separate code path + * that still accepts `data-foo="bar"` on intrinsic/component elements, + * but `createElement(Component, { 'data-foo': 'bar' })` no longer + * typechecks under `strict: true` because the object literal is checked + * against `React.ComponentProps` which lacks the + * widening index. + * + * Cast your props literal to `ComponentProps & DataProps` to + * keep the `createElement` call strictly typed for everything else: + * + * createElement(Button, { + * type: 'button', + * 'data-action': 'create-webhook', + * } as React.ComponentProps & DataProps) + */ +export type DataProps = { [key: `data-${string}`]: unknown }; From 8fa2f2df8879e8a682f546d13c1bf6c2041baf74 Mon Sep 17 00:00:00 2001 From: Zbigniew Sobiecki Date: Sat, 18 Apr 2026 19:21:56 +0000 Subject: [PATCH 2/5] refactor(ui): extract shared CopyButton to ui/copy-button.tsx Moved the previously cross-file-imported CopyButton from integration-scm-tab.tsx into its own primitive under ui/copy-button.tsx. The alerting tab and PM webhook adapters now import from the canonical location instead of reaching into a sibling tab module. Co-Authored-By: Claude Opus 4 (1M context) --- .../projects/integration-alerting-tab.tsx | 2 +- .../projects/integration-scm-tab.tsx | 32 ++-------------- web/src/components/ui/copy-button.tsx | 37 +++++++++++++++++++ 3 files changed, 42 insertions(+), 29 deletions(-) create mode 100644 web/src/components/ui/copy-button.tsx diff --git a/web/src/components/projects/integration-alerting-tab.tsx b/web/src/components/projects/integration-alerting-tab.tsx index d36745ed..3363f497 100644 --- a/web/src/components/projects/integration-alerting-tab.tsx +++ b/web/src/components/projects/integration-alerting-tab.tsx @@ -4,11 +4,11 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Trash2 } from 'lucide-react'; import { useState } from 'react'; +import { CopyButton } from '@/components/ui/copy-button.js'; import { Input } from '@/components/ui/input.js'; import { Label } from '@/components/ui/label.js'; import { API_URL } from '@/lib/api.js'; import { trpc, trpcClient } from '@/lib/trpc.js'; -import { CopyButton } from './integration-scm-tab.js'; import { ProjectSecretField } from './project-secret-field.js'; // ============================================================================ diff --git a/web/src/components/projects/integration-scm-tab.tsx b/web/src/components/projects/integration-scm-tab.tsx index 81233f54..471eb15c 100644 --- a/web/src/components/projects/integration-scm-tab.tsx +++ b/web/src/components/projects/integration-scm-tab.tsx @@ -1,14 +1,13 @@ /** * SCM (GitHub) integration tab components. - * Contains: CopyButton, GitHubCredentialSlots, GitHubWebhookSection, SCMTab. - * CopyButton is co-located here and also exported for use by AlertingTab. + * Contains: GitHubCredentialSlots, GitHubWebhookSection, SCMTab. + * `CopyButton` lives at `@/components/ui/copy-button.js` (extracted during + * PM wizard styling restoration). */ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { AlertCircle, AlertTriangle, - Check, - Clipboard, ExternalLink, Info, Loader2, @@ -16,36 +15,13 @@ import { Trash2, } from 'lucide-react'; import { useEffect, useState } from 'react'; +import { CopyButton } from '@/components/ui/copy-button.js'; import { Input } from '@/components/ui/input.js'; import { Label } from '@/components/ui/label.js'; import { API_URL } from '@/lib/api.js'; import { trpc, trpcClient } from '@/lib/trpc.js'; import { ProjectSecretField } from './project-secret-field.js'; -// ============================================================================ -// CopyButton (shared with AlertingTab) -// ============================================================================ - -export function CopyButton({ text }: { text: string }) { - const [copied, setCopied] = useState(false); - const handleCopy = async () => { - await navigator.clipboard.writeText(text); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - return ( - - ); -} - // ============================================================================ // GitHub Credential Slots (replaces the old CredentialSelector dropdowns) // ============================================================================ diff --git a/web/src/components/ui/copy-button.tsx b/web/src/components/ui/copy-button.tsx new file mode 100644 index 00000000..332f5ae9 --- /dev/null +++ b/web/src/components/ui/copy-button.tsx @@ -0,0 +1,37 @@ +import { Check, Clipboard } from 'lucide-react'; +import { useState } from 'react'; + +/** + * Compact copy-to-clipboard button with a `Copy` → `Copied` state toggle. + * + * Extracted from `integration-scm-tab.tsx` where it previously lived as a + * local `export function CopyButton` imported cross-file by + * `integration-alerting-tab.tsx`. Now a proper shared UI primitive: + * additional consumers in the PM wizard path + * (`pm-providers/steps/webhook-url-display.tsx`, the Trello/JIRA curl + * fallbacks) import from here. + */ +export function CopyButton({ text }: { text: string }) { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + if (typeof navigator === 'undefined' || !navigator.clipboard?.writeText) return; + await navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( + + ); +} From 5a5565282292203c04c35cd36141e05e79f4ffe6 Mon Sep 17 00:00:00 2001 From: Zbigniew Sobiecki Date: Sat, 18 Apr 2026 19:22:13 +0000 Subject: [PATCH 3/5] fix(pm-wizard): apply shadcn primitives to shared step components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap raw ,