From 2a1273b6831ccdde1335fef3fcdba8dacf136ad7 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Mon, 4 Mar 2024 17:33:53 +0000 Subject: [PATCH] Major v3 web app update (#924) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added the new colours to the Tailwind config * Fixed Tsailwind compile errors * WIP Geist working in the app * WIP Storybook * Storybook is working again… ouch * Use Geist mono for CodeMirror * Some find/replace class changes * Resizable colours improved * WIP on primary button * Minor style improvements * Improve the side menu header styling * Improved v2 breadcrumbs * Account side menu tweaks * The page now works with the NavBar and UpgradePrompt * Removed unused import * Refactored the buttons to be dry-er * Removed the breadcrumbs * WIP on nav bar * WIP updating the button styles * Fix for the old test page * Fix for type errors * Removed console.log * Colour change in the resizable handle * New loading bar divider * Removed the design plugin * Creating our own storybook pages * Added some “storybook” pages * Added DetailCell storybook * WIP new animation for the loading page * Added the new loading divider * Fix for the time frame filter having a green border * Style improvements to the new test page * Small style improvements * Improvements to the list pagination components * Nav bar back button improved * Improvements to the run page * Lots of v2 style improvements * Help looks better * Improvements to the regenerate API key modal * More general improvements * Added section titles to our storybook * Added form stories * Fix for missing key prop * Loading bar is working * Fixed button submitting state * Fix for the test button icon * Code bg improved * Improved oauth buttons * Integration page header fix * Various improvements, mainly changing button variants * Segmented control style updates * Added an outline of 3px for the segment control * Fixes for the billing pages * More pricing improvements * Added dialog to storybook * Typography now in storybook * Free usage bar added to storybook * Added inline clode to storybook * Separated menu types into a sub section in storybook * NamedIcon now in storybook * Added page header to storybook * Added pricing callout to storybook * Added radio groups to storybook * Added segmented controllers to storybook * Added shortcuts to storybook * Added switches to storybook * Added toast to storybook * Added tooltips to storybook * Added tree view to storybook * Added usage charts to storybook * Tarted up the environment variables page * Fix for the FreePlanUsage panel bg colours * Added new badge variants * Added the to the tasks table * Span header improvements * Started work on the timeline * Started work on the timeline * Fix for the run page header * Timeline tick marks are working * Timeline working * Got the durations showing on hover * Button improvements * Experimenting with how the duration should be shown * Lots of refinements to the timeline * Much nicer API for the Timeline * The switch is now purple to match the design, with inverted thumb when on * Removed old stuff * Export the props * Added the span with duration back in * Simplified more and fixed the span durations * Put in some decent dummy data * Rendering defaults * Wip with the mouse cursor * Improvements to the story * Added Slider primitive * Integrated the icons into the slider * Lots of layout improvements * Cleaned up imports * “Root” indicator * Animated background on partials * Pull the styles through to the spans and log dots * Fixed some bugs in the TreeView when nodes get created or destroyed * Fix for incorrect parentTask reference * Added run streaming endpoint for live refreshing * Added the tabs to Storybook * Fix for status font size mismatch * Fix for the misaligned run filter status * Set a minimum duration on the timeline * The TreeView now re-calculates if the number of nodes has changed * Partials now render to the end * Improved the durations gutter * The duration is calculated using the current time if there are partial events * Added the environment label to storybook * Improved the environment label style * Removed bottom margin from badge * Button style improvements * Callouts style fits the new design better * Got the scrollbars to appear on the tree/timeline * Added scroll support * Synced scrolling working * Close button and exit icon for the detail panel * secure field shows start of env * Clipboard improvements * Latest lockfile * Fix for the durations wrapping --------- Co-authored-by: James Ritchie --- apps/webapp/.storybook/main.ts | 1 - apps/webapp/.storybook/preview.tsx | 2 +- apps/webapp/app/assets/icons/ExitIcon.tsx | 14 + .../assets/images/error-banner-tile@2x.png | Bin 0 -> 166 bytes .../app/assets/images/tile-in-progress@2x.png | Bin 0 -> 170 bytes apps/webapp/app/components/ActiveBadge.tsx | 6 +- apps/webapp/app/components/ComingSoon.tsx | 6 +- .../app/components/DefinitionTooltip.tsx | 2 +- apps/webapp/app/components/ErrorDisplay.tsx | 2 +- apps/webapp/app/components/Feedback.tsx | 6 +- apps/webapp/app/components/ListPagination.tsx | 16 +- .../webapp/app/components/LoginPageLayout.tsx | 8 +- .../webapp/app/components/NoMobileOverlay.tsx | 4 +- .../app/components/ProductHuntBanner.tsx | 2 +- .../app/components/UserProfilePhoto.tsx | 2 +- apps/webapp/app/components/VersionLabel.tsx | 6 +- .../app/components/billing/FreePlanUsage.tsx | 6 +- .../components/billing/PricingCalculator.tsx | 30 +- .../app/components/billing/PricingTiers.tsx | 31 +- .../billing/RunsVolumeDiscountTable.tsx | 2 +- .../app/components/billing/UpgradePrompt.tsx | 70 +- .../app/components/billing/UsageBar.tsx | 8 +- apps/webapp/app/components/code/CodeBlock.tsx | 24 +- .../webapp/app/components/code/InlineCode.tsx | 2 +- .../webapp/app/components/code/JSONEditor.tsx | 31 +- .../app/components/code/codeMirrorTheme.ts | 13 +- .../environments/EndpointIndexStatus.tsx | 2 +- .../environments/EnvironmentLabel.tsx | 20 +- .../environments/RegenerateApiKeyModal.tsx | 82 +- .../app/components/events/EventsFilters.tsx | 2 +- .../app/components/events/EventsTable.tsx | 8 +- .../frameworks/FrameworkComingSoon.tsx | 8 +- .../frameworks/FrameworkSelector.tsx | 2 +- .../helpContent/HelpContentText.tsx | 9 +- .../ConnectToIntegrationSheet.tsx | 6 +- .../integrations/ConnectToOAuthForm.tsx | 6 +- .../components/integrations/CustomHelp.tsx | 4 +- .../IntegrationWithMissingFieldSheet.tsx | 2 +- .../integrations/SelectOAuthMethod.tsx | 5 +- .../integrations/UpdateOAuthForm.tsx | 6 +- .../components/jobs/DeleteJobModalContent.tsx | 11 +- .../app/components/jobs/JobSkeleton.tsx | 10 +- .../app/components/layout/AppLayout.tsx | 37 +- .../components/navigation/AccountSideMenu.tsx | 12 +- .../app/components/navigation/Breadcrumb.tsx | 35 - .../navigation/PageNavigationIndicator.tsx | 10 - .../app/components/navigation/SideMenu.tsx | 26 +- .../components/navigation/SideMenuHeader.tsx | 6 +- .../components/navigation/SideMenuItem.tsx | 12 +- .../app/components/primitives/Alert.tsx | 6 +- .../app/components/primitives/Badge.tsx | 11 +- .../components/primitives/BreadcrumbIcon.tsx | 2 +- .../app/components/primitives/Buttons.tsx | 247 ++-- .../app/components/primitives/Callout.tsx | 58 +- .../app/components/primitives/Checkbox.tsx | 34 +- .../app/components/primitives/ClientTabs.tsx | 10 +- .../components/primitives/ClipboardField.tsx | 43 +- .../app/components/primitives/DateField.tsx | 16 +- .../app/components/primitives/DetailCell.tsx | 10 +- .../app/components/primitives/Dialog.tsx | 13 +- .../app/components/primitives/FormTitle.tsx | 2 +- .../app/components/primitives/Headers.tsx | 4 +- .../webapp/app/components/primitives/Help.tsx | 8 +- .../webapp/app/components/primitives/Icon.tsx | 22 +- .../app/components/primitives/Input.tsx | 57 +- .../components/primitives/LabelValueStack.tsx | 2 +- .../primitives/LoadingBarDivider.tsx | 52 + .../app/components/primitives/NamedIcon.tsx | 46 +- .../app/components/primitives/PageHeader.tsx | 79 +- .../app/components/primitives/Pagination.tsx | 23 +- .../app/components/primitives/Paragraph.tsx | 30 +- .../app/components/primitives/Popover.tsx | 18 +- .../app/components/primitives/RadioButton.tsx | 36 +- .../app/components/primitives/Resizable.tsx | 10 +- .../primitives/SegmentedControl.tsx | 10 +- .../app/components/primitives/Select.tsx | 17 +- .../app/components/primitives/Sheet.tsx | 14 +- .../app/components/primitives/ShortcutKey.tsx | 4 +- .../app/components/primitives/Slider.tsx | 52 + .../app/components/primitives/StepNumber.tsx | 4 +- .../app/components/primitives/Switch.tsx | 12 +- .../app/components/primitives/Table.tsx | 20 +- .../webapp/app/components/primitives/Tabs.tsx | 8 +- .../app/components/primitives/TextArea.tsx | 2 +- .../app/components/primitives/TextLink.tsx | 2 +- .../app/components/primitives/Timeline.tsx | 243 +++ .../app/components/primitives/Toast.tsx | 6 +- .../app/components/primitives/Tooltip.tsx | 8 +- .../primitives/TreeView/TreeView.tsx | 64 +- .../components/primitives/TreeView/reducer.ts | 13 + .../components/primitives/TreeView/utils.ts | 2 +- apps/webapp/app/components/run/RunCard.tsx | 18 +- .../webapp/app/components/run/RunOverview.tsx | 157 +- .../app/components/run/TaskAttemptStatus.tsx | 2 +- apps/webapp/app/components/run/TaskCard.tsx | 8 +- .../app/components/run/TaskCardSkeleton.tsx | 6 +- apps/webapp/app/components/run/TaskStatus.tsx | 4 +- .../webapp/app/components/runs/RunFilters.tsx | 6 +- .../app/components/runs/RunStatuses.tsx | 6 +- apps/webapp/app/components/runs/RunsTable.tsx | 8 +- .../app/components/runs/TimeFrameFilter.tsx | 10 +- .../runs/WebhookDeliveryRunsTable.tsx | 8 +- .../app/components/runs/v3/RunFilters.tsx | 4 +- .../webapp/app/components/runs/v3/RunIcon.tsx | 10 +- .../app/components/runs/v3/SpanTitle.tsx | 57 +- .../app/components/runs/v3/TaskRunStatus.tsx | 18 +- .../app/components/runs/v3/TaskRunsTable.tsx | 8 +- .../app/components/stories/Badges.stories.tsx | 25 - .../components/stories/Callout.stories.tsx | 56 - .../components/stories/CodeBlock.stories.tsx | 184 --- .../app/components/stories/Dialog.stories.tsx | 51 - .../app/components/stories/Forms.stories.tsx | 253 ---- .../components/stories/InlineCode.stories.tsx | 32 - .../app/components/stories/Menus.stories.tsx | 144 -- .../components/stories/PageHeader.stories.tsx | 69 - .../stories/SegmentedControl.stories.tsx | 38 - .../app/presenters/v3/RunPresenter.server.ts | 33 +- .../v3/RunStreamPresenter.server.ts | 118 ++ .../route.tsx | 47 +- .../route.tsx | 18 +- .../route.tsx | 21 +- .../route.tsx | 154 +- .../route.tsx | 64 +- .../route.tsx | 16 +- .../route.tsx | 10 +- .../route.tsx | 12 +- .../route.tsx | 98 +- .../route.tsx | 25 +- .../route.tsx | 41 +- .../ConfigureEndpointSheet.tsx | 3 +- .../route.tsx | 50 +- .../route.tsx | 64 +- .../route.tsx | 48 +- .../route.tsx | 6 - .../route.tsx | 66 +- .../route.tsx | 38 +- .../route.tsx | 6 - .../route.tsx | 26 +- .../route.tsx | 12 +- .../route.tsx | 313 ++-- .../route.tsx | 180 +-- .../route.tsx | 48 +- .../route.tsx | 12 +- .../route.tsx | 10 +- .../route.tsx | 10 +- .../route.tsx | 7 - .../route.tsx | 10 +- .../route.tsx | 10 +- .../route.tsx | 7 - .../route.tsx | 7 - .../route.tsx | 10 +- .../route.tsx | 11 +- .../route.tsx | 11 +- .../route.tsx | 2 +- .../route.tsx | 14 +- .../route.tsx | 20 +- .../route.tsx | 84 +- .../route.tsx | 219 ++- .../route.tsx | 40 +- .../route.tsx | 60 +- .../route.tsx | 35 - .../route.tsx | 26 +- .../route.tsx | 43 +- .../route.tsx | 44 +- .../route.tsx | 10 +- .../route.tsx | 22 +- .../route.tsx | 42 +- .../route.tsx | 18 +- .../route.tsx | 96 +- .../route.tsx | 38 +- .../route.tsx | 13 + .../route.tsx | 494 +++++-- .../route.tsx | 10 +- .../route.tsx | 58 +- .../route.tsx | 20 +- .../route.tsx | 10 +- .../route.tsx | 11 +- .../route.tsx | 32 +- .../_app.orgs.$organizationSlug/route.tsx | 27 +- .../route.tsx | 6 +- .../route.tsx | 2 +- .../route.tsx | 4 +- .../webapp/app/routes/_app.orgs.new/route.tsx | 9 +- .../app/routes/account._index/route.tsx | 18 +- .../app/routes/account.tokens/route.tsx | 50 +- apps/webapp/app/routes/account/route.tsx | 22 +- apps/webapp/app/routes/admin._index.tsx | 13 +- apps/webapp/app/routes/invites.tsx | 2 +- apps/webapp/app/routes/login._index/route.tsx | 4 +- apps/webapp/app/routes/login.magic/route.tsx | 6 +- .../app/routes/resources.codeexample.tsx | 2 +- .../app/routes/storybook.badges/route.tsx | 15 + .../storybook.buttons/route.tsx} | 162 +- .../app/routes/storybook.callout/route.tsx | 62 + .../storybook.checkboxes/route.tsx} | 28 +- .../storybook.clipboard-field/route.tsx} | 39 +- .../app/routes/storybook.code-block/route.tsx | 39 + .../storybook.detail-cell/route.tsx} | 29 +- .../app/routes/storybook.dialog/route.tsx | 27 + .../storybook.environment-label/route.tsx | 23 + .../storybook.free-plan-usage/route.tsx} | 23 +- .../routes/storybook.inline-code/route.tsx | 12 + .../routes/storybook.input-fields/route.tsx | 108 ++ .../app/routes/storybook.login-form/route.tsx | 39 + .../storybook.named-icon/route.tsx} | 35 +- .../routes/storybook.page-header/route.tsx | 29 + .../app/routes/storybook.popover/route.tsx | 36 + .../storybook.pricing-callout/route.tsx} | 20 +- .../storybook.radio-group/route.tsx} | 23 +- .../routes/storybook.search-fields/route.tsx | 56 + .../storybook.segmented-control/route.tsx | 15 + .../app/routes/storybook.select/route.tsx | 91 ++ .../storybook.shortcuts/route.tsx} | 32 +- .../routes/storybook.simple-form/route.tsx | 55 + .../storybook.switch/route.tsx} | 17 +- .../storybook.tabs.$tabNumber/route.tsx | 10 + .../app/routes/storybook.tabs/route.tsx | 18 + .../app/routes/storybook.timeline/route.tsx | 276 ++++ .../storybook.toast/route.tsx} | 25 +- .../storybook.tooltip/route.tsx} | 38 +- .../storybook.tree-view/route.tsx} | 27 +- .../storybook.typography/route.tsx} | 45 +- .../storybook.usage/route.tsx} | 22 +- apps/webapp/app/routes/storybook/route.tsx | 210 +++ apps/webapp/app/tailwind.css | 11 +- apps/webapp/app/utils/handle.ts | 2 - apps/webapp/app/utils/pathBuilder.ts | 8 + apps/webapp/package.json | 13 +- apps/webapp/postcss.config.js | 5 +- apps/webapp/tailwind.config.js | 249 ++-- config-packages/tailwind-config/package.json | 9 - .../tailwind-config/postcss.config.js | 6 - .../tailwind-config/tailwind.config.js | 24 - package.json | 6 +- packages/core/src/v3/utils/durations.ts | 4 + pnpm-lock.yaml | 1300 +++++++++++------ .../app/components/ReactHooks.tsx | 4 +- .../app/components/SendReactHookEventForm.tsx | 2 +- .../app/events/[eventId]/page.tsx | 4 +- references/package-tester/package.json | 2 +- 240 files changed, 5187 insertions(+), 4513 deletions(-) create mode 100644 apps/webapp/app/assets/icons/ExitIcon.tsx create mode 100644 apps/webapp/app/assets/images/error-banner-tile@2x.png create mode 100644 apps/webapp/app/assets/images/tile-in-progress@2x.png delete mode 100644 apps/webapp/app/components/navigation/Breadcrumb.tsx delete mode 100644 apps/webapp/app/components/navigation/PageNavigationIndicator.tsx create mode 100644 apps/webapp/app/components/primitives/LoadingBarDivider.tsx create mode 100644 apps/webapp/app/components/primitives/Slider.tsx create mode 100644 apps/webapp/app/components/primitives/Timeline.tsx delete mode 100644 apps/webapp/app/components/stories/Badges.stories.tsx delete mode 100644 apps/webapp/app/components/stories/Callout.stories.tsx delete mode 100644 apps/webapp/app/components/stories/CodeBlock.stories.tsx delete mode 100644 apps/webapp/app/components/stories/Dialog.stories.tsx delete mode 100644 apps/webapp/app/components/stories/Forms.stories.tsx delete mode 100644 apps/webapp/app/components/stories/InlineCode.stories.tsx delete mode 100644 apps/webapp/app/components/stories/Menus.stories.tsx delete mode 100644 apps/webapp/app/components/stories/PageHeader.stories.tsx delete mode 100644 apps/webapp/app/components/stories/SegmentedControl.stories.tsx create mode 100644 apps/webapp/app/presenters/v3/RunStreamPresenter.server.ts create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.stream/route.tsx create mode 100644 apps/webapp/app/routes/storybook.badges/route.tsx rename apps/webapp/app/{components/stories/Button.stories.tsx => routes/storybook.buttons/route.tsx} (73%) create mode 100644 apps/webapp/app/routes/storybook.callout/route.tsx rename apps/webapp/app/{components/stories/Checkboxes.stories.tsx => routes/storybook.checkboxes/route.tsx} (81%) rename apps/webapp/app/{components/stories/ClipboardField.stories.tsx => routes/storybook.clipboard-field/route.tsx} (76%) create mode 100644 apps/webapp/app/routes/storybook.code-block/route.tsx rename apps/webapp/app/{components/stories/DetailCell.stories.tsx => routes/storybook.detail-cell/route.tsx} (50%) create mode 100644 apps/webapp/app/routes/storybook.dialog/route.tsx create mode 100644 apps/webapp/app/routes/storybook.environment-label/route.tsx rename apps/webapp/app/{components/stories/FreePlanUsage.stories.tsx => routes/storybook.free-plan-usage/route.tsx} (60%) create mode 100644 apps/webapp/app/routes/storybook.inline-code/route.tsx create mode 100644 apps/webapp/app/routes/storybook.input-fields/route.tsx create mode 100644 apps/webapp/app/routes/storybook.login-form/route.tsx rename apps/webapp/app/{components/stories/NamedIcon.stories.tsx => routes/storybook.named-icon/route.tsx} (54%) create mode 100644 apps/webapp/app/routes/storybook.page-header/route.tsx create mode 100644 apps/webapp/app/routes/storybook.popover/route.tsx rename apps/webapp/app/{components/stories/PricingCallout.stories.tsx => routes/storybook.pricing-callout/route.tsx} (56%) rename apps/webapp/app/{components/stories/RadioGroup.stories.tsx => routes/storybook.radio-group/route.tsx} (74%) create mode 100644 apps/webapp/app/routes/storybook.search-fields/route.tsx create mode 100644 apps/webapp/app/routes/storybook.segmented-control/route.tsx create mode 100644 apps/webapp/app/routes/storybook.select/route.tsx rename apps/webapp/app/{components/stories/Shortcuts.stories.tsx => routes/storybook.shortcuts/route.tsx} (70%) create mode 100644 apps/webapp/app/routes/storybook.simple-form/route.tsx rename apps/webapp/app/{components/stories/Switch.stories.tsx => routes/storybook.switch/route.tsx} (59%) create mode 100644 apps/webapp/app/routes/storybook.tabs.$tabNumber/route.tsx create mode 100644 apps/webapp/app/routes/storybook.tabs/route.tsx create mode 100644 apps/webapp/app/routes/storybook.timeline/route.tsx rename apps/webapp/app/{components/stories/ToastUI.stories.tsx => routes/storybook.toast/route.tsx} (69%) rename apps/webapp/app/{components/stories/Tooltip.stories.tsx => routes/storybook.tooltip/route.tsx} (55%) rename apps/webapp/app/{components/stories/TreeView.stories.tsx => routes/storybook.tree-view/route.tsx} (89%) rename apps/webapp/app/{components/stories/Typography.stories.tsx => routes/storybook.typography/route.tsx} (76%) rename apps/webapp/app/{components/stories/UsageBar.stories.tsx => routes/storybook.usage/route.tsx} (78%) create mode 100644 apps/webapp/app/routes/storybook/route.tsx delete mode 100644 config-packages/tailwind-config/package.json delete mode 100644 config-packages/tailwind-config/postcss.config.js delete mode 100644 config-packages/tailwind-config/tailwind.config.js diff --git a/apps/webapp/.storybook/main.ts b/apps/webapp/.storybook/main.ts index 1fea1efde..60217579c 100644 --- a/apps/webapp/.storybook/main.ts +++ b/apps/webapp/.storybook/main.ts @@ -27,7 +27,6 @@ const config: StorybookConfig = { "@storybook/addon-interactions", "storybook-addon-variants", "storybook-addon-designs", - "@storybook/addon-docs", { name: "@storybook/addon-styling", options: { diff --git a/apps/webapp/.storybook/preview.tsx b/apps/webapp/.storybook/preview.tsx index 14932b462..c5113ec63 100644 --- a/apps/webapp/.storybook/preview.tsx +++ b/apps/webapp/.storybook/preview.tsx @@ -19,7 +19,7 @@ const preview: Preview = { values: [ { name: "App background", - value: "#0B1018", + value: "#15171A", }, ], }, diff --git a/apps/webapp/app/assets/icons/ExitIcon.tsx b/apps/webapp/app/assets/icons/ExitIcon.tsx new file mode 100644 index 000000000..3bbd24cfc --- /dev/null +++ b/apps/webapp/app/assets/icons/ExitIcon.tsx @@ -0,0 +1,14 @@ +export function ExitIcon({ className }: { className?: string }) { + return ( + + + + + + ); +} diff --git a/apps/webapp/app/assets/images/error-banner-tile@2x.png b/apps/webapp/app/assets/images/error-banner-tile@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2d601bfced71586c33d929a53d03a0b7ab5362e1 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`xt=bLAr*{E&l&PHDDbo8}6&zKpAR{qw#<3AbAh`X-2k zGqOx)=qheFXu09%gn*oW?dG@7PbJ)*ijd1)RvO20)J*@rQ1;6DZBJW(7BhId`njxg HN@xNAz+FJi literal 0 HcmV?d00001 diff --git a/apps/webapp/app/assets/images/tile-in-progress@2x.png b/apps/webapp/app/assets/images/tile-in-progress@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5724c3a0a2de74fd7f64c902fba768600798126a GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;Ap{I*uNCo5C%duPxiX1EtO5O*3 zw?FqTGpti#k^E&zi4TcC?k!32o+ftqo$}!mJ)ZZ4D_UPVdgQS0+93MkSXu&y^g)Ab z%>rv04SWuJ_)L=Q^gPYE=g^k~m2J$iH@HfYjSX6Q6C_?caxAM1`YXn<>?fmCgIeAL T*Ohrd%Naaf{an^LB{Ts5SSUS& literal 0 HcmV?d00001 diff --git a/apps/webapp/app/components/ActiveBadge.tsx b/apps/webapp/app/components/ActiveBadge.tsx index cb7aef6a0..0b0ba45e9 100644 --- a/apps/webapp/app/components/ActiveBadge.tsx +++ b/apps/webapp/app/components/ActiveBadge.tsx @@ -17,13 +17,13 @@ export function ActiveBadge({ active, className, badgeSize = "normal" }: ActiveB switch (active) { case true: return ( - + Active ); case false: return ( - + Disabled ); @@ -52,6 +52,6 @@ export function NewBadge({ badgeSize?: keyof typeof variant; }) { return ( - New! + New! ); } diff --git a/apps/webapp/app/components/ComingSoon.tsx b/apps/webapp/app/components/ComingSoon.tsx index 283f1f4a5..54d4f9e0a 100644 --- a/apps/webapp/app/components/ComingSoon.tsx +++ b/apps/webapp/app/components/ComingSoon.tsx @@ -13,10 +13,10 @@ type ComingSoonProps = { export function ComingSoon({ title, description, icon }: ComingSoonProps) { return ( -
-
+
+
{typeof icon === "string" ? ( - + ) : ( icon )} diff --git a/apps/webapp/app/components/DefinitionTooltip.tsx b/apps/webapp/app/components/DefinitionTooltip.tsx index ad02663f9..d69924149 100644 --- a/apps/webapp/app/components/DefinitionTooltip.tsx +++ b/apps/webapp/app/components/DefinitionTooltip.tsx @@ -15,7 +15,7 @@ export function DefinitionTip({ - + {children} diff --git a/apps/webapp/app/components/ErrorDisplay.tsx b/apps/webapp/app/components/ErrorDisplay.tsx index c40360a27..d1e763bef 100644 --- a/apps/webapp/app/components/ErrorDisplay.tsx +++ b/apps/webapp/app/components/ErrorDisplay.tsx @@ -40,7 +40,7 @@ type DisplayOptionsProps = { export function ErrorDisplay({ title, message, button }: DisplayOptionsProps) { return (
- {title} + {title} {message && {message}} {button ? button.title : "Home"} diff --git a/apps/webapp/app/components/Feedback.tsx b/apps/webapp/app/components/Feedback.tsx index ea67f783a..63d4f801b 100644 --- a/apps/webapp/app/components/Feedback.tsx +++ b/apps/webapp/app/components/Feedback.tsx @@ -114,11 +114,11 @@ function DiscordBanner() {
-

+

Join the Trigger.dev
Discord community @@ -127,7 +127,7 @@ function DiscordBanner() { Get help or answer questions from the Trigger.dev community.

- +
); } diff --git a/apps/webapp/app/components/ListPagination.tsx b/apps/webapp/app/components/ListPagination.tsx index 05ed9ceda..c62b74972 100644 --- a/apps/webapp/app/components/ListPagination.tsx +++ b/apps/webapp/app/components/ListPagination.tsx @@ -25,11 +25,13 @@ function NextButton({ cursor }: { cursor?: string }) { return ( !path && e.preventDefault()} > @@ -44,11 +46,13 @@ function PreviousButton({ cursor }: { cursor?: string }) { return ( !path && e.preventDefault()} > diff --git a/apps/webapp/app/components/LoginPageLayout.tsx b/apps/webapp/app/components/LoginPageLayout.tsx index 70917e6c5..877b6e362 100644 --- a/apps/webapp/app/components/LoginPageLayout.tsx +++ b/apps/webapp/app/components/LoginPageLayout.tsx @@ -43,7 +43,7 @@ const quotes: QuoteType[] = [ ]; const layout = "group grid place-items-center text-center overflow-hidden"; -const gridCell = "hover:bg-midnight-850 rounded-lg transition bg-midnight-850/40"; +const gridCell = "hover:bg-background-bright rounded-lg transition bg-background-bright/40"; const opacity = "opacity-10 group-hover:opacity-100 transition group-hover:scale-105"; const logos = "h-[60%] w-[60%] transition grayscale group-hover:grayscale-0"; const features = "h-[60%] w-[60%] text-gray-500 grayscale transition group-hover:grayscale-0"; @@ -61,7 +61,7 @@ export function LoginPageLayout({ children }: { children: React.ReactNode }) { return (
-
+
@@ -106,12 +106,12 @@ export function LoginPageLayout({ children }: { children: React.ReactNode }) { }>
- + {randomQuote?.quote} {randomQuote?.person} diff --git a/apps/webapp/app/components/NoMobileOverlay.tsx b/apps/webapp/app/components/NoMobileOverlay.tsx index 2e6de051b..3a22850ee 100644 --- a/apps/webapp/app/components/NoMobileOverlay.tsx +++ b/apps/webapp/app/components/NoMobileOverlay.tsx @@ -7,9 +7,9 @@ export function NoMobileOverlay() { <>
-
+
- + Trigger.dev is currently only available on desktop. Back Home diff --git a/apps/webapp/app/components/ProductHuntBanner.tsx b/apps/webapp/app/components/ProductHuntBanner.tsx index 39642da98..b85893836 100644 --- a/apps/webapp/app/components/ProductHuntBanner.tsx +++ b/apps/webapp/app/components/ProductHuntBanner.tsx @@ -14,7 +14,7 @@ export function ProductHuntBanner() { Vote for us today only! diff --git a/apps/webapp/app/components/UserProfilePhoto.tsx b/apps/webapp/app/components/UserProfilePhoto.tsx index a59e0a653..5903353d1 100644 --- a/apps/webapp/app/components/UserProfilePhoto.tsx +++ b/apps/webapp/app/components/UserProfilePhoto.tsx @@ -25,6 +25,6 @@ export function UserAvatar({ />
) : ( - + ); } diff --git a/apps/webapp/app/components/VersionLabel.tsx b/apps/webapp/app/components/VersionLabel.tsx index 9c44e765c..e2470454a 100644 --- a/apps/webapp/app/components/VersionLabel.tsx +++ b/apps/webapp/app/components/VersionLabel.tsx @@ -14,13 +14,13 @@ export function VersionLabel({ environment, userName, version }: VersionLabelPro return (
-
v{version}
+
v{version}
diff --git a/apps/webapp/app/components/billing/FreePlanUsage.tsx b/apps/webapp/app/components/billing/FreePlanUsage.tsx index 95c260449..f652fee75 100644 --- a/apps/webapp/app/components/billing/FreePlanUsage.tsx +++ b/apps/webapp/app/components/billing/FreePlanUsage.tsx @@ -18,14 +18,14 @@ export function FreePlanUsage({ to, percentage }: { to: string; percentage: numb return (
- - Free Plan + + Free Plan
Learn more diff --git a/apps/webapp/app/components/billing/PricingCalculator.tsx b/apps/webapp/app/components/billing/PricingCalculator.tsx index 35379116a..daca65a2e 100644 --- a/apps/webapp/app/components/billing/PricingCalculator.tsx +++ b/apps/webapp/app/components/billing/PricingCalculator.tsx @@ -84,18 +84,18 @@ function ConcurrentRunsSlider({ max={options.length - 1} step={1} > - - + +
{options.map((tier, i) => { return ( - + {tier.upto} ); @@ -103,13 +103,13 @@ function ConcurrentRunsSlider({
- = - + = + {formatCurrency(cost, true)}
-
+
); } @@ -157,11 +157,11 @@ function RunsSlider({ max={runIncrements} step={1} > - - + + @@ -185,13 +185,13 @@ function RunsSlider({
- = - + = + {formatCurrency(cost, true)}
-
+
); } @@ -250,9 +250,7 @@ function SliderMarker({ : "justify-middle right-0" )} > - - {text} - + {text}
); diff --git a/apps/webapp/app/components/billing/PricingTiers.tsx b/apps/webapp/app/components/billing/PricingTiers.tsx index cdd08553a..30b9c2a00 100644 --- a/apps/webapp/app/components/billing/PricingTiers.tsx +++ b/apps/webapp/app/components/billing/PricingTiers.tsx @@ -144,7 +144,7 @@ export function TierFree({
{buttonPath ? ( ) : ( )}
+
{ + if (!onBlur) return; + onBlur(editor.current?.textContent ?? ""); + }} + />
); } diff --git a/apps/webapp/app/components/code/codeMirrorTheme.ts b/apps/webapp/app/components/code/codeMirrorTheme.ts index c74ff8de3..74d074b3c 100644 --- a/apps/webapp/app/components/code/codeMirrorTheme.ts +++ b/apps/webapp/app/components/code/codeMirrorTheme.ts @@ -1,8 +1,7 @@ -import { EditorView } from "@codemirror/view"; +import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; import type { Extension } from "@codemirror/state"; -import { HighlightStyle } from "@codemirror/language"; -import { tagHighlighter, tags } from "@lezer/highlight"; -import { syntaxHighlighting } from "@codemirror/language"; +import { EditorView } from "@codemirror/view"; +import { tags } from "@lezer/highlight"; export function darkTheme(): Extension { const chalky = "#e5c07b", @@ -21,11 +20,11 @@ export function darkTheme(): Extension { tooltipBackground = "#353a42", selection = "rgb(71 85 105)", cursor = "#528bff", - scrollbarTrack = "#0E1521", + scrollbarTrack = "rgba(0,0,0,0)", scrollbarTrackActive = "#131B2B", scrollbarThumb = "#293649", scrollbarThumbActive = "#3C4B62", - scrollbarBg = "#0E1521"; + scrollbarBg = "rgba(0,0,0,0)"; const jsonHeroEditorTheme = EditorView.theme( { @@ -36,7 +35,7 @@ export function darkTheme(): Extension { ".cm-content": { caretColor: cursor, - fontFamily: "monospace", + fontFamily: "Geist Mono Variable", fontSize: "14px", }, diff --git a/apps/webapp/app/components/environments/EndpointIndexStatus.tsx b/apps/webapp/app/components/environments/EndpointIndexStatus.tsx index 67398b0b7..41c87db47 100644 --- a/apps/webapp/app/components/environments/EndpointIndexStatus.tsx +++ b/apps/webapp/app/components/environments/EndpointIndexStatus.tsx @@ -63,7 +63,7 @@ export function endpointIndexStatusTitle(status: EndpointIndexStatus): string { export function endpointIndexStatusClassNameColor(status: EndpointIndexStatus): string { switch (status) { case "PENDING": - return "text-dimmed"; + return "text-text-dimmed"; case "STARTED": return "text-blue-500"; case "SUCCESS": diff --git a/apps/webapp/app/components/environments/EnvironmentLabel.tsx b/apps/webapp/app/components/environments/EnvironmentLabel.tsx index 4df18297b..cc3852202 100644 --- a/apps/webapp/app/components/environments/EnvironmentLabel.tsx +++ b/apps/webapp/app/components/environments/EnvironmentLabel.tsx @@ -2,21 +2,29 @@ import { RuntimeEnvironment } from "~/models/runtimeEnvironment.server"; import { cn } from "~/utils/cn"; type Environment = Pick; +const variants = { + small: "h-4 text-xxs px-[0.1875rem] rounded-[2px]", + large: "h-6 text-xs px-1.5 rounded", +}; export function EnvironmentLabel({ environment, + size = "small", userName, className, }: { environment: Environment; + size?: keyof typeof variants; userName?: string; className?: string; }) { return ( @@ -54,13 +62,13 @@ export function environmentColorClassName(environment: Environment) { export function environmentBorderClassName(environment: Environment) { switch (environment.type) { case "PRODUCTION": - return "border-green-500"; + return "border-green-500/50"; case "STAGING": - return "border-amber-500"; + return "border-amber-500/50"; case "DEVELOPMENT": - return "border-pink-500"; + return "border-pink-500/50"; case "PREVIEW": - return "border-yellow-500"; + return "border-yellow-500/50"; } } diff --git a/apps/webapp/app/components/environments/RegenerateApiKeyModal.tsx b/apps/webapp/app/components/environments/RegenerateApiKeyModal.tsx index 86caba799..08d266770 100644 --- a/apps/webapp/app/components/environments/RegenerateApiKeyModal.tsx +++ b/apps/webapp/app/components/environments/RegenerateApiKeyModal.tsx @@ -1,4 +1,4 @@ -import { ArrowPathIcon } from "@heroicons/react/20/solid"; +import { ArrowPathIcon, ArrowRightIcon } from "@heroicons/react/20/solid"; import { ExclamationTriangleIcon } from "@heroicons/react/24/solid"; import { useFetcher } from "@remix-run/react"; import { useState } from "react"; @@ -9,6 +9,10 @@ import { Header1 } from "../primitives/Headers"; import { Input } from "../primitives/Input"; import { Paragraph } from "../primitives/Paragraph"; import { Spinner } from "../primitives/Spinner"; +import { Callout } from "../primitives/Callout"; +import { Fieldset } from "../primitives/Fieldset"; +import { InputGroup } from "../primitives/InputGroup"; +import { FormButtons } from "../primitives/FormButtons"; type ModalProps = { id: string; @@ -27,8 +31,8 @@ export function RegenerateApiKeyModal({ id, title }: ModalProps) { + } + cancelButton={} /> - -
+
); diff --git a/apps/webapp/app/components/events/EventsFilters.tsx b/apps/webapp/app/components/events/EventsFilters.tsx index f2bf2ff12..cdbeb7ee0 100644 --- a/apps/webapp/app/components/events/EventsFilters.tsx +++ b/apps/webapp/app/components/events/EventsFilters.tsx @@ -96,7 +96,7 @@ export function EventsFilters() { -
diff --git a/apps/webapp/app/components/events/EventsTable.tsx b/apps/webapp/app/components/events/EventsTable.tsx index ad62c7f5d..c80de1d96 100644 --- a/apps/webapp/app/components/events/EventsTable.tsx +++ b/apps/webapp/app/components/events/EventsTable.tsx @@ -101,9 +101,9 @@ export function EventsTable({ {event.isTest ? ( - + ) : ( - + )} {event.runs} @@ -115,9 +115,9 @@ export function EventsTable({ {isLoading && ( - Loading… + Loading… )} diff --git a/apps/webapp/app/components/frameworks/FrameworkComingSoon.tsx b/apps/webapp/app/components/frameworks/FrameworkComingSoon.tsx index da30882e4..5dbc99fd6 100644 --- a/apps/webapp/app/components/frameworks/FrameworkComingSoon.tsx +++ b/apps/webapp/app/components/frameworks/FrameworkComingSoon.tsx @@ -36,18 +36,18 @@ export function FrameworkComingSoon({ Choose a different framework
- + We're working hard to bring support for {frameworkName} in Trigger.dev. Follow along with the GitHub issue or contribute and help us bring it to Trigger.dev faster. triggerdotdev/trigger.dev

- #{githubIssueNumber}Framework: + #{githubIssueNumber}Framework: support for {frameworkName}

@@ -55,7 +55,7 @@ export function FrameworkComingSoon({ View on GitHub
diff --git a/apps/webapp/app/components/frameworks/FrameworkSelector.tsx b/apps/webapp/app/components/frameworks/FrameworkSelector.tsx index 993a0bc64..b81c567e8 100644 --- a/apps/webapp/app/components/frameworks/FrameworkSelector.tsx +++ b/apps/webapp/app/components/frameworks/FrameworkSelector.tsx @@ -89,7 +89,7 @@ function FrameworkLink({ children, to, supported = false }: FrameworkLinkProps) diff --git a/apps/webapp/app/components/helpContent/HelpContentText.tsx b/apps/webapp/app/components/helpContent/HelpContentText.tsx index 78d4807f5..e16e45969 100644 --- a/apps/webapp/app/components/helpContent/HelpContentText.tsx +++ b/apps/webapp/app/components/helpContent/HelpContentText.tsx @@ -21,6 +21,7 @@ import selectExample from "./select-example.png"; import { StepContentContainer } from "../StepContentContainer"; import { TriggerDevCommand } from "../SetupCommands"; import { IntegrationIcon } from "~/assets/icons/IntegrationIcon"; +import { BookOpenIcon } from "@heroicons/react/20/solid"; export function HowToRunYourJob() { const organization = useOrganization(); @@ -41,7 +42,7 @@ export function HowToRunYourJob() { to={jobTestPath(organization, project, job)} variant={"primary/small"} LeadingIcon={"beaker"} - leadingIconClassName="text-bright" + leadingIconClassName="text-text-bright" > Test @@ -55,9 +56,9 @@ export function HowToRunYourJob() { How to run a Job diff --git a/apps/webapp/app/components/integrations/ConnectToIntegrationSheet.tsx b/apps/webapp/app/components/integrations/ConnectToIntegrationSheet.tsx index d5a121342..37c80df5e 100644 --- a/apps/webapp/app/components/integrations/ConnectToIntegrationSheet.tsx +++ b/apps/webapp/app/components/integrations/ConnectToIntegrationSheet.tsx @@ -50,16 +50,16 @@ export function ConnectToIntegrationSheet({
View examples diff --git a/apps/webapp/app/components/integrations/ConnectToOAuthForm.tsx b/apps/webapp/app/components/integrations/ConnectToOAuthForm.tsx index 98ee5ffdc..a6757a6db 100644 --- a/apps/webapp/app/components/integrations/ConnectToOAuthForm.tsx +++ b/apps/webapp/app/components/integrations/ConnectToOAuthForm.tsx @@ -179,7 +179,7 @@ export function ConnectToOAuthForm({ */}
Select {integration.name} scopes - + {simplur`${selectedScopes.size} scope[|s] selected`}
@@ -232,7 +232,7 @@ export function ConnectToOAuthForm({ )} -
+
{scopes.error}
diff --git a/apps/webapp/app/components/integrations/CustomHelp.tsx b/apps/webapp/app/components/integrations/CustomHelp.tsx index 5b555eeb3..d47602371 100644 --- a/apps/webapp/app/components/integrations/CustomHelp.tsx +++ b/apps/webapp/app/components/integrations/CustomHelp.tsx @@ -41,14 +41,14 @@ export function CustomHelp({ api }: { api: Api }) { modified to suit your use-case. {api.examples.length > 1 && ( -
+
{api.examples?.map((example, index) => (
diff --git a/apps/webapp/app/components/integrations/SelectOAuthMethod.tsx b/apps/webapp/app/components/integrations/SelectOAuthMethod.tsx index 60ef7420d..127031e51 100644 --- a/apps/webapp/app/components/integrations/SelectOAuthMethod.tsx +++ b/apps/webapp/app/components/integrations/SelectOAuthMethod.tsx @@ -8,6 +8,7 @@ import { Paragraph } from "../primitives/Paragraph"; import { Client } from "~/presenters/IntegrationsPresenter.server"; import { UpdateOAuthForm } from "./UpdateOAuthForm"; import { LinkButton } from "../primitives/Buttons"; +import { BookOpenIcon } from "@heroicons/react/20/solid"; export function SelectOAuthMethod({ integration, @@ -114,8 +115,8 @@ export function SelectOAuthMethod({ We support external authentication providers through Auth Resolvers. Read the docs to learn more:{" "} */}
Select {integration.name} scopes - + {simplur`${selectedScopes.size} scope[|s] selected`}
@@ -221,7 +221,7 @@ export function UpdateOAuthForm({ )} -
+
{scopes.error}
diff --git a/apps/webapp/app/components/jobs/DeleteJobModalContent.tsx b/apps/webapp/app/components/jobs/DeleteJobModalContent.tsx index 47692ad15..c53a85088 100644 --- a/apps/webapp/app/components/jobs/DeleteJobModalContent.tsx +++ b/apps/webapp/app/components/jobs/DeleteJobModalContent.tsx @@ -74,7 +74,7 @@ export function DeleteJobDialogContent({ {canDelete @@ -84,8 +84,9 @@ export function DeleteJobDialogContent({ {canDelete ? ( <> - This will permanently delete the Job {title} - . This includes the deletion of all Run history. This cannot be undone. + This will permanently delete the Job{" "} + {title}. This includes the deletion of + all Run history. This cannot be undone. ) : ( <> @@ -111,7 +112,7 @@ export function DeleteJobDialogContent({ <> Delete this Job @@ -123,7 +124,7 @@ export function DeleteJobDialogContent({ <> Delete this Job diff --git a/apps/webapp/app/components/jobs/JobSkeleton.tsx b/apps/webapp/app/components/jobs/JobSkeleton.tsx index fd8b5c07c..3b9d48d03 100644 --- a/apps/webapp/app/components/jobs/JobSkeleton.tsx +++ b/apps/webapp/app/components/jobs/JobSkeleton.tsx @@ -1,17 +1,17 @@ export function JobSkeleton() { return ( -
-
+
+
-
+
-
+
-
+
diff --git a/apps/webapp/app/components/layout/AppLayout.tsx b/apps/webapp/app/components/layout/AppLayout.tsx index b9e72d1aa..73e7d7920 100644 --- a/apps/webapp/app/components/layout/AppLayout.tsx +++ b/apps/webapp/app/components/layout/AppLayout.tsx @@ -1,20 +1,28 @@ +import { useOptionalOrganization } from "~/hooks/useOrganizations"; import { cn } from "~/utils/cn"; +import { useShowUpgradePrompt } from "../billing/UpgradePrompt"; /** This container is used to surround the entire app, it correctly places the nav bar */ export function AppContainer({ children }: { children: React.ReactNode }) { return
{children}
; } +export function MainBody({ children }: { children: React.ReactNode }) { + return
{children}
; +} + /** This container should be placed around the content on a page */ -export function PageContainer({ - children, - className, -}: { - children: React.ReactNode; - className?: string; -}) { +export function PageContainer({ children }: { children: React.ReactNode }) { + const organization = useOptionalOrganization(); + const showUpgradePrompt = useShowUpgradePrompt(organization); + return ( -
+
{children}
); @@ -23,16 +31,19 @@ export function PageContainer({ export function PageBody({ children, scrollable = true, + className, }: { children: React.ReactNode; scrollable?: boolean; + className?: string; }) { return (
{children} @@ -40,10 +51,6 @@ export function PageBody({ ); } -export function PageBodyPadding({ children }: { children: React.ReactNode }) { - return
{children}
; -} - export function MainCenteredContainer({ children, className, @@ -52,7 +59,7 @@ export function MainCenteredContainer({ className?: string; }) { return ( -
+
{children}
); diff --git a/apps/webapp/app/components/navigation/AccountSideMenu.tsx b/apps/webapp/app/components/navigation/AccountSideMenu.tsx index 8decbf403..2f96629e2 100644 --- a/apps/webapp/app/components/navigation/AccountSideMenu.tsx +++ b/apps/webapp/app/components/navigation/AccountSideMenu.tsx @@ -16,15 +16,13 @@ export function AccountSideMenu({ user }: { user: User }) { return (
-
+
-
+
@@ -58,7 +56,7 @@ export function AccountSideMenu({ user }: { user: User }) {
)}
-
+
ReactNode; - -export function Breadcrumb() { - const matches = useMatches() as UIMatch[]; - - return ( -
- {matches - .filter((b) => b.handle && b.handle.breadcrumb) - .map((match, index) => { - const breadcrumb = match.handle.breadcrumb as BreadcrumbItem; - - return ( - - {index !== 0 && } {breadcrumb(match, matches)} - - ); - })} -
- ); -} - -export function BreadcrumbLink({ title, to }: { title: string; to: string }) { - return ( - - {title} - - ); -} diff --git a/apps/webapp/app/components/navigation/PageNavigationIndicator.tsx b/apps/webapp/app/components/navigation/PageNavigationIndicator.tsx deleted file mode 100644 index 7a84c3bcb..000000000 --- a/apps/webapp/app/components/navigation/PageNavigationIndicator.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useNavigation } from "@remix-run/react"; -import { Spinner } from "../primitives/Spinner"; -import { cn } from "~/utils/cn"; - -export function PageNavigationIndicator({ className }: { className?: string }) { - const navigation = useNavigation(); - if (navigation.state === "loading") { - return ; - } -} diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index 8d92fd0c2..e4ae2084c 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -110,21 +110,21 @@ export function SideMenu({ user, project, organization, organizations }: SideMen return (
@@ -182,7 +182,7 @@ export function SideMenu({ user, project, organization, organizations }: SideMen />
-
+
{currentPlan?.subscription?.isPaying === true && ( @@ -206,13 +206,13 @@ export function SideMenu({ user, project, organization, organizations }: SideMen support with the Trigger.dev team.
-
+
In your Slack app, create a new channel from the main menu by going to File{" "} - New Channel + New Channel @@ -312,7 +312,7 @@ function ProjectSelector({ {project.name ?? "Select a project"} @@ -328,7 +328,7 @@ function ProjectSelector({ key={p.id} to={projectPath(organization, p)} title={ -
+
{p.name}
@@ -348,7 +348,7 @@ function ProjectSelector({
))} -
+
@@ -367,16 +367,16 @@ function UserMenu({ user }: { user: SideMenuUser }) { return ( setProfileMenuOpen(open)}> - + diff --git a/apps/webapp/app/components/navigation/SideMenuHeader.tsx b/apps/webapp/app/components/navigation/SideMenuHeader.tsx index 299aa6b0e..8502ea0e3 100644 --- a/apps/webapp/app/components/navigation/SideMenuHeader.tsx +++ b/apps/webapp/app/components/navigation/SideMenuHeader.tsx @@ -16,17 +16,17 @@ export function SideMenuHeader({ title, children }: { title: string; children?:
{title} {children !== undefined ? ( setHeaderMenuOpen(open)} open={isHeaderMenuOpen}> - +
{children}
diff --git a/apps/webapp/app/components/navigation/SideMenuItem.tsx b/apps/webapp/app/components/navigation/SideMenuItem.tsx index 273017a06..41c0f2ee5 100644 --- a/apps/webapp/app/components/navigation/SideMenuItem.tsx +++ b/apps/webapp/app/components/navigation/SideMenuItem.tsx @@ -35,13 +35,13 @@ export function SideMenuItem({ fullWidth textAlignLeft LeadingIcon={icon} - leadingIconClassName={isActive ? iconColor : "text-dimmed"} + leadingIconClassName={isActive ? iconColor : "text-text-dimmed"} to={to} target={target} className={cn( - "text-bright group-hover:bg-slate-850", - subItem ? "text-dimmed" : "", - isActive ? "bg-slate-850 text-bright" : "group-hover:text-bright" + "text-text-bright group-hover:bg-charcoal-750", + subItem ? "text-text-dimmed" : "", + isActive ? "bg-tertiary text-text-bright" : "group-hover:text-text-bright" )} >
@@ -69,5 +69,7 @@ export function SideMenuItem({ } export function MenuCount({ count }: { count: number | string }) { - return
{count}
; + return ( +
{count}
+ ); } diff --git a/apps/webapp/app/components/primitives/Alert.tsx b/apps/webapp/app/components/primitives/Alert.tsx index 15b922fac..bd5a83ab9 100644 --- a/apps/webapp/app/components/primitives/Alert.tsx +++ b/apps/webapp/app/components/primitives/Alert.tsx @@ -27,7 +27,7 @@ const AlertOverlay = React.forwardRef< >(({ className, children, ...props }, ref) => ( (({ className, ...props }, ref) => ( )); diff --git a/apps/webapp/app/components/primitives/Badge.tsx b/apps/webapp/app/components/primitives/Badge.tsx index c45aad0e6..b8ecd826e 100644 --- a/apps/webapp/app/components/primitives/Badge.tsx +++ b/apps/webapp/app/components/primitives/Badge.tsx @@ -3,9 +3,14 @@ import { cn } from "~/utils/cn"; const variants = { default: - "grid place-items-center rounded-full px-2 h-5 tracking-wider text-xxs bg-slate-700 text-bright uppercase whitespace-nowrap", + "grid place-items-center rounded-full px-2 h-5 tracking-wider text-xxs bg-charcoal-750 text-text-bright uppercase whitespace-nowrap", + small: + "grid place-items-center rounded-full px-[0.4rem] h-4 tracking-wider text-xxs bg-background-dimmed text-text-dimmed uppercase whitespace-nowrap", outline: - "grid place-items-center rounded-sm px-1 h-5 tracking-wider text-xxs border border-dimmed text-dimmed uppercase whitespace-nowrap", + "grid place-items-center rounded-sm px-1.5 h-5 tracking-wider text-xxs border border-dimmed text-text-dimmed uppercase whitespace-nowrap", + v3: "grid place-items-center rounded-full px-[0.4rem] h-5 tracking-wider text-xxs bg-charcoal-750 text-primary whitespace-nowrap", + "outline-rounded": + "grid place-items-center rounded-full px-1.5 h-5 tracking-wider text-xxs border border-blue-500 text-blue-500 uppercase whitespace-nowrap", }; type BadgeProps = React.HTMLAttributes & { @@ -15,7 +20,7 @@ type BadgeProps = React.HTMLAttributes & { export function Badge({ className, variant = "default", children, ...props }: BadgeProps) { return (
- {children} + {children}
); } diff --git a/apps/webapp/app/components/primitives/BreadcrumbIcon.tsx b/apps/webapp/app/components/primitives/BreadcrumbIcon.tsx index c649a2480..b5dfb55ba 100644 --- a/apps/webapp/app/components/primitives/BreadcrumbIcon.tsx +++ b/apps/webapp/app/components/primitives/BreadcrumbIcon.tsx @@ -3,7 +3,7 @@ import { cn } from "~/utils/cn"; export function BreadcrumbIcon({ className }: { className?: string }) { return ( , - textColor: "text-bright", - linkClassName: "transition hover:bg-slate-750", + className: "border-charcoal-700 bg-charcoal-800", + icon: , + textColor: "text-text-bright", + linkClassName: "transition hover:bg-charcoal-750", }, warning: { - className: "border-yellow-400/20 bg-yellow-400/30", - icon: , + className: "border-warning/20 bg-warning/10", + icon: , textColor: "text-yellow-200", - linkClassName: "transition hover:bg-yellow-400/40", + linkClassName: "transition hover:bg-warning/20", }, error: { - className: "border-rose-500/20 bg-rose-500/30", - icon: , + className: "border-error/20 bg-error/10", + icon: , textColor: "text-rose-200", - linkClassName: "transition hover:bg-rose-500/40", + linkClassName: "transition hover:bg-error/20", }, idea: { - className: "border-green-400/20 bg-green-400/30", - icon: , + className: "border-success/20 bg-success/10", + icon: , textColor: "text-green-200", - linkClassName: "transition hover:bg-green-400/40", + linkClassName: "transition hover:bg-success/20", }, success: { - className: "border-green-400/20 bg-green-400/30", - icon: , + className: "border-success/20 bg-success/10", + icon: , textColor: "text-green-200", - linkClassName: "transition hover:bg-green-400/40", + linkClassName: "transition hover:bg-success/20", }, docs: { - className: "border-blue-400/20 bg-blue-400/30", + className: "border-blue-400/20 bg-blue-400/10", icon: , textColor: "text-blue-200", - linkClassName: "transition hover:bg-blue-400/40", + linkClassName: "transition hover:bg-blue-400/20", }, pending: { className: "border-blue-400/20 bg-blue-800/30", icon: , textColor: "text-blue-300", - linkClassName: "transition hover:bg-blue-400/40", + linkClassName: "transition hover:bg-blue-400/20", }, pricing: { - className: "border-indigo-400/20 bg-indigo-800/30", - icon: , - textColor: "text-indigo-200", - linkClassName: "transition hover:bg-indigo-400/40", + className: "border-charcoal-700 bg-charcoal-800", + icon: , + textColor: "text-text-bright", + linkClassName: "transition hover:bg-charcoal-750", }, } as const; @@ -93,13 +93,13 @@ export function Callout({ href={to} target="_blank" className={cn( - `flex w-full items-center justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`, + `flex w-full items-start justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`, variantDefinition.className, variantDefinition.linkClassName, className )} > -
+
{icon ? icon : variantDefinition.icon} {typeof children === "string" ? ( @@ -135,7 +135,9 @@ export function Callout({ children )}
- +
+ +
); } @@ -144,13 +146,13 @@ export function Callout({ return (
-
+
{icon ? icon : variantDefinition.icon} {typeof children === "string" ? ( diff --git a/apps/webapp/app/components/primitives/Checkbox.tsx b/apps/webapp/app/components/primitives/Checkbox.tsx index 9dae321d1..f706c2ac9 100644 --- a/apps/webapp/app/components/primitives/Checkbox.tsx +++ b/apps/webapp/app/components/primitives/Checkbox.tsx @@ -7,44 +7,44 @@ import { Paragraph } from "./Paragraph"; const variants = { "simple/small": { button: "w-fit pr-4", - label: "text-sm text-bright mt-0.5 select-none", - description: "text-dimmed", + label: "text-sm text-text-bright mt-0.5 select-none", + description: "text-text-dimmed", inputPosition: "mt-1", isChecked: "", isDisabled: "opacity-70", }, simple: { button: "w-fit pr-4", - label: "text-bright select-none", - description: "text-dimmed", + label: "text-text-bright select-none", + description: "text-text-dimmed", inputPosition: "mt-1", isChecked: "", isDisabled: "opacity-70", }, "button/small": { button: - "flex items-center w-fit h-8 pl-2 pr-3 rounded border border-slate-800 hover:bg-slate-850 hover:border-slate-750 transition", - label: "text-sm text-bright select-none", - description: "text-dimmed", + "flex items-center w-fit h-8 pl-2 pr-3 rounded border border-charcoal-800 hover:bg-charcoal-850 hover:border-charcoal-750 transition", + label: "text-sm text-text-bright select-none", + description: "text-text-dimmed", inputPosition: "mt-0", - isChecked: "bg-slate-850 border-slate-750 hover:!bg-slate-850", + isChecked: "bg-charcoal-850 border-charcoal-750 hover:!bg-charcoal-850", isDisabled: "opacity-70 hover:bg-transparent", }, button: { button: - "w-fit py-2 pl-3 pr-4 rounded border border-slate-800 hover:bg-slate-850 hover:border-slate-750 transition", - label: "text-bright select-none", - description: "text-dimmed", + "w-fit py-2 pl-3 pr-4 rounded border border-charcoal-800 hover:bg-charcoal-850 hover:border-charcoal-750 transition", + label: "text-text-bright select-none", + description: "text-text-dimmed", inputPosition: "mt-1", - isChecked: "bg-slate-850 border-slate-750 hover:!bg-slate-850", + isChecked: "bg-charcoal-850 border-charcoal-750 hover:!bg-charcoal-850", isDisabled: "opacity-70 hover:bg-transparent", }, description: { - button: "w-full py-2 pl-3 pr-4 checked:hover:bg-slate-850 transition", - label: "text-bright font-semibold", - description: "text-dimmed", + button: "w-full py-2 pl-3 pr-4 checked:hover:bg-charcoal-850 transition", + label: "text-text-bright font-semibold", + description: "text-text-dimmed", inputPosition: "mt-1", - isChecked: "bg-slate-850", + isChecked: "bg-charcoal-850", isDisabled: "opacity-70", }, }; @@ -137,7 +137,7 @@ export const Checkbox = React.forwardRef( className={cn( inputPositionClasses, props.readOnly || disabled ? "cursor-default" : "cursor-pointer", - "rounded-sm border border-slate-700 bg-transparent transition checked:!bg-indigo-500 read-only:border-slate-650 read-only:!bg-slate-700 group-hover:bg-slate-900 group-hover:checked:bg-indigo-500 group-focus:ring-1 focus:ring-indigo-500 focus:ring-offset-0 focus:ring-offset-transparent focus-visible:outline-none focus-visible:ring-indigo-500 disabled:border-slate-650 disabled:!bg-slate-700" + "read-only:border-charcoal-650 disabled:border-charcoal-650 rounded-sm border border-charcoal-700 bg-transparent transition checked:!bg-indigo-500 read-only:!bg-charcoal-700 group-hover:bg-charcoal-900 group-hover:checked:bg-indigo-500 group-focus:ring-1 focus:ring-indigo-500 focus:ring-offset-0 focus:ring-offset-transparent focus-visible:outline-none focus-visible:ring-indigo-500 disabled:!bg-charcoal-700" )} id={id} ref={ref} diff --git a/apps/webapp/app/components/primitives/ClientTabs.tsx b/apps/webapp/app/components/primitives/ClientTabs.tsx index daefcaa3b..c8ac014dd 100644 --- a/apps/webapp/app/components/primitives/ClientTabs.tsx +++ b/apps/webapp/app/components/primitives/ClientTabs.tsx @@ -26,7 +26,7 @@ const ClientTabsTrigger = React.forwardRef< {tabs.map((tab, index) => { const isActive = currentValue === tab.value; @@ -75,7 +75,7 @@ export function ClientTabsWithUnderline({ className, tabs, currentValue, layoutI {tab.label} @@ -83,7 +83,7 @@ export function ClientTabsWithUnderline({ className, tabs, currentValue, layoutI {isActive ? ( ) : ( -
+
)} ); diff --git a/apps/webapp/app/components/primitives/ClipboardField.tsx b/apps/webapp/app/components/primitives/ClipboardField.tsx index 6bae87ce4..d401e9c59 100644 --- a/apps/webapp/app/components/primitives/ClipboardField.tsx +++ b/apps/webapp/app/components/primitives/ClipboardField.tsx @@ -4,12 +4,12 @@ import { cn } from "~/utils/cn"; import { Button } from "./Buttons"; import { IconNames, NamedIcon } from "./NamedIcon"; -const variations = { +const variants = { "primary/small": { container: - "flex items-center text-dimmed font-mono rounded border border-slate-800 bg-slate-850 text-xs transition hover:border-slate-750 hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", + "flex items-center text-text-dimmed font-mono rounded border bg-charcoal-850 text-xs transition hover:bg-charcoal-700 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", input: - "bg-slate-850 border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent", + "bg-charcoal-750 border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent", buttonVariant: "primary/small" as const, button: "rounded-l-none min-w-[3.1rem]", iconSize: "h-3 w-3", @@ -17,29 +17,30 @@ const variations = { }, "secondary/small": { container: - "flex items-center text-dimmed font-mono rounded border border-slate-750 bg-slate-850 text-xs transition hover:border-slate-750 hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", + "flex items-center text-text-dimmed font-mono rounded border bg-transparent text-xs transition hover:bg-charcoal-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", input: - "bg-slate-850 border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent", - buttonVariant: "secondary/small" as const, - button: "rounded-l-none border-l border-slate-750 min-w-[3.1rem]", + "bg-charcoal-750 border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent", + buttonVariant: "tertiary/small" as const, + button: "rounded-l-none border-l border-charcoal-750 min-w-[3.1rem]", iconSize: "h-3 w-3", iconPadding: "pl-1", }, "tertiary/small": { container: - "flex items-center text-dimmed font-mono rounded border border-slate-850 bg-transparent text-xs transition hover:border-slate-800 hover:bg-slate-950 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", + "group flex items-center text-text-dimmed font-mono rounded bg-transparent text-xs transition hover:border-charcoal-700 hover:bg-tertiary focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", input: "bg-transparent border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent", - buttonVariant: "tertiary/small" as const, - button: "rounded-l-none border-l border-slate-850 min-w-[3.1rem]", + buttonVariant: "minimal/small" as const, + button: + "rounded-l-none border-l border-transparent group-hover:border-charcoal-700 min-w-[3.1rem]", iconSize: "h-3 w-3", iconPadding: "pl-1", }, "primary/medium": { container: - "flex items-center text-dimmed font-mono rounded border border-slate-800 bg-slate-850 text-sm transition hover:border-slate-750 hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", + "flex items-center text-text-dimmed font-mono rounded border border-charcoal-800 bg-charcoal-850 text-sm transition hover:border-charcoal-750 hover:bg-charcoal-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", input: - "bg-slate-850 border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent", + "bg-charcoal-850 border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent", buttonVariant: "primary/medium" as const, button: "rounded-l-none min-w-[4rem]", iconSize: "h-4 w-4", @@ -47,21 +48,21 @@ const variations = { }, "secondary/medium": { container: - "flex items-center text-dimmed font-mono rounded border border-slate-750 bg-slate-850 text-sm transition hover:border-slate-750 hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", + "flex items-center text-text-dimmed font-mono rounded border border-charcoal-750 bg-charcoal-850 text-sm transition hover:border-charcoal-750 hover:bg-charcoal-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent", input: - "bg-slate-850 border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent", + "bg-charcoal-850 border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent", buttonVariant: "secondary/medium" as const, - button: "rounded-l-none border-l border-slate-750 min-w-[4rem]", + button: "rounded-l-none border-l border-charcoal-750 min-w-[4rem]", iconSize: "h-4 w-4", iconPadding: "pl-2", }, "tertiary/medium": { container: - "flex items-center text-dimmed font-mono rounded border border-slate-850 bg-transparent text-sm transition hover:border-slate-800 hover:bg-slate-950 focus:border-4 focus:border-solid focus:border-l-transparent", + "flex items-center text-text-dimmed font-mono rounded border border-charcoal-850 bg-transparent text-sm transition hover:border-charcoal-800 hover:bg-charcoal-950 focus:border-4 focus:border-solid focus:border-l-transparent", input: "bg-transparent border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent", buttonVariant: "tertiary/medium" as const, - button: "rounded-l-none border-l border-slate-850 min-w-[4rem]", + button: "rounded-l-none border-l border-charcoal-850 min-w-[4rem]", iconSize: "h-4 w-4", iconPadding: "pl-2", }, @@ -70,7 +71,7 @@ const variations = { type ClipboardFieldProps = { value: string; secure?: boolean | string; - variant: keyof typeof variations; + variant: keyof typeof variants; className?: string; icon?: IconNames | React.ReactNode; fullWidth?: boolean; @@ -104,9 +105,9 @@ export function ClipboardField({ setIsSecure(secure !== undefined && secure); }, [secure]); - const { container, input, buttonVariant, button } = variations[variant]; - const iconClassName = variations[variant].iconSize; - const iconPosition = variations[variant].iconPadding; + const { container, input, buttonVariant, button } = variants[variant]; + const iconClassName = variants[variant].iconSize; + const iconPosition = variants[variant].iconPadding; const inputIcon = useRef(null); return ( diff --git a/apps/webapp/app/components/primitives/DateField.tsx b/apps/webapp/app/components/primitives/DateField.tsx index 1b149e42d..403aaa0d9 100644 --- a/apps/webapp/app/components/primitives/DateField.tsx +++ b/apps/webapp/app/components/primitives/DateField.tsx @@ -90,7 +90,7 @@ export function DateField({ return (
- + {label}
@@ -98,7 +98,7 @@ export function DateField({ {...fieldProps} ref={ref} className={cn( - "flex rounded-sm border border-slate-800 bg-midnight-900 p-0.5 px-1.5 transition-colors focus-within:border-slate-500 hover:border-slate-700 focus-within:hover:border-slate-500", + "flex rounded-sm border border-charcoal-800 bg-charcoal-750 p-0.5 px-1.5 transition-colors focus-within:border-charcoal-500 hover:border-charcoal-700 focus-within:hover:border-charcoal-500", fieldClassName )} > @@ -118,7 +118,7 @@ export function DateField({
{showNowButton && (
diff --git a/apps/webapp/app/components/primitives/DetailCell.tsx b/apps/webapp/app/components/primitives/DetailCell.tsx index 6047cc150..0be8597c8 100644 --- a/apps/webapp/app/components/primitives/DetailCell.tsx +++ b/apps/webapp/app/components/primitives/DetailCell.tsx @@ -51,19 +51,19 @@ export function DetailCell({ return (
@@ -73,7 +73,7 @@ export function DetailCell({ @@ -85,7 +85,7 @@ export function DetailCell({ diff --git a/apps/webapp/app/components/primitives/Dialog.tsx b/apps/webapp/app/components/primitives/Dialog.tsx index 3a382112f..7f9562a35 100644 --- a/apps/webapp/app/components/primitives/Dialog.tsx +++ b/apps/webapp/app/components/primitives/Dialog.tsx @@ -26,7 +26,7 @@ const DialogOverlay = React.forwardRef<
{children} - +
) => ( -
+
); DialogHeader.displayName = "DialogHeader"; @@ -103,7 +106,7 @@ const DialogDescription = React.forwardRef< >(({ className, ...props }, ref) => ( )); diff --git a/apps/webapp/app/components/primitives/FormTitle.tsx b/apps/webapp/app/components/primitives/FormTitle.tsx index 98b8c635b..cc2f698ca 100644 --- a/apps/webapp/app/components/primitives/FormTitle.tsx +++ b/apps/webapp/app/components/primitives/FormTitle.tsx @@ -21,7 +21,7 @@ export function FormTitle({
diff --git a/apps/webapp/app/components/primitives/Headers.tsx b/apps/webapp/app/components/primitives/Headers.tsx index 7d9c9ef21..809c24f65 100644 --- a/apps/webapp/app/components/primitives/Headers.tsx +++ b/apps/webapp/app/components/primitives/Headers.tsx @@ -16,8 +16,8 @@ const headerVariants = { }; const textColorVariants = { - bright: "text-bright", - dimmed: "text-dimmed", + bright: "text-text-bright", + dimmed: "text-text-dimmed", }; export type HeaderVariant = keyof typeof headerVariants; diff --git a/apps/webapp/app/components/primitives/Help.tsx b/apps/webapp/app/components/primitives/Help.tsx index 816b2e3f7..9f9ad1273 100644 --- a/apps/webapp/app/components/primitives/Help.tsx +++ b/apps/webapp/app/components/primitives/Help.tsx @@ -46,7 +46,7 @@ export function HelpTrigger({ title }: { title: string }) { <> ) : (
{allowDismissing && (
-
+
{children}
diff --git a/apps/webapp/app/components/primitives/Icon.tsx b/apps/webapp/app/components/primitives/Icon.tsx index a2907054b..00200a188 100644 --- a/apps/webapp/app/components/primitives/Icon.tsx +++ b/apps/webapp/app/components/primitives/Icon.tsx @@ -3,7 +3,10 @@ import { IconNamesOrString, NamedIcon } from "./NamedIcon"; import { cn } from "~/utils/cn"; import { render } from "react-dom"; -export type RenderIcon = IconNamesOrString | FunctionComponent<{className?: string}> | React.ReactNode; +export type RenderIcon = + | IconNamesOrString + | FunctionComponent<{ className?: string }> + | React.ReactNode; type IconProps = { icon?: RenderIcon; @@ -17,7 +20,7 @@ export function Icon(props: IconProps) { if (typeof props.icon === "string") { return } />; } - + if (typeof props.icon === "function") { const Icon = props.icon; return ; @@ -26,9 +29,16 @@ export function Icon(props: IconProps) { if (React.isValidElement(props.icon)) { return <>{props.icon}; } - - if (props.icon && typeof props.icon === 'object' && ('type' in props.icon || '$$typeof' in props.icon)) { - return createElement>(props.icon as any, { className: props.className } as any); + + if ( + props.icon && + typeof props.icon === "object" && + ("type" in props.icon || "$$typeof" in props.icon) + ) { + return createElement>( + props.icon as any, + { className: props.className } as any + ); } console.error("Invalid icon", props); @@ -39,7 +49,7 @@ export function IconInBox({ boxClassName, ...props }: IconProps & { boxClassName return (
diff --git a/apps/webapp/app/components/primitives/Input.tsx b/apps/webapp/app/components/primitives/Input.tsx index cc14bb5dd..9a0604670 100644 --- a/apps/webapp/app/components/primitives/Input.tsx +++ b/apps/webapp/app/components/primitives/Input.tsx @@ -3,45 +3,43 @@ import { useImperativeHandle, useRef } from "react"; import { cn } from "~/utils/cn"; import { Icon, RenderIcon } from "./Icon"; -const containerBase = "has-[:focus-visible]:outline-none has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-0 has-[:focus]:border-ring has-[:focus]:outline-none has-[:focus]:ring-2 has-[:focus]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ring-offset-background transition cursor-text" +const containerBase = + "has-[:focus-visible]:outline-none has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-0 has-[:focus]:border-ring has-[:focus]:outline-none has-[:focus]:ring-2 has-[:focus]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ring-offset-background transition cursor-text"; -const inputBase = "h-full w-full text-bright bg-transparent file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 disabled:cursor-not-allowed outline-none ring-0 border-none" +const inputBase = + "h-full w-full text-text-bright bg-transparent file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 disabled:cursor-not-allowed outline-none ring-0 border-none"; -const shortcutBase = "grid h-fit place-content-center border border-dimmed/40 font-normal text-dimmed" +const shortcutBase = + "grid h-fit place-content-center border border-dimmed/40 font-normal text-text-dimmed"; const variants = { large: { container: - "px-1 w-full h-10 rounded-[3px] border border-slate-800 bg-slate-850 hover:border-slate-750 hover:bg-slate-800", - input: - "px-2 text-sm", + "px-1 w-full h-10 rounded-[3px] border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-750 hover:bg-charcoal-650", + input: "px-2 text-sm", iconSize: "h-4 w-4 ml-1", - shortcut: - "mr-1 min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem] select-none", + shortcut: "mr-1 min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem] select-none", }, medium: { - container: "px-1 h-8 w-full rounded border border-slate-800 bg-slate-850 hover:border-slate-750 hover:bg-slate-800", - input: - "px-1.5 rounded text-sm", + container: + "px-1 h-8 w-full rounded border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-750 hover:bg-charcoal-650", + input: "px-1.5 rounded text-sm", iconSize: "h-4 w-4 ml-0.5", - shortcut: - "min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem]", + shortcut: "min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem]", }, small: { - container: "px-0.5 h-6 w-full rounded border border-slate-800 bg-slate-850 hover:border-slate-750 hover:bg-slate-800", - input: - "px-1 rounded text-xs", + container: + "px-0.5 h-6 w-full rounded border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-750 hover:bg-charcoal-650", + input: "px-1 rounded text-xs", iconSize: "h-3 w-3 ml-0.5", - shortcut: - "min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]", + shortcut: "min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]", }, tertiary: { - container: "px-0.5 h-6 w-full rounded border border-transparent hover:border-slate-800 hover:bg-slate-850", - input: - "px-1 rounded text-xs", + container: + "px-0.5 h-6 w-full rounded border border-transparent hover:border-charcoal-800 hover:bg-charcoal-750", + input: "px-1 rounded text-xs", iconSize: "h-3 w-3 ml-0.5", - shortcut: - "min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]" + shortcut: "min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]", }, }; @@ -64,14 +62,17 @@ const Input = React.forwardRef( return (
innerRef.current && innerRef.current.focus()} > {icon && ( -
- +
+
)} {value} - + } diff --git a/apps/webapp/app/components/primitives/LoadingBarDivider.tsx b/apps/webapp/app/components/primitives/LoadingBarDivider.tsx new file mode 100644 index 000000000..f227f9e73 --- /dev/null +++ b/apps/webapp/app/components/primitives/LoadingBarDivider.tsx @@ -0,0 +1,52 @@ +import { AnimatePresence, useAnimate, usePresence } from "framer-motion"; +import { useEffect } from "react"; + +type LoadingBarDividerProps = { + isLoading: boolean; +}; + +export function LoadingBarDivider({ isLoading }: LoadingBarDividerProps) { + return ( +
+ +
+ ); +} + +export function AnimationDivider({ isLoading }: LoadingBarDividerProps) { + const [scope, animate] = useAnimate(); + const [isPresent, safeToRemove] = usePresence(); + + useEffect(() => { + if (isPresent) { + const enterAnimation = async () => { + await animate( + [ + [scope.current, { width: "30%", left: "35%" }, { duration: 1, ease: "easeIn" }], + [scope.current, { width: 0, left: "100%" }, { duration: 1, ease: "easeOut" }], + ], + { repeat: Infinity, repeatType: "reverse" } + ); + }; + enterAnimation(); + } else { + const exitAnimation = async () => { + await animate(scope.current, { opacity: 0 }); + safeToRemove(); + }; + + exitAnimation(); + } + }, [isPresent, isLoading]); + + return ( + + {isLoading && ( +
+ )} + + ); +} diff --git a/apps/webapp/app/components/primitives/NamedIcon.tsx b/apps/webapp/app/components/primitives/NamedIcon.tsx index d776a1d88..217ee73af 100644 --- a/apps/webapp/app/components/primitives/NamedIcon.tsx +++ b/apps/webapp/app/components/primitives/NamedIcon.tsx @@ -74,7 +74,7 @@ import { Spinner } from "./Spinner"; import tablerSpritePath from "./tabler-sprite.svg"; const icons = { - account: (className: string) => , + account: (className: string) => , active: (className: string) => , "arrow-right": (className: string) => , "arrow-left": (className: string) => , @@ -82,32 +82,34 @@ const icons = { beaker: (className: string) => , bell: (className: string) => , billing: (className: string) => , - browser: (className: string) => , + browser: (className: string) => , calendar: (className: string) => ( ), - check: (className: string) => , + check: (className: string) => , "chevron-down": (className: string) => ( - + + ), + "chevron-up": (className: string) => ( + ), - "chevron-up": (className: string) => , "chevron-left": (className: string) => ( - + ), "chevron-right": (className: string) => ( - + ), countdown: (className: string) => , clock: (className: string) => , - close: (className: string) => , + close: (className: string) => , "connection-alert": (className: string) => ( ), - docs: (className: string) => , + docs: (className: string) => , dynamic: (className: string) => , error: (className: string) => , "external-link": (className: string) => ( - + ), flag: (className: string) => , folder: (className: string) => , @@ -119,7 +121,7 @@ const icons = { ), heart: (className: string) => , - house: (className: string) => , + house: (className: string) => , id: (className: string) => , inactive: (className: string) => , info: (className: string) => , @@ -135,11 +137,11 @@ const icons = { key: (className: string) => , lightbulb: (className: string) => , "clipboard-checked": (className: string) => ( - + ), - list: (className: string) => , + list: (className: string) => , log: (className: string) => ( - + ), "logo-icon": (className: string) => , organization: (className: string) => ( @@ -149,16 +151,20 @@ const icons = { "plus-small": (className: string) => ( ), - property: (className: string) => , + property: (className: string) => , pulse: (className: string) => , "qr-code": (className: string) => , - refresh: (className: string) => , + refresh: (className: string) => , sapling: (className: string) => , - search: (className: string) => , - settings: (className: string) => , + search: (className: string) => ( + + ), + settings: (className: string) => , spinner: (className: string) => , "spinner-white": (className: string) => , - squares: (className: string) => , + squares: (className: string) => ( + + ), star: (className: string) => , stop: (className: string) => , team: (className: string) => , @@ -258,7 +264,7 @@ export function NamedIconInBox({ return (
diff --git a/apps/webapp/app/components/primitives/PageHeader.tsx b/apps/webapp/app/components/primitives/PageHeader.tsx index ee72b45fb..72c2314e5 100644 --- a/apps/webapp/app/components/primitives/PageHeader.tsx +++ b/apps/webapp/app/components/primitives/PageHeader.tsx @@ -1,33 +1,46 @@ -import { Link } from "@remix-run/react"; -import { Header1 } from "./Headers"; -import { BreadcrumbIcon } from "./BreadcrumbIcon"; -import { ChevronLeftIcon } from "@heroicons/react/24/solid"; -import { Paragraph } from "./Paragraph"; -import { cn } from "~/utils/cn"; -import { NamedIcon } from "./NamedIcon"; -import { Tabs, TabsProps } from "./Tabs"; -import { Icon, RenderIcon } from "./Icon"; -import { Button, LinkButton } from "./Buttons"; import { ArrowUpRightIcon } from "@heroicons/react/20/solid"; +import { ChevronLeftIcon } from "@heroicons/react/24/solid"; +import { Link, useNavigation } from "@remix-run/react"; +import { useOptionalOrganization } from "~/hooks/useOrganizations"; +import { cn } from "~/utils/cn"; +import { plansPath } from "~/utils/pathBuilder"; +import { UpgradePrompt, useShowUpgradePrompt } from "../billing/UpgradePrompt"; +import { BreadcrumbIcon } from "./BreadcrumbIcon"; +import { LinkButton } from "./Buttons"; +import { Header2, Header3 } from "./Headers"; +import { NamedIcon } from "./NamedIcon"; +import { Paragraph } from "./Paragraph"; +import { Tabs, TabsProps } from "./Tabs"; +import { LoadingBarDivider } from "./LoadingBarDivider"; type WithChildren = { children: React.ReactNode; }; -export function PageHeader({ children, hideBorder }: WithChildren & { hideBorder?: boolean }) { +export function NavBar({ children }: WithChildren) { + const organization = useOptionalOrganization(); + const showUpgradePrompt = useShowUpgradePrompt(organization); + const navigation = useNavigation(); + const isLoading = navigation.state === "loading" || navigation.state === "submitting"; + return ( -
- {children} +
+
+
{children}
+ +
+ {showUpgradePrompt.shouldShow && organization && ( + + )}
); } -export function PageTitleRow({ children }: WithChildren) { - return
{children}
; -} - type PageTitleProps = { - icon?: RenderIcon; title: string; backButton?: { to: string; @@ -35,45 +48,31 @@ type PageTitleProps = { }; }; -export function PageTitle({ icon, title, backButton }: PageTitleProps) { +export function PageTitle({ title, backButton }: PageTitleProps) { return (
{backButton && (
- - - {backButton.text} - + {backButton.text} - +
)} - - {icon && } - {title} - + {title}
); } -export function PageButtons({ children }: WithChildren) { +export function PageAccessories({ children }: WithChildren) { return
{children}
; } -export function PageDescription({ children }: WithChildren) { - return ( - - {children} - - ); -} - export function PageInfoRow({ children }: WithChildren) { - return
{children}
; + return
{children}
; } export function PageInfoGroup({ @@ -139,7 +138,7 @@ function PageInfoPropertyContent({ export function PageTabs(props: TabsProps) { return ( -
+
); diff --git a/apps/webapp/app/components/primitives/Pagination.tsx b/apps/webapp/app/components/primitives/Pagination.tsx index 5367c0229..c859452f9 100644 --- a/apps/webapp/app/components/primitives/Pagination.tsx +++ b/apps/webapp/app/components/primitives/Pagination.tsx @@ -21,12 +21,12 @@ export function PaginationControls({ } return ( -
+
{currentPage > 1 && ( Previous @@ -34,7 +34,7 @@ export function PaginationControls({ {currentPage < totalPages && ( Next @@ -42,7 +42,7 @@ export function PaginationControls({
-

+

Showing {(currentPage - 1) * pageSize + 1} to{" "} {currentPage * pageSize} of{" "} {totalResults} results @@ -56,8 +56,8 @@ export function PaginationControls({ Previous

@@ -129,7 +129,7 @@ export const RadioGroupItem = React.forwardRef< >
diff --git a/apps/webapp/app/components/primitives/Resizable.tsx b/apps/webapp/app/components/primitives/Resizable.tsx index d95ec6b42..776b210f3 100644 --- a/apps/webapp/app/components/primitives/Resizable.tsx +++ b/apps/webapp/app/components/primitives/Resizable.tsx @@ -8,7 +8,7 @@ const ResizablePanelGroup = ({ ...props }: React.ComponentProps) => ( ); @@ -24,16 +24,16 @@ const ResizableHandle = ({ }) => ( div]:rotate-90", + "focus-visible:ring-ring group relative flex w-0.75 items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 hover:w-0.75 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-1 [&[data-panel-group-direction=vertical]>div]:rotate-90", className )} {...props} > -
+
{withHandle && ( -
+
{Array.from({ length: 3 }).map((_, index) => ( -
+
))}
)} diff --git a/apps/webapp/app/components/primitives/SegmentedControl.tsx b/apps/webapp/app/components/primitives/SegmentedControl.tsx index b01d9eb9f..5576ad2cf 100644 --- a/apps/webapp/app/components/primitives/SegmentedControl.tsx +++ b/apps/webapp/app/components/primitives/SegmentedControl.tsx @@ -25,7 +25,7 @@ export default function SegmentedControl({ onChange, }: SegmentedControlProps) { return ( -
+
@@ -64,7 +64,7 @@ export default function SegmentedControl({ )}
diff --git a/apps/webapp/app/components/primitives/Select.tsx b/apps/webapp/app/components/primitives/Select.tsx index 3d3b32af2..3610790b7 100644 --- a/apps/webapp/app/components/primitives/Select.tsx +++ b/apps/webapp/app/components/primitives/Select.tsx @@ -6,9 +6,10 @@ import { Check, ChevronDown } from "lucide-react"; import { cn } from "~/utils/cn"; const sizes = { - "secondary/small": "text-xs h-6 bg-tertiary hover:bg-tertiary-foreground pr-2 pl-1 bg-slate-800", + "secondary/small": + "text-xs h-6 bg-tertiary hover:bg-tertiary-foreground pr-2 pl-1 bg-charcoal-800", medium: - "text-sm h-8 bg-slate-850 border border-slate-800 hover:bg-slate-800 hover:border-slate-750 px-2.5", + "text-sm h-8 bg-charcoal-850 border border-charcoal-800 hover:bg-charcoal-800 hover:border-charcoal-750 px-2.5", }; export type SelectProps = { @@ -28,7 +29,7 @@ const SelectTrigger = React.forwardRef< @@ -56,7 +57,7 @@ const SelectContent = React.forwardRef< (({ className, ...props }, ref) => ( )); diff --git a/apps/webapp/app/components/primitives/Sheet.tsx b/apps/webapp/app/components/primitives/Sheet.tsx index 844c29ca1..dd9d1895d 100644 --- a/apps/webapp/app/components/primitives/Sheet.tsx +++ b/apps/webapp/app/components/primitives/Sheet.tsx @@ -41,7 +41,7 @@ const SheetOverlay = React.forwardRef< >(({ className, children, ...props }, ref) => (
-
- +
+ Close @@ -171,7 +171,7 @@ SheetContent.displayName = SheetPrimitive.Content.displayName; export const SheetBody = ({ className, ...props }: React.HTMLAttributes) => (
) => (
) => (
-
{children}
+
{children}
); diff --git a/apps/webapp/app/components/primitives/ShortcutKey.tsx b/apps/webapp/app/components/primitives/ShortcutKey.tsx index 8be993c12..1bf0e3cbc 100644 --- a/apps/webapp/app/components/primitives/ShortcutKey.tsx +++ b/apps/webapp/app/components/primitives/ShortcutKey.tsx @@ -5,9 +5,9 @@ import { useOperatingSystem } from "./OperatingSystemProvider"; const variants = { small: - "text-[0.6rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-dimmed group-hover:text-bright/80 group-hover:border-dimmed/60 transition uppercase", + "text-[0.6rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-text-dimmed group-hover:text-text-bright/80 group-hover:border-dimmed/60 transition uppercase", medium: - "text-[0.75rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-dimmed group-hover:text-bright/80 group-hover:border-dimmed/60 transition uppercase", + "text-[0.75rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-text-dimmed group-hover:text-text-bright/80 group-hover:border-dimmed/60 transition uppercase", }; export type ShortcutKeyVariant = keyof typeof variants; diff --git a/apps/webapp/app/components/primitives/Slider.tsx b/apps/webapp/app/components/primitives/Slider.tsx new file mode 100644 index 000000000..928ada018 --- /dev/null +++ b/apps/webapp/app/components/primitives/Slider.tsx @@ -0,0 +1,52 @@ +import * as RadixSlider from "@radix-ui/react-slider"; +import { ComponentProps } from "react"; +import { cn } from "~/utils/cn"; +import { Icon, RenderIcon } from "./Icon"; + +const variants = { + tertiary: { + container: "h-6 gap-1 rounded-sm hover:bg-charcoal-700 px-1", + icons: "h-4 w-4 text-text-bright", + root: "h-4", + track: "h-1 bg-grid-bright group-hover:bg-background-dimmed", + range: "bg-transparent group-hover:bg-secondary", + thumb: + "h-3 w-3 border-2 border-text-dimmed bg-grid-bright shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] hover:border-text-dimmed focus:shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)]", + }, +}; + +type VariantName = keyof typeof variants; + +export type SliderProps = ComponentProps & { + LeadingIcon?: RenderIcon; + TrailingIcon?: RenderIcon; + variant: VariantName; +}; + +export function Slider({ variant, className, LeadingIcon, TrailingIcon, ...props }: SliderProps) { + const variation = variants[variant]; + return ( +
+ {LeadingIcon && } + + + + + + + {TrailingIcon && } +
+ ); +} diff --git a/apps/webapp/app/components/primitives/StepNumber.tsx b/apps/webapp/app/components/primitives/StepNumber.tsx index 1ae524bff..8cf198ba4 100644 --- a/apps/webapp/app/components/primitives/StepNumber.tsx +++ b/apps/webapp/app/components/primitives/StepNumber.tsx @@ -21,14 +21,14 @@ export function StepNumber({
{active ? (
- + {stepNumber} {title}
) : (
- + {complete ? "✓" : stepNumber} diff --git a/apps/webapp/app/components/primitives/Switch.tsx b/apps/webapp/app/components/primitives/Switch.tsx index b7bef1169..f4e1c67e0 100644 --- a/apps/webapp/app/components/primitives/Switch.tsx +++ b/apps/webapp/app/components/primitives/Switch.tsx @@ -6,16 +6,16 @@ import { cn } from "~/utils/cn"; const variations = { large: { - container: "flex items-center gap-x-2 rounded-md hover:bg-slate-850 p-2", + container: "flex items-center gap-x-2 rounded-md hover:bg-charcoal-850 p-2", root: "h-6 w-11", thumb: "h-5 w-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0", - text: "text-sm text-slate-400 group-hover:text-slate-200", + text: "text-sm text-charcoal-400 group-hover:text-charcoal-200", }, small: { - container: "flex items-center gap-x-1.5 rounded hover:bg-slate-850 pr-1 py-[0.1rem] pl-1.5", + container: "flex items-center gap-x-1.5 rounded hover:bg-charcoal-850 pr-1 py-[0.1rem] pl-1.5", root: "h-3 w-6", thumb: "h-2.5 w-2.5 data-[state=checked]:translate-x-2.5 data-[state=unchecked]:translate-x-0", - text: "text-xs text-slate-400 group-hover:text-slate-200 hover:cursor-pointer", + text: "text-xs text-charcoal-400 group-hover:text-charcoal-200 hover:cursor-pointer", }, }; @@ -37,14 +37,14 @@ export const Switch = React.forwardRef
diff --git a/apps/webapp/app/components/primitives/Table.tsx b/apps/webapp/app/components/primitives/Table.tsx index e698a47d5..29f9cd059 100644 --- a/apps/webapp/app/components/primitives/Table.tsx +++ b/apps/webapp/app/components/primitives/Table.tsx @@ -16,7 +16,7 @@ export const Table = forwardRef( return (
return ( {children} @@ -55,7 +55,7 @@ type TableBodyProps = { export const TableBody = forwardRef( ({ className, children }, ref) => { return ( - + {children} ); @@ -106,7 +106,7 @@ export const TableHeaderCell = forwardRef( ( @@ -153,7 +153,7 @@ export const TableCell = forwardRef( } const flexClasses = cn( - "flex w-full whitespace-nowrap px-4 py-3 text-xs text-dimmed", + "flex w-full whitespace-nowrap px-4 py-3 text-xs text-text-dimmed", alignment === "left" ? "justify-start text-left" : alignment === "center" @@ -165,9 +165,9 @@ export const TableCell = forwardRef( {children} - + ); }); @@ -238,7 +238,7 @@ export const TableCellMenu = forwardRef< setIsOpen(open)}>
{children}
diff --git a/apps/webapp/app/components/primitives/Tabs.tsx b/apps/webapp/app/components/primitives/Tabs.tsx index 00c701a30..c675e2b82 100644 --- a/apps/webapp/app/components/primitives/Tabs.tsx +++ b/apps/webapp/app/components/primitives/Tabs.tsx @@ -8,12 +8,12 @@ export type TabsProps = { to: string; }[]; className?: string; - layoutId: string + layoutId: string; }; export function Tabs({ tabs, className, layoutId }: TabsProps) { return ( -
+
{tabs.map((tab, index) => ( {({ isActive, isPending }) => ( @@ -21,7 +21,7 @@ export function Tabs({ tabs, className, layoutId }: TabsProps) { {tab.label} @@ -29,7 +29,7 @@ export function Tabs({ tabs, className, layoutId }: TabsProps) { {isActive || isPending ? ( ) : ( -
+
)} )} diff --git a/apps/webapp/app/components/primitives/TextArea.tsx b/apps/webapp/app/components/primitives/TextArea.tsx index fafff8e03..5e56452c7 100644 --- a/apps/webapp/app/components/primitives/TextArea.tsx +++ b/apps/webapp/app/components/primitives/TextArea.tsx @@ -8,7 +8,7 @@ export function TextArea({ className, rows, ...props }: TextAreaProps) { {...props} rows={rows ?? 6} className={cn( - "w-full rounded-md border border-slate-800 bg-slate-850 px-3 text-sm text-bright ring-offset-background transition file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-muted-foreground hover:border-slate-750 hover:bg-slate-800 focus:border-ring focus:outline-none focus:ring-2 focus:ring-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50", + "ring-offset-background placeholder:text-muted-foreground focus:border-ring focus:ring-ring focus-visible:ring-ring w-full rounded-md border border-charcoal-800 bg-charcoal-850 px-3 text-sm text-text-bright transition file:border-0 file:bg-transparent file:text-base file:font-medium hover:border-charcoal-750 hover:bg-charcoal-800 focus:outline-none focus:ring-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50", className )} /> diff --git a/apps/webapp/app/components/primitives/TextLink.tsx b/apps/webapp/app/components/primitives/TextLink.tsx index 5762d9f05..984edbf61 100644 --- a/apps/webapp/app/components/primitives/TextLink.tsx +++ b/apps/webapp/app/components/primitives/TextLink.tsx @@ -6,7 +6,7 @@ const variations = { primary: "text-indigo-500 transition hover:text-indigo-400 inline-flex gap-0.5 items-center group", secondary: - "text-dimmed transition underline underline-offset-2 decoration-dimmed/50 hover:decoration-dimmed inline-flex gap-0.5 items-center group", + "text-text-dimmed transition underline underline-offset-2 decoration-dimmed/50 hover:decoration-dimmed inline-flex gap-0.5 items-center group", } as const; type TextLinkProps = { diff --git a/apps/webapp/app/components/primitives/Timeline.tsx b/apps/webapp/app/components/primitives/Timeline.tsx new file mode 100644 index 000000000..23ea46d20 --- /dev/null +++ b/apps/webapp/app/components/primitives/Timeline.tsx @@ -0,0 +1,243 @@ +import { + Component, + ComponentPropsWithoutRef, + Fragment, + ReactNode, + createContext, + useCallback, + useContext, + useRef, + useState, +} from "react"; + +interface MousePosition { + x: number; + y: number; +} +const MousePositionContext = createContext(undefined); +export function MousePositionProvider({ children }: { children: ReactNode }) { + const ref = useRef(null); + const [position, setPosition] = useState(undefined); + + const handleMouseMove = useCallback( + (e: React.MouseEvent) => { + if (!ref.current) { + setPosition(undefined); + return; + } + + const { top, left, width, height } = ref.current.getBoundingClientRect(); + const x = (e.clientX - left) / width; + const y = (e.clientY - top) / height; + + if (x < 0 || x > 1 || y < 0 || y > 1) { + setPosition(undefined); + return; + } + + setPosition({ x, y }); + }, + [ref.current] + ); + + return ( +
setPosition(undefined)} + onMouseMove={handleMouseMove} + style={{ width: "100%", height: "100%" }} + > + {children} +
+ ); +} +export const useMousePosition = () => { + return useContext(MousePositionContext); +}; + +type TimelineContextState = { + startMs: number; + durationMs: number; +}; +const TimelineContext = createContext({} as TimelineContextState); + +function useTimeline() { + return useContext(TimelineContext); +} + +type TimelineMousePositionContextState = { x: number; y: number } | undefined; +const TimelineMousePositionContext = createContext(undefined); +function useTimelineMousePosition() { + return useContext(TimelineMousePositionContext); +} + +export type RootProps = { + /** If the timeline doesn't start at zero. Doesn't impact layout but gives you the times back */ + startMs?: number; + durationMs: number; + /** A number between 0 and 1, determines the width between min and max */ + scale: number; + minWidth: number; + maxWidth: number; + children?: ReactNode; + className?: string; +}; + +/** The main element that determines the dimensions for all sub-elements */ +export function Root({ + startMs = 0, + durationMs, + scale, + minWidth, + maxWidth, + children, + className, +}: RootProps) { + const pixelWidth = calculatePixelWidth(minWidth, maxWidth, scale); + + return ( + +
+ {children} +
+
+ ); +} + +export type RowProps = ComponentPropsWithoutRef<"div">; + +/** This simply acts as a container, with position relative. + * This allows you to nest "Rows" and put heights on them */ +export function Row({ className, children, ...props }: RowProps) { + return ( +
+ {children} +
+ ); +} + +export type PointProps = { + ms: number; + className?: string; + children?: (ms: number) => ReactNode; +}; + +/** A point in time, it has no duration */ +export function Point({ ms, className, children }: PointProps) { + const { startMs, durationMs } = useTimeline(); + const position = inverseLerp(startMs, startMs + durationMs, ms); + return ( +
+ {children && children(ms)} +
+ ); +} + +export type SpanProps = { + startMs: number; + durationMs: number; + className?: string; + children?: ReactNode; +}; + +/** As span of time with a start and duration */ +export function Span({ startMs, durationMs, className, children }: SpanProps) { + const { startMs: rootStartMs, durationMs: rootDurationMs } = useTimeline(); + const position = inverseLerp(rootStartMs, rootStartMs + rootDurationMs, startMs); + const width = + inverseLerp(rootStartMs, rootStartMs + rootDurationMs, startMs + durationMs) - position; + return ( +
+ {children} +
+ ); +} + +export type EquallyDistributeProps = { + count: number; + children: (ms: number, index: number) => ReactNode; +}; + +/** Render a child equally distributed across the duration */ +export function EquallyDistribute({ count, children }: EquallyDistributeProps) { + const { startMs, durationMs } = useTimeline(); + + return ( + <> + {Array.from({ length: count }).map((_, index) => { + const ms = startMs + (durationMs / (count - 1)) * index; + return {children(ms, index)}; + })} + + ); +} + +export type FollowCursorProps = { + children: (ms: number) => ReactNode; +}; + +/** Renders a child that follows the cursor */ +export function FollowCursor({ children }: FollowCursorProps) { + const { startMs, durationMs } = useTimeline(); + const relativeMousePosition = useMousePosition(); + const ms = relativeMousePosition?.x + ? lerp(startMs, startMs + durationMs, relativeMousePosition.x) + : undefined; + + if (ms === undefined) return null; + + return ( +
+ {children(ms)} +
+ ); +} + +/** Gives the total width of the root */ +function calculatePixelWidth(minWidth: number, maxWidth: number, scale: number) { + return lerp(minWidth, maxWidth, scale); +} + +/** Linearly interpolates between the min/max values, using t. + * It can't go outside the range */ +function lerp(min: number, max: number, t: number) { + return min + (max - min) * clamp(t, 0, 1); +} + +/** Inverse lerp */ +function inverseLerp(min: number, max: number, value: number) { + return (value - min) / (max - min); +} + +/** Clamps a value between a min and max */ +function clamp(value: number, min: number, max: number) { + return Math.min(max, Math.max(min, value)); +} diff --git a/apps/webapp/app/components/primitives/Toast.tsx b/apps/webapp/app/components/primitives/Toast.tsx index 8bd6954a1..d0b1eab67 100644 --- a/apps/webapp/app/components/primitives/Toast.tsx +++ b/apps/webapp/app/components/primitives/Toast.tsx @@ -39,7 +39,7 @@ export function ToastUI({ }) { return (
)} - {message} + {message}
@@ -389,9 +388,9 @@ function RerunPopover({ name={conform.INTENT} value="continue" fullWidth - className="text-bright" + className="text-text-bright" > - + Retry Job run
diff --git a/apps/webapp/app/components/run/TaskAttemptStatus.tsx b/apps/webapp/app/components/run/TaskAttemptStatus.tsx index 8943ad483..df87d1629 100644 --- a/apps/webapp/app/components/run/TaskAttemptStatus.tsx +++ b/apps/webapp/app/components/run/TaskAttemptStatus.tsx @@ -38,7 +38,7 @@ function taskAttemptStatusClassNameColor(status: TaskAttemptStatus): string { case "COMPLETED": return "text-green-500"; case "PENDING": - return "text-slate-400"; + return "text-charcoal-400"; case "STARTED": return "text-blue-500"; case "ERRORED": diff --git a/apps/webapp/app/components/run/TaskCard.tsx b/apps/webapp/app/components/run/TaskCard.tsx index 47cd572a6..a0ccc9e3a 100644 --- a/apps/webapp/app/components/run/TaskCard.tsx +++ b/apps/webapp/app/components/run/TaskCard.tsx @@ -64,7 +64,7 @@ export function TaskCard({ ) } @@ -112,11 +112,11 @@ export function TaskCard({ {subtasks && subtasks.length > 0 && ( )} diff --git a/apps/webapp/app/components/run/TaskCardSkeleton.tsx b/apps/webapp/app/components/run/TaskCardSkeleton.tsx index 47db3abec..e3708eb87 100644 --- a/apps/webapp/app/components/run/TaskCardSkeleton.tsx +++ b/apps/webapp/app/components/run/TaskCardSkeleton.tsx @@ -5,15 +5,15 @@ export function TaskCardSkeleton() { } + title={
} />
-
+
-
+
diff --git a/apps/webapp/app/components/run/TaskStatus.tsx b/apps/webapp/app/components/run/TaskStatus.tsx index 6a861516f..38a4703a7 100644 --- a/apps/webapp/app/components/run/TaskStatus.tsx +++ b/apps/webapp/app/components/run/TaskStatus.tsx @@ -41,7 +41,7 @@ function taskStatusClassNameColor(status: TaskStatus): string { case "COMPLETED": return "text-green-500"; case "PENDING": - return "text-slate-500"; + return "text-charcoal-500"; case "RUNNING": return "text-blue-500"; case "WAITING": @@ -49,6 +49,6 @@ function taskStatusClassNameColor(status: TaskStatus): string { case "ERRORED": return "text-rose-500"; case "CANCELED": - return "text-slate-500"; + return "text-charcoal-500"; } } diff --git a/apps/webapp/app/components/runs/RunFilters.tsx b/apps/webapp/app/components/runs/RunFilters.tsx index e912d353b..efaa8006e 100644 --- a/apps/webapp/app/components/runs/RunFilters.tsx +++ b/apps/webapp/app/components/runs/RunFilters.tsx @@ -141,7 +141,7 @@ export function RunsFilters() { -
@@ -209,7 +209,7 @@ export function filterStatusTitle(status: FilterableStatus): string { export function filterStatusClassNameColor(status: FilterableStatus): string { switch (status) { case "QUEUED": - return "text-slate-500"; + return "text-charcoal-500"; case "IN_PROGRESS": return "text-blue-500"; case "WAITING": @@ -219,7 +219,7 @@ export function filterStatusClassNameColor(status: FilterableStatus): string { case "FAILED": return "text-rose-500"; case "CANCELED": - return "text-slate-500"; + return "text-charcoal-500"; case "TIMEDOUT": return "text-amber-300"; default: { diff --git a/apps/webapp/app/components/runs/RunStatuses.tsx b/apps/webapp/app/components/runs/RunStatuses.tsx index 871aafe21..f5f1cc228 100644 --- a/apps/webapp/app/components/runs/RunStatuses.tsx +++ b/apps/webapp/app/components/runs/RunStatuses.tsx @@ -100,14 +100,14 @@ export function runStatusClassNameColor(status: JobRunStatus): string { case "SUCCESS": return "text-green-500"; case "PENDING": - return "text-slate-500"; + return "text-charcoal-500"; case "STARTED": case "EXECUTING": case "WAITING_TO_CONTINUE": case "WAITING_TO_EXECUTE": return "text-blue-500"; case "QUEUED": - return "text-slate-500"; + return "text-charcoal-500"; case "FAILURE": case "UNRESOLVED_AUTH": case "INVALID_PAYLOAD": @@ -121,7 +121,7 @@ export function runStatusClassNameColor(status: JobRunStatus): string { case "PREPROCESSING": return "text-blue-500"; case "CANCELED": - return "text-slate-500"; + return "text-charcoal-500"; default: { const _exhaustiveCheck: never = status; throw new Error(`Non-exhaustive match for value: ${status}`); diff --git a/apps/webapp/app/components/runs/RunsTable.tsx b/apps/webapp/app/components/runs/RunsTable.tsx index 661eea4d9..c194da953 100644 --- a/apps/webapp/app/components/runs/RunsTable.tsx +++ b/apps/webapp/app/components/runs/RunsTable.tsx @@ -117,9 +117,9 @@ export function RunsTable({ {run.isTest ? ( - + ) : ( - + )} {run.version} @@ -134,9 +134,9 @@ export function RunsTable({ {isLoading && ( - Loading… + Loading… )} diff --git a/apps/webapp/app/components/runs/TimeFrameFilter.tsx b/apps/webapp/app/components/runs/TimeFrameFilter.tsx index 220a8c90e..135afdd95 100644 --- a/apps/webapp/app/components/runs/TimeFrameFilter.tsx +++ b/apps/webapp/app/components/runs/TimeFrameFilter.tsx @@ -51,17 +51,17 @@ export function TimeFrameFilter({ from, to, onRangeChanged }: RunTimeFrameFilter setIsOpen(open)} open={isOpen} modal> - + setActiveTab(v as Mode)} @@ -135,7 +135,7 @@ function RelativeTimeFrame({ variant={value === timeframe.value ? "primary/small" : "tertiary/small"} className={cn( "w-full", - value !== timeframe.value && "border border-slate-700 group-hover:bg-slate-700" + value !== timeframe.value && "border border-charcoal-700 group-hover:bg-charcoal-700" )} onClick={() => { onValueChange(timeframe.value); diff --git a/apps/webapp/app/components/runs/WebhookDeliveryRunsTable.tsx b/apps/webapp/app/components/runs/WebhookDeliveryRunsTable.tsx index f6aef0e70..7b97fcd02 100644 --- a/apps/webapp/app/components/runs/WebhookDeliveryRunsTable.tsx +++ b/apps/webapp/app/components/runs/WebhookDeliveryRunsTable.tsx @@ -95,9 +95,9 @@ export function WebhookDeliveryRunsTable({ {run.verified ? ( - + ) : ( - + )} {run.createdAt ? : "–"} @@ -108,9 +108,9 @@ export function WebhookDeliveryRunsTable({ {isLoading && ( - Loading… + Loading… )} diff --git a/apps/webapp/app/components/runs/v3/RunFilters.tsx b/apps/webapp/app/components/runs/v3/RunFilters.tsx index b4d63d19a..599ef3837 100644 --- a/apps/webapp/app/components/runs/v3/RunFilters.tsx +++ b/apps/webapp/app/components/runs/v3/RunFilters.tsx @@ -167,7 +167,7 @@ export function RunsFilters({ possibleEnvironments, possibleTasks }: RunFiltersP {allTaskRunStatuses.map((status) => ( - + ))} @@ -198,7 +198,7 @@ export function RunsFilters({ possibleEnvironments, possibleTasks }: RunFiltersP -
diff --git a/apps/webapp/app/components/runs/v3/RunIcon.tsx b/apps/webapp/app/components/runs/v3/RunIcon.tsx index 5229f5029..ca708b5a4 100644 --- a/apps/webapp/app/components/runs/v3/RunIcon.tsx +++ b/apps/webapp/app/components/runs/v3/RunIcon.tsx @@ -16,22 +16,22 @@ type TaskIconProps = { }; export function RunIcon({ name, className }: TaskIconProps) { - if (!name) return ; + if (!name) return ; switch (name) { case "task": return ; case "attempt": - return ; + return ; case "wait": return ; case "trace": - return ; + return ; //log levels case "debug": case "log": case "info": - return ; + return ; case "warn": return ; case "error": @@ -44,7 +44,7 @@ export function RunIcon({ name, className }: TaskIconProps) { } + fallback={} /> ); } diff --git a/apps/webapp/app/components/runs/v3/SpanTitle.tsx b/apps/webapp/app/components/runs/v3/SpanTitle.tsx index 3bda38269..de034be0a 100644 --- a/apps/webapp/app/components/runs/v3/SpanTitle.tsx +++ b/apps/webapp/app/components/runs/v3/SpanTitle.tsx @@ -64,7 +64,7 @@ export function SpanCodePathAccessory({ return ( @@ -73,13 +73,13 @@ export function SpanCodePathAccessory({ {item.text} {index < accessory.items.length - 1 && ( - + )} @@ -89,19 +89,19 @@ export function SpanCodePathAccessory({ ); } -function eventTextClassName(event: SpanTitleProps) { +function eventTextClassName(event: Pick) { if (event.isError) { return "text-rose-500"; } switch (event.level) { case "TRACE": { - return classNameForVariant(event.style.variant); + return textClassNameForVariant(event.style.variant); } case "LOG": case "INFO": case "DEBUG": { - return classNameForVariant(event.style.variant); + return textClassNameForVariant(event.style.variant); } case "WARN": { return "text-amber-400"; @@ -110,18 +110,57 @@ function eventTextClassName(event: SpanTitleProps) { return "text-rose-500"; } default: { - return classNameForVariant(event.style.variant); + return textClassNameForVariant(event.style.variant); } } } -function classNameForVariant(variant: TaskEventStyle["variant"]) { +export function eventBackgroundClassName( + event: Pick +) { + if (event.isError) { + return "bg-rose-500"; + } + + switch (event.level) { + case "TRACE": { + return backgroundClassNameForVariant(event.style.variant); + } + case "LOG": + case "INFO": + case "DEBUG": { + return backgroundClassNameForVariant(event.style.variant); + } + case "WARN": { + return "bg-amber-400"; + } + case "ERROR": { + return "bg-rose-500"; + } + default: { + return backgroundClassNameForVariant(event.style.variant); + } + } +} + +function textClassNameForVariant(variant: TaskEventStyle["variant"]) { switch (variant) { case "primary": { return "text-blue-500"; } default: { - return "text-dimmed"; + return "text-text-dimmed"; + } + } +} + +function backgroundClassNameForVariant(variant: TaskEventStyle["variant"]) { + switch (variant) { + case "primary": { + return "bg-blue-500"; + } + default: { + return "bg-charcoal-500"; } } } diff --git a/apps/webapp/app/components/runs/v3/TaskRunStatus.tsx b/apps/webapp/app/components/runs/v3/TaskRunStatus.tsx index d5342c604..5ec0a2c20 100644 --- a/apps/webapp/app/components/runs/v3/TaskRunStatus.tsx +++ b/apps/webapp/app/components/runs/v3/TaskRunStatus.tsx @@ -10,9 +10,15 @@ import { Spinner } from "~/components/primitives/Spinner"; import { cn } from "~/utils/cn"; import { ExtendedTaskAttemptStatus } from "./RunFilters"; -export function TaskRunStatus({ status }: { status: ExtendedTaskAttemptStatus | null }) { +export function TaskRunStatus({ + status, + className, +}: { + status: ExtendedTaskAttemptStatus | null; + className?: string; +}) { return ( - + @@ -59,14 +65,14 @@ export function TaskRunStatusIcon({ export function runStatusClassNameColor(status: ExtendedTaskAttemptStatus | null): string { if (status === null) { - return "text-slate-500"; + return "text-charcoal-500"; } switch (status) { case "ENQUEUED": - return "text-slate-500"; + return "text-charcoal-500"; case "PENDING": - return "text-slate-500"; + return "text-charcoal-500"; case "EXECUTING": return "text-blue-500"; case "PAUSED": @@ -74,7 +80,7 @@ export function runStatusClassNameColor(status: ExtendedTaskAttemptStatus | null case "FAILED": return "text-rose-500"; case "CANCELED": - return "text-slate-500"; + return "text-charcoal-500"; case "COMPLETED": return "text-green-500"; default: { diff --git a/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx b/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx index ea73b80ed..654c84ea3 100644 --- a/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx +++ b/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx @@ -94,9 +94,9 @@ export function TaskRunsTable({ {run.isTest ? ( - + ) : ( - + )} @@ -110,9 +110,9 @@ export function TaskRunsTable({ {isLoading && ( - Loading… + Loading… )} diff --git a/apps/webapp/app/components/stories/Badges.stories.tsx b/apps/webapp/app/components/stories/Badges.stories.tsx deleted file mode 100644 index 93a7f566f..000000000 --- a/apps/webapp/app/components/stories/Badges.stories.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { Badge } from "../primitives/Badge"; - -const meta: Meta = { - title: "Primitives/Badges", - decorators: [withDesign], -}; - -export default meta; - -type Story = StoryObj; - -export const Basic: Story = { - render: () => , -}; - -function BadgesExample() { - return ( -
- Default - Outline -
- ); -} diff --git a/apps/webapp/app/components/stories/Callout.stories.tsx b/apps/webapp/app/components/stories/Callout.stories.tsx deleted file mode 100644 index c8dc3debd..000000000 --- a/apps/webapp/app/components/stories/Callout.stories.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { useState } from "react"; -import { Button } from "../primitives/Buttons"; -import { Callout } from "../primitives/Callout"; -import { EnvelopeIcon } from "@heroicons/react/24/solid"; - -const meta: Meta = { - title: "Primitives/Callout", - decorators: [withDesign], -}; - -export default meta; - -type Story = StoryObj; - -export const Callouts: Story = { - render: () => , -}; - -function CalloutsSet() { - return ( -
-
- This is an info callout - This is a warning callout - This is an error callout - This is an idea callout - This is a docs callout - }> - This has a custom icon - -
-
- - This is an info callout - - - This is a warning callout - - - This is an error callout - - - This is an idea callout - - - This is a docs callout - - }> - This has a custom icon - -
-
- ); -} diff --git a/apps/webapp/app/components/stories/CodeBlock.stories.tsx b/apps/webapp/app/components/stories/CodeBlock.stories.tsx deleted file mode 100644 index 7d2e13620..000000000 --- a/apps/webapp/app/components/stories/CodeBlock.stories.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { CodeBlock } from "../code/CodeBlock"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { cn } from "~/utils/cn"; - -const meta: Meta = { - title: "code/CodeBlock", - component: CodeBlock, - decorators: [withDesign], - tags: ["autodocs"], -}; - -export default meta; - -type Story = StoryObj; - -export const Block: Story = { - args: { - code: `export const client = new TriggerClient("smoke-test", { - apiUrl: "http://localhost:3000", - endpoint: "http://localhost:3007/__trigger/entry", - logLevel: "debug", - longLine: "This is a long line that will scroll off the edge of the screen and cause a horizontal scrollbar", - onLog: (log) => { - console.log(log); - }, - onLogError: (log) => { - console.error(log); - }, - onLogWarning: (log) => { - console.warn(log); - }, - onLogInfo: (log) => { - console.info(log); - }, -});`, - highlightedRanges: [ - [6, 8], - [12, 14], - ], - }, - - render: (args) => , -}; - -Block.parameters = { - design: { - type: "figma", - url: "https://www.figma.com/file/LKQ4FJ4bTnCSjedbRpk931/Sample-File", - }, -}; - -export const OneLiner: Story = { - args: { - code: `{ id: "my-first-job" }`, - showLineNumbers: false, - }, - - render: (args) => , -}; - -export const AnimatedLineHighlighting: Story = { - args: { - code: `{ id: "my-first-job" }`, - showLineNumbers: false, - }, - - render: (args) => , -}; - -const highlightedRegions: { title: string; range?: [number, number][] }[] = [ - { - title: "When a Stripe payment fails", - range: [ - [1, 2], - [4, 4], - ], - }, - { - title: ", ", - }, - { - title: "schedule an email to the user", - range: [[6, 8]], - }, - { - title: ", and ", - }, - { - title: "send a notification to the admin", - range: [[10, 12]], - }, - { - title: ".", - }, -]; - -const code = `export const client = new TriggerClient("smoke-test", { - apiUrl: "http://localhost:3000", - endpoint: "http://localhost:3007/__trigger/entry", - logLevel: "debug", - longLine: "This is a long line that will scroll off the edge of the screen and cause a horizontal scrollbar", - onLog: (log) => { - console.log(log); - }, - onLogError: (log) => { - console.error(log); - }, - onLogWarning: (log) => { - console.warn(log); - }, - onLogInfo: (log) => { - console.info(log); - }, -});`; - -function AnimatedHighlight() { - const firstRange = highlightedRegions.findIndex((region) => region.range); - const [highlighted, setHighlighted] = useState(firstRange); - const [shouldAnimate, setShouldAnimate] = useState(true); - const interval = useRef(); - - useEffect(() => { - clearInterval(interval.current); - interval.current = setInterval(() => { - if (shouldAnimate) { - let nextIndex = highlightedRegions.findIndex((v, i) => { - return i > highlighted && v.range; - }); - if (nextIndex === -1) { - nextIndex = highlightedRegions.findIndex((region) => region.range); - } - setHighlighted(nextIndex); - } - }, 2000); - }, [highlighted, shouldAnimate]); - - const highlightedRanges = highlightedRegions[highlighted]?.range - ? highlightedRegions[highlighted].range! - : []; - - return ( -
-

- {highlightedRegions.map((region, index) => ( - { - if (region.range) { - setHighlighted(index); - setShouldAnimate(false); - } - setShouldAnimate(false); - }} - onMouseLeave={() => { - setShouldAnimate(true); - interval.current?.refresh(); - }} - className={cn( - "transition-bg text-xl transition-colors duration-300", - region.range ? "cursor-pointer rounded px-1 text-indigo-500" : "text-slate-500", - highlighted === index && "bg-indigo-500 text-white" - )} - > - {region.title} - - ))} -

-
{ - setHighlighted(-1); - setShouldAnimate(false); - }} - onMouseLeave={() => { - setShouldAnimate(true); - setHighlighted(highlightedRegions.findIndex((region) => region.range)); - }} - > - -
-
- ); -} diff --git a/apps/webapp/app/components/stories/Dialog.stories.tsx b/apps/webapp/app/components/stories/Dialog.stories.tsx deleted file mode 100644 index a1806503b..000000000 --- a/apps/webapp/app/components/stories/Dialog.stories.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "../primitives/Dialog"; - -const meta: Meta = { - title: "Primitives/Dialog", - // @ts-ignore - component: Dialog, - decorators: [withDesign], -}; - -export default meta; - -type Story = StoryObj; - -export const Basic: Story = { - args: { - // @ts-ignore - appear: true, - show: true, - title: "Dialog Title", - }, - render: (args) => ( - - Open - - - Are you sure absolutely sure? - - This action cannot be undone. This will permanently delete your account and remove your - data from our servers. - - - - - ), -}; - -Basic.parameters = { - design: { - type: "figma", - url: "https://www.figma.com/file/LKQ4FJ4bTnCSjedbRpk931/Sample-File", - }, -}; diff --git a/apps/webapp/app/components/stories/Forms.stories.tsx b/apps/webapp/app/components/stories/Forms.stories.tsx deleted file mode 100644 index eeb66b1fa..000000000 --- a/apps/webapp/app/components/stories/Forms.stories.tsx +++ /dev/null @@ -1,253 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { Fieldset } from "../primitives/Fieldset"; -import { InputGroup } from "../primitives/InputGroup"; -import { Input } from "../primitives/Input"; -import { Hint } from "../primitives/Hint"; -import { FormError } from "../primitives/FormError"; -import { FormButtons } from "../primitives/FormButtons"; -import { Button } from "../primitives/Buttons"; -import { MainCenteredContainer } from "../layout/AppLayout"; -import { FormTitle } from "../primitives/FormTitle"; -import { Form } from "@remix-run/react"; -import { NamedIcon } from "../primitives/NamedIcon"; -import { Paragraph } from "../primitives/Paragraph"; -import { LogoIcon } from "../LogoIcon"; -import { Label } from "../primitives/Label"; -import { TextLink } from "../primitives/TextLink"; -import { EnvironmentLabel } from "../environments/EnvironmentLabel"; - -const meta: Meta = { - title: "Primitives/Forms", - component: Forms, - decorators: [withDesign], -}; - -export default meta; - -type Story = StoryObj; - -export const Basic: Story = { - args: { - text: "Action text", - }, - - render: (args) => , -}; - -Basic.parameters = { - design: { - type: "figma", - url: "https://www.figma.com/file/LKQ4FJ4bTnCSjedbRpk931/Sample-File", - }, -}; - -export const Login: Story = { - args: { - text: "Action text", - }, - - render: (args) => , -}; - -export const Search: Story = { - args: { - text: "Action text", - }, - - render: (args) => , -}; - -export const Inputs: Story = { - render: (args) => , -}; - -function Forms() { - return ( - -
- -
-
- - - - E.g. your company name or your workspace name. - - - - - - You must enter a project name - Your Jobs will live inside this Project. - - - - Create - - } - cancelButton={} - /> -
-
-
-
- ); -} - -function LoginForm() { - return ( - -
- - -
-
- - - - By creating an account you agree to our terms and{" "} - privacy policies. - -
-
-
-
- ); -} - -function SearchForm() { - return ( - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- ); -} - -function InputFields() { - return ( -
- - -
- ); -} - -function InputFieldSet({ disabled }: { disabled?: boolean }) { - return ( -
-
- - - - -
-
- - - - -
-
- } - shortcut="⌘K" - /> - } - shortcut="⌘K" - /> - } - shortcut="⌘K" - /> - } - shortcut="⌘K" - /> - } - shortcut="⌘K" - /> - } - shortcut="⌘K" - /> -
-
- ); -} \ No newline at end of file diff --git a/apps/webapp/app/components/stories/InlineCode.stories.tsx b/apps/webapp/app/components/stories/InlineCode.stories.tsx deleted file mode 100644 index 4001fc6d9..000000000 --- a/apps/webapp/app/components/stories/InlineCode.stories.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { InlineCode } from "../code/InlineCode"; -import { Paragraph } from "../primitives/Paragraph"; - -const meta: Meta = { - title: "code/InlineCode", - component: InlineCode, - decorators: [withDesign], -}; - -export default meta; - -type Story = StoryObj; - -export const Normal: Story = { - args: { - children: `{ id: "my-first-job" }`, - }, - render: (args) => ( - - You should use when you want to achieve this. - - ), -}; - -Normal.parameters = { - design: { - type: "figma", - url: "https://www.figma.com/file/LKQ4FJ4bTnCSjedbRpk931/Sample-File", - }, -}; diff --git a/apps/webapp/app/components/stories/Menus.stories.tsx b/apps/webapp/app/components/stories/Menus.stories.tsx deleted file mode 100644 index b79f296a0..000000000 --- a/apps/webapp/app/components/stories/Menus.stories.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { Fragment, useState } from "react"; - -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, -} from "../primitives/Select"; -import { Header1, Header2 } from "../primitives/Headers"; -import { SelectSeparator } from "@radix-ui/react-select"; -import { - Popover, - PopoverArrowTrigger, - PopoverContent, - PopoverMenuItem, - PopoverSectionHeader, -} from "../primitives/Popover"; - -const meta: Meta = { - title: "Primitives/Menus", - component: SelectMenu, - decorators: [withDesign], - tags: ["autodocs"], -}; - -export default meta; - -type Story = StoryObj; - -export const Selects: Story = { - render: (args) => , -}; - -export const Popovers: Story = { - render: (args) => , -}; - -function SelectMenu() { - return ( -
- Variants - size=small width=content - - - - size=small width=full - - - - size=medium width=content - - - - size=medium width=full - - - -
- ); -} - -function PopoverMenu() { - const [isOpen, setIsOpen] = useState(false); - return ( - setIsOpen(open)}> - My Blog - - - - -
- - -
-
-
- -
-
-
- ); -} diff --git a/apps/webapp/app/components/stories/PageHeader.stories.tsx b/apps/webapp/app/components/stories/PageHeader.stories.tsx deleted file mode 100644 index eaed15a69..000000000 --- a/apps/webapp/app/components/stories/PageHeader.stories.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { LinkButton } from "../primitives/Buttons"; -import { - PageHeader, - PageTitleRow, - PageTitle, - PageButtons, - PageDescription, -} from "../primitives/PageHeader"; - -const meta: Meta = { - title: "Primitives/PageHeaders", - component: PageHeaders, - decorators: [withDesign], -}; - -export default meta; - -type Story = StoryObj; - -export const Basic: Story = { - args: { - text: "Action text", - }, - - render: (args) => , -}; - -Basic.parameters = { - design: { - type: "figma", - url: "https://www.figma.com/file/LKQ4FJ4bTnCSjedbRpk931/Sample-File", - }, -}; - -function PageHeaders() { - return ( -
-
- - - - - - Create a new Organization - - - - - Create new Organizations and new Projects to help organize your Jobs. - - -
-
- - - - - - Create a new Organization - - - - -
-
- ); -} diff --git a/apps/webapp/app/components/stories/SegmentedControl.stories.tsx b/apps/webapp/app/components/stories/SegmentedControl.stories.tsx deleted file mode 100644 index e4acc2577..000000000 --- a/apps/webapp/app/components/stories/SegmentedControl.stories.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { withDesign } from "storybook-addon-designs"; -import { MainCenteredContainer } from "../layout/AppLayout"; -import SegmentedControl from "../primitives/SegmentedControl"; - -const meta: Meta = { - title: "Primitives/SegmentedControl", - decorators: [withDesign], - component: StyledSegmentedControl, -}; - -export default meta; - -type Story = StoryObj; - -export const Basic: Story = { - render: () => , -}; - -Basic.parameters = { - design: { - type: "figma", - url: "https://www.figma.com/file/jBqUJJ2d4lU6aSeKIIOBMY/Trigger.dev?type=design&node-id=2577%3A87576&t=ambgtfvgnwXTHmzI-1", - }, -}; - -const options = [ - { label: "Label 1", value: "developer" }, - { label: "Label 2", value: "Users" }, -]; - -function StyledSegmentedControl() { - return ( - - - - ); -} diff --git a/apps/webapp/app/presenters/v3/RunPresenter.server.ts b/apps/webapp/app/presenters/v3/RunPresenter.server.ts index 505bbc7c6..419d5087b 100644 --- a/apps/webapp/app/presenters/v3/RunPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/RunPresenter.server.ts @@ -1,3 +1,4 @@ +import { millisecondsToNanoseconds } from "@trigger.dev/core/v3/utils/durations"; import { createTreeFromFlatItems, flattenTree } from "~/components/primitives/TreeView/TreeView"; import { PrismaClient, prisma } from "~/db.server"; import { getUsername } from "~/utils/username"; @@ -66,8 +67,37 @@ export class RunPresenter { throw new Error("Trace not found"); } + //this tree starts at the passed in span (hides parent elements if there are any) const tree = createTreeFromFlatItems(traceSummary.spans, run.spanId); + //we need the start offset for each item, and the total duration of the entire tree + const treeRootStartTimeMs = tree ? tree?.data.startTime.getTime() : 0; + let totalDuration = tree?.data.duration ?? 0; + const events = tree + ? flattenTree(tree).map((n) => { + const offset = millisecondsToNanoseconds( + n.data.startTime.getTime() - treeRootStartTimeMs + ); + totalDuration = Math.max(totalDuration, offset + n.data.duration); + return { ...n, data: { ...n.data, offset, isRoot: n.id === traceSummary.rootSpan.id } }; + }) + : []; + + //if any elements are partial we want the total duration to represent all of the time until now + totalDuration = events.some((e) => e.data.isPartial) + ? millisecondsToNanoseconds(Date.now() - treeRootStartTimeMs) + : totalDuration; + + //total duration should be a minimum of 1ms + totalDuration = Math.max(totalDuration, millisecondsToNanoseconds(1)); + + //we need to adjust any partial nodes so they run the full duration + for (const event of events) { + if (event.data.isPartial) { + event.data.duration = totalDuration - event.data.offset; + } + } + return { run: { number: run.number, @@ -79,9 +109,10 @@ export class RunPresenter { userName: getUsername(run.runtimeEnvironment.orgMember?.user), }, }, - events: tree ? flattenTree(tree) : [], + events: events, parentRunFriendlyId: tree?.id === traceSummary.rootSpan.id ? undefined : traceSummary.rootSpan.runId, + duration: totalDuration, }; } } diff --git a/apps/webapp/app/presenters/v3/RunStreamPresenter.server.ts b/apps/webapp/app/presenters/v3/RunStreamPresenter.server.ts new file mode 100644 index 000000000..465471046 --- /dev/null +++ b/apps/webapp/app/presenters/v3/RunStreamPresenter.server.ts @@ -0,0 +1,118 @@ +import { JobRun, TaskRun, TaskRunAttempt } from "@trigger.dev/database"; +import { PrismaClient, prisma } from "~/db.server"; +import { logger } from "~/services/logger.server"; +import { sse } from "~/utils/sse.server"; + +type RunWithAttempts = { + updatedAt: Date; + attempts: { + status: TaskRunAttempt["status"]; + updatedAt: Date; + }[]; +}; + +export class RunStreamPresenter { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call({ + request, + runFriendlyId, + }: { + request: Request; + runFriendlyId: TaskRun["friendlyId"]; + }) { + const run = await this.#runForUpdates(runFriendlyId); + + if (!run) { + return new Response("Not found", { status: 404 }); + } + + let lastUpdatedAt = this.#getLatestUpdatedAt(run); + + logger.info("RunStreamPresenter.call", { + runFriendlyId, + lastUpdatedAt, + }); + + return sse({ + request, + run: async (send, stop) => { + const result = await this.#runForUpdates(runFriendlyId); + if (!result) { + return stop(); + } + + if (this.#isRunCompleted(result)) { + logger.info("RunStreamPresenter.call completed", { + runFriendlyId, + lastUpdatedAt, + completed: true, + }); + send({ data: new Date().toISOString() }); + return stop(); + } + + const newUpdatedAt = this.#getLatestUpdatedAt(result); + if (lastUpdatedAt !== newUpdatedAt) { + logger.info("RunStreamPresenter.call updated", { + runFriendlyId, + lastUpdatedAt, + newUpdatedAt, + }); + send({ data: result.updatedAt.toISOString() }); + } + + logger.info("RunStreamPresenter.call waiting", { + runFriendlyId, + lastUpdatedAt, + newUpdatedAt, + }); + + lastUpdatedAt = newUpdatedAt; + }, + }); + } + + #runForUpdates(friendlyId: string) { + return this.#prismaClient.taskRun.findUnique({ + where: { + friendlyId, + }, + select: { + updatedAt: true, + attempts: { + select: { + status: true, + updatedAt: true, + }, + orderBy: { + createdAt: "desc", + }, + take: 1, + }, + }, + }); + } + + #getLatestUpdatedAt(run: RunWithAttempts) { + const lastAttempt = run.attempts[0]; + if (lastAttempt) { + return lastAttempt.updatedAt.getTime(); + } + + return run.updatedAt.getTime(); + } + + #isRunCompleted(run: RunWithAttempts) { + return run.attempts.some( + (attempt) => + attempt.status === "FAILED" || + attempt.status === "CANCELED" || + attempt.status === "COMPLETED" + ); + } +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug._index/route.tsx index f7aaed590..2fcbff9a3 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug._index/route.tsx @@ -1,61 +1,52 @@ import { Link } from "@remix-run/react"; import simplur from "simplur"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; -import { BreadcrumbLink } from "~/components/navigation/Breadcrumb"; import { LinkButton } from "~/components/primitives/Buttons"; import { Header3 } from "~/components/primitives/Headers"; import { NamedIcon } from "~/components/primitives/NamedIcon"; import { - PageHeader, - PageTitleRow, - PageTitle, - PageButtons, - PageInfoRow, + PageAccessories, + NavBar, PageInfoGroup, + PageInfoRow, + PageTitle, } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; import { useOrganization } from "~/hooks/useOrganizations"; -import { Handle } from "~/utils/handle"; import { newProjectPath, projectPath } from "~/utils/pathBuilder"; -export const handle: Handle = { - breadcrumb: (match) => , -}; - export default function Page() { const organization = useOrganization(); return ( - - - - - - Create a new project - - - + + + + + Create a new project + + + + - + UID: {organization.id} - -