@devxcrew/react-ui 0.0.0-stage → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -2
- package/package.json +105 -4
- package/src/blocks/agent-chat-workspace/agent-chat-workspace.tsx +162 -0
- package/src/blocks/agent-chat-workspace/index.ts +2 -0
- package/src/blocks/agent-provider-settings/agent-provider-settings.tsx +183 -0
- package/src/blocks/agent-provider-settings/index.ts +2 -0
- package/src/blocks/agent-task-workspace/agent-task-workspace.tsx +48 -0
- package/src/blocks/agent-task-workspace/index.ts +2 -0
- package/src/blocks/analysis-context/analysis-context.tsx +6 -0
- package/src/blocks/analysis-context/index.ts +1 -0
- package/src/blocks/app-header/app-header.tsx +159 -0
- package/src/blocks/app-header/index.ts +2 -0
- package/src/blocks/archived-chat-workspace/archived-chat-workspace.tsx +29 -0
- package/src/blocks/archived-chat-workspace/index.ts +2 -0
- package/src/blocks/auth/README.md +32 -0
- package/src/blocks/auth/admin-login-page.tsx +15 -0
- package/src/blocks/auth/admin-password-forgot-page.tsx +5 -0
- package/src/blocks/auth/auth-page-layout.tsx +90 -0
- package/src/blocks/auth/client-login-page.tsx +20 -0
- package/src/blocks/auth/client-password-forgot-page.tsx +5 -0
- package/src/blocks/auth/identity-management-desk.tsx +530 -0
- package/src/blocks/auth/index.ts +12 -0
- package/src/blocks/auth/login-page.tsx +206 -0
- package/src/blocks/auth/password-forgot-page.tsx +60 -0
- package/src/blocks/auth/portal-pages.tsx +32 -0
- package/src/blocks/auth/privileged-desk.tsx +55 -0
- package/src/blocks/auth/register-page.tsx +117 -0
- package/src/blocks/auth/session-boundary.tsx +296 -0
- package/src/blocks/auth/super-admin-login-page.tsx +15 -0
- package/src/blocks/auth/super-admin-password-forgot-page.tsx +5 -0
- package/src/blocks/blog/blog-card.tsx +156 -0
- package/src/blocks/blog/blog-reader.tsx +175 -0
- package/src/blocks/blog/blog-types.ts +47 -0
- package/src/blocks/blog/index.ts +3 -0
- package/src/blocks/chat-composer/chat-composer.tsx +140 -0
- package/src/blocks/chat-composer/index.ts +1 -0
- package/src/blocks/chat-history/chat-history.tsx +59 -0
- package/src/blocks/chat-history/index.ts +1 -0
- package/src/blocks/chat-response-progress/chat-response-progress.tsx +34 -0
- package/src/blocks/chat-response-progress/index.ts +2 -0
- package/src/blocks/chat-runtime-controls/chat-runtime-controls.tsx +56 -0
- package/src/blocks/chat-runtime-controls/index.ts +2 -0
- package/src/blocks/chat-runtime-trace/chat-runtime-trace.tsx +71 -0
- package/src/blocks/chat-runtime-trace/index.ts +2 -0
- package/src/blocks/codex-connection-settings/codex-connection-settings.tsx +42 -0
- package/src/blocks/codex-connection-settings/index.ts +1 -0
- package/src/blocks/dropzone/dropzone-file-item.tsx +113 -0
- package/src/blocks/dropzone/dropzone-types.ts +32 -0
- package/src/blocks/dropzone/dropzone.tsx +216 -0
- package/src/blocks/dropzone/index.ts +3 -0
- package/src/blocks/ecommerce/cart/cart-drawer.tsx +228 -0
- package/src/blocks/ecommerce/cart/cart-types.ts +27 -0
- package/src/blocks/ecommerce/cart/index.ts +2 -0
- package/src/blocks/ecommerce/categories/categories-types.ts +23 -0
- package/src/blocks/ecommerce/categories/category-showcase.tsx +125 -0
- package/src/blocks/ecommerce/categories/index.ts +2 -0
- package/src/blocks/ecommerce/checkout/checkout-types.ts +48 -0
- package/src/blocks/ecommerce/checkout/checkout-wizard.tsx +468 -0
- package/src/blocks/ecommerce/checkout/index.ts +2 -0
- package/src/blocks/ecommerce/comparison/comparison-types.ts +28 -0
- package/src/blocks/ecommerce/comparison/index.ts +2 -0
- package/src/blocks/ecommerce/comparison/product-comparison.tsx +174 -0
- package/src/blocks/ecommerce/coupon-wallet/coupon-wallet-types.ts +21 -0
- package/src/blocks/ecommerce/coupon-wallet/coupon-wallet.tsx +154 -0
- package/src/blocks/ecommerce/coupon-wallet/index.ts +2 -0
- package/src/blocks/ecommerce/delivery-tracker/delivery-tracker-types.ts +20 -0
- package/src/blocks/ecommerce/delivery-tracker/delivery-tracker.tsx +169 -0
- package/src/blocks/ecommerce/delivery-tracker/index.ts +2 -0
- package/src/blocks/ecommerce/index.ts +12 -0
- package/src/blocks/ecommerce/payment-methods/index.ts +2 -0
- package/src/blocks/ecommerce/payment-methods/payment-method-selector.tsx +224 -0
- package/src/blocks/ecommerce/payment-methods/payment-methods-types.ts +21 -0
- package/src/blocks/ecommerce/price-history/index.ts +2 -0
- package/src/blocks/ecommerce/price-history/price-history-chart.tsx +217 -0
- package/src/blocks/ecommerce/price-history/price-history-types.ts +16 -0
- package/src/blocks/ecommerce/reviews/index.ts +2 -0
- package/src/blocks/ecommerce/reviews/reviews-section.tsx +309 -0
- package/src/blocks/ecommerce/reviews/reviews-types.ts +26 -0
- package/src/blocks/ecommerce/wishlist/index.ts +2 -0
- package/src/blocks/ecommerce/wishlist/wishlist-grid.tsx +149 -0
- package/src/blocks/ecommerce/wishlist/wishlist-types.ts +19 -0
- package/src/blocks/execution-status/README.md +25 -0
- package/src/blocks/execution-status/execution-status.tsx +135 -0
- package/src/blocks/execution-status/index.ts +2 -0
- package/src/blocks/file-tree/file-icons.tsx +84 -0
- package/src/blocks/file-tree/file-tree-node.tsx +158 -0
- package/src/blocks/file-tree/file-tree-types.ts +33 -0
- package/src/blocks/file-tree/file-tree.tsx +163 -0
- package/src/blocks/file-tree/index.ts +5 -0
- package/src/blocks/filter-builder/filter-builder.tsx +169 -0
- package/src/blocks/filter-builder/filter-row.tsx +164 -0
- package/src/blocks/filter-builder/filter-types.ts +61 -0
- package/src/blocks/filter-builder/index.ts +12 -0
- package/src/blocks/footer/footer-types.ts +35 -0
- package/src/blocks/footer/index.ts +2 -0
- package/src/blocks/footer/site-footer.tsx +212 -0
- package/src/blocks/form/form-block.tsx +113 -0
- package/src/blocks/form/form-lookup-field.tsx +60 -0
- package/src/blocks/form/index.ts +4 -0
- package/src/blocks/handover-stack/handover-stack.tsx +41 -0
- package/src/blocks/handover-stack/index.ts +1 -0
- package/src/blocks/idea-handover/idea-handover-readiness.ts +41 -0
- package/src/blocks/idea-handover/idea-handover-workspace.tsx +150 -0
- package/src/blocks/idea-handover/index.ts +4 -0
- package/src/blocks/kanban/index.ts +11 -0
- package/src/blocks/kanban/kanban-board.tsx +146 -0
- package/src/blocks/kanban/kanban-card.tsx +149 -0
- package/src/blocks/kanban/kanban-column.tsx +102 -0
- package/src/blocks/kanban/kanban-types.ts +38 -0
- package/src/blocks/loader/global-loader.tsx +90 -0
- package/src/blocks/loader/index.ts +1 -0
- package/src/blocks/mascot/README.md +21 -0
- package/src/blocks/mascot/index.ts +2 -0
- package/src/blocks/mascot/mascot.tsx +202 -0
- package/src/blocks/master-list/README.md +21 -0
- package/src/blocks/master-list/index.ts +12 -0
- package/src/blocks/master-list/master-form.tsx +157 -0
- package/src/blocks/master-list/master-list-desk.tsx +370 -0
- package/src/blocks/master-list/master-list.tsx +189 -0
- package/src/blocks/master-list/types.ts +18 -0
- package/src/blocks/mermaid-preview/index.ts +1 -0
- package/src/blocks/mermaid-preview/mermaid-preview.tsx +164 -0
- package/src/blocks/notifications/README.md +7 -0
- package/src/blocks/notifications/index.ts +1 -0
- package/src/blocks/notifications/notification-center-page.tsx +73 -0
- package/src/blocks/pricing/index.ts +7 -0
- package/src/blocks/pricing/pricing-table.tsx +173 -0
- package/src/blocks/pricing/pricing-types.ts +34 -0
- package/src/blocks/product-card/index.ts +2 -0
- package/src/blocks/product-card/product-card-types.ts +35 -0
- package/src/blocks/product-card/product-card.tsx +183 -0
- package/src/blocks/table/data-table-block.tsx +313 -0
- package/src/blocks/table/data-table-filter-menu.tsx +48 -0
- package/src/blocks/table/data-table-icon-menu-trigger.tsx +44 -0
- package/src/blocks/table/data-table-menu-content.tsx +32 -0
- package/src/blocks/table/data-table-pagination.tsx +149 -0
- package/src/blocks/table/data-table-row-actions.tsx +53 -0
- package/src/blocks/table/data-table-status.tsx +42 -0
- package/src/blocks/table/data-table-toolbar.tsx +124 -0
- package/src/blocks/table/data-table-totals.tsx +31 -0
- package/src/blocks/table/index.ts +13 -0
- package/src/blocks/workspace/index.ts +19 -0
- package/src/blocks/workspace/workspace-action-card.tsx +73 -0
- package/src/blocks/workspace/workspace-application-header.tsx +67 -0
- package/src/blocks/workspace/workspace-entity-card.tsx +48 -0
- package/src/blocks/workspace/workspace-health-summary.tsx +15 -0
- package/src/blocks/workspace/workspace-metric-card.tsx +97 -0
- package/src/blocks/workspace/workspace-page-header.tsx +43 -0
- package/src/blocks/workspace/workspace-publish-status.tsx +15 -0
- package/src/blocks/workspace/workspace-route-checklist.tsx +28 -0
- package/src/blocks/workspace/workspace-runtime-status.tsx +48 -0
- package/src/blocks/workspace/workspace-section-card.tsx +42 -0
- package/src/components/accordion.tsx +65 -0
- package/src/components/alert-dialog.tsx +160 -0
- package/src/components/alert.tsx +68 -0
- package/src/components/aspect-ratio.tsx +22 -0
- package/src/components/attachment.tsx +195 -0
- package/src/components/avatar.tsx +90 -0
- package/src/components/badge.tsx +55 -0
- package/src/components/breadcrumb.tsx +100 -0
- package/src/components/bubble.tsx +123 -0
- package/src/components/button-group.tsx +79 -0
- package/src/components/button.tsx +71 -0
- package/src/components/calendar.tsx +185 -0
- package/src/components/card.tsx +100 -0
- package/src/components/carousel.tsx +229 -0
- package/src/components/chart.tsx +338 -0
- package/src/components/checkbox.tsx +27 -0
- package/src/components/collapsible.tsx +15 -0
- package/src/components/combobox.tsx +269 -0
- package/src/components/command.tsx +181 -0
- package/src/components/compact-model-switcher.tsx +128 -0
- package/src/components/context-menu.tsx +244 -0
- package/src/components/dialog.tsx +138 -0
- package/src/components/direction.tsx +1 -0
- package/src/components/drawer.tsx +208 -0
- package/src/components/dropdown-menu.tsx +257 -0
- package/src/components/empty.tsx +93 -0
- package/src/components/field.tsx +232 -0
- package/src/components/hover-card.tsx +45 -0
- package/src/components/input-group.tsx +146 -0
- package/src/components/input-otp.tsx +83 -0
- package/src/components/input.tsx +19 -0
- package/src/components/item.tsx +188 -0
- package/src/components/kbd.tsx +26 -0
- package/src/components/label.tsx +17 -0
- package/src/components/markdown-content.tsx +113 -0
- package/src/components/marker.tsx +67 -0
- package/src/components/menubar.tsx +267 -0
- package/src/components/message-scroller.tsx +91 -0
- package/src/components/message.tsx +84 -0
- package/src/components/native-select.tsx +54 -0
- package/src/components/navigation-menu.tsx +160 -0
- package/src/components/pagination.tsx +118 -0
- package/src/components/popover.tsx +74 -0
- package/src/components/progress.tsx +82 -0
- package/src/components/questionnaire.tsx +150 -0
- package/src/components/radio-group.tsx +35 -0
- package/src/components/resizable.tsx +41 -0
- package/src/components/rich-text-editor.tsx +604 -0
- package/src/components/scroll-area.tsx +47 -0
- package/src/components/select.tsx +189 -0
- package/src/components/separator.tsx +18 -0
- package/src/components/sheet.tsx +125 -0
- package/src/components/sidebar.tsx +693 -0
- package/src/components/skeleton.tsx +13 -0
- package/src/components/slider.tsx +51 -0
- package/src/components/sonner.tsx +45 -0
- package/src/components/sparkline.tsx +231 -0
- package/src/components/spinner.tsx +20 -0
- package/src/components/status-badge.tsx +114 -0
- package/src/components/switch.tsx +31 -0
- package/src/components/table.tsx +91 -0
- package/src/components/tabs.tsx +72 -0
- package/src/components/textarea.tsx +17 -0
- package/src/components/toast.tsx +222 -0
- package/src/components/toggle-group.tsx +85 -0
- package/src/components/toggle.tsx +42 -0
- package/src/components/tooltip.tsx +53 -0
- package/src/design-system/README.md +55 -0
- package/src/design-system/action-intents.ts +21 -0
- package/src/design-system/agent-manifest.ts +83 -0
- package/src/design-system/block-registry.ts +272 -0
- package/src/design-system/component-registry.ts +142 -0
- package/src/design-system/contracts.ts +69 -0
- package/src/design-system/defaults.ts +3 -0
- package/src/design-system/index.ts +29 -0
- package/src/design-system/mcp-server.ts +156 -0
- package/src/design-system/page-registry.ts +74 -0
- package/src/design-system/selection.ts +67 -0
- package/src/design-system/template-registry.ts +33 -0
- package/src/features/interface-topology/index.ts +14 -0
- package/src/features/interface-topology/interface-topology.types.ts +40 -0
- package/src/features/interface-topology/topology-control.tsx +18 -0
- package/src/features/interface-topology/topology-inspector.tsx +228 -0
- package/src/features/interface-topology/topology-marker.tsx +47 -0
- package/src/features/interface-topology/topology-region.tsx +44 -0
- package/src/features/interface-topology/use-interface-topology.ts +178 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/hooks/use-scroll-follow.ts +22 -0
- package/src/index.ts +11 -0
- package/src/layouts/README.md +65 -0
- package/src/layouts/agent-workspace/agent-activity-rail.tsx +101 -0
- package/src/layouts/agent-workspace/agent-workspace.tsx +27 -0
- package/src/layouts/agent-workspace/agent-workspace.types.ts +28 -0
- package/src/layouts/agent-workspace/index.ts +7 -0
- package/src/layouts/blog-header/blog-header-types.ts +25 -0
- package/src/layouts/blog-header/blog-header.tsx +172 -0
- package/src/layouts/blog-header/index.ts +2 -0
- package/src/layouts/documentation-workspace/README.md +8 -0
- package/src/layouts/documentation-workspace/documentation-workspace.tsx +34 -0
- package/src/layouts/documentation-workspace/index.ts +1 -0
- package/src/layouts/ecommerce-header/ecommerce-actions.tsx +89 -0
- package/src/layouts/ecommerce-header/ecommerce-announcement.tsx +43 -0
- package/src/layouts/ecommerce-header/ecommerce-drawer.tsx +97 -0
- package/src/layouts/ecommerce-header/ecommerce-header-types.ts +59 -0
- package/src/layouts/ecommerce-header/ecommerce-header.tsx +47 -0
- package/src/layouts/ecommerce-header/ecommerce-navigation.tsx +98 -0
- package/src/layouts/ecommerce-header/ecommerce-search.tsx +86 -0
- package/src/layouts/ecommerce-header/index.ts +2 -0
- package/src/layouts/main-workspace/index.ts +28 -0
- package/src/layouts/mdi/index.ts +1 -0
- package/src/layouts/mdi/mdi.tsx +20 -0
- package/src/layouts/mdi-main/index.ts +27 -0
- package/src/layouts/mdi-main/mdi-app-catalog.generated.ts +159 -0
- package/src/layouts/mdi-main/mdi-app-catalog.ts +76 -0
- package/src/layouts/mdi-main/mdi-app-switcher.tsx +107 -0
- package/src/layouts/mdi-main/mdi-feature-settings.tsx +135 -0
- package/src/layouts/mdi-main/mdi-global-search.tsx +192 -0
- package/src/layouts/mdi-main/mdi-main.tsx +241 -0
- package/src/layouts/mdi-main/mdi-notifications-menu.tsx +197 -0
- package/src/layouts/mdi-main/mdi-profile-menu.tsx +79 -0
- package/src/layouts/mdi-main/mdi-sidebar-navigation.tsx +226 -0
- package/src/layouts/mdi-main/mdi-sidebar.tsx +110 -0
- package/src/layouts/mdi-main/mdi-status-bar.tsx +26 -0
- package/src/layouts/mdi-main/mdi-top-menu-control.ts +4 -0
- package/src/layouts/mdi-main/mdi-top-menu.tsx +117 -0
- package/src/layouts/mdi-main/mdi-topology.tsx +297 -0
- package/src/layouts/mdi-main/mdi-tweak-panel.tsx +104 -0
- package/src/layouts/mdi-main/mdi-types.ts +131 -0
- package/src/layouts/mdi-main/use-mdi-features.ts +36 -0
- package/src/layouts/mdi-main/use-mdi-sidebar-state.ts +59 -0
- package/src/layouts/site-header/announcement-bar.tsx +55 -0
- package/src/layouts/site-header/category-strip.tsx +41 -0
- package/src/layouts/site-header/index.ts +16 -0
- package/src/layouts/site-header/main-navbar.tsx +179 -0
- package/src/layouts/site-header/mobile-drawer.tsx +140 -0
- package/src/layouts/site-header/site-header-types.ts +63 -0
- package/src/layouts/site-header/site-header.tsx +58 -0
- package/src/lib/random-id.ts +19 -0
- package/src/lib/utils.ts +6 -0
- package/src/providers/pdf-provider.ts +137 -0
- package/src/styles/globals.css +374 -0
- package/src/templates/README.md +12 -0
- package/src/templates/dashboard-01/components/app-sidebar.tsx +177 -0
- package/src/templates/dashboard-01/components/chart-area-interactive.tsx +276 -0
- package/src/templates/dashboard-01/components/data-table-cell-viewer.tsx +213 -0
- package/src/templates/dashboard-01/components/data-table.tsx +577 -0
- package/src/templates/dashboard-01/components/nav-documents.tsx +79 -0
- package/src/templates/dashboard-01/components/nav-main.tsx +55 -0
- package/src/templates/dashboard-01/components/nav-secondary.tsx +39 -0
- package/src/templates/dashboard-01/components/nav-user.tsx +97 -0
- package/src/templates/dashboard-01/components/section-cards.tsx +99 -0
- package/src/templates/dashboard-01/components/site-header.tsx +14 -0
- package/src/templates/dashboard-01/data.json +614 -0
- package/src/templates/dashboard-01/index.ts +7 -0
- package/src/templates/documentation-sidebar/app-sidebar.tsx +95 -0
- package/src/templates/documentation-sidebar/index.ts +2 -0
- package/src/templates/sidebar-07/components/app-sidebar.tsx +175 -0
- package/src/templates/sidebar-07/components/nav-main.tsx +64 -0
- package/src/templates/sidebar-07/components/nav-projects.tsx +79 -0
- package/src/templates/sidebar-07/components/nav-user.tsx +105 -0
- package/src/templates/sidebar-07/components/team-switcher.tsx +94 -0
- package/src/templates/sidebar-07/index.ts +1 -0
- package/src/templates/ui-page/index.ts +7 -0
- package/src/templates/ui-page/ui-browser-frame.tsx +27 -0
- package/src/templates/ui-page/ui-template-code.tsx +48 -0
- package/src/templates/ui-page/ui-template-header.tsx +87 -0
- package/src/templates/ui-page/ui-template-navigation.tsx +60 -0
- package/src/templates/ui-page/ui-template-page.tsx +97 -0
- package/src/theme/index.ts +12 -0
- package/src/theme/theme-config.ts +30 -0
- package/src/theme/theme-provider.tsx +142 -0
- package/src/theme/theme-selector.tsx +75 -0
- package/src/tokens/index.ts +15 -0
- package/src/vite-env.d.ts +3 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { Columns3, Filter, Search } from 'lucide-react';
|
|
3
|
+
import { DropdownMenu, DropdownMenuCheckboxItem } from '@devxcrew/react-ui/components/dropdown-menu';
|
|
4
|
+
import { Input } from '@devxcrew/react-ui/components/input';
|
|
5
|
+
import { DataTableIconMenuTrigger } from './data-table-icon-menu-trigger';
|
|
6
|
+
import { DataTableMenuContent, dataTableMenuOptionClass } from './data-table-menu-content';
|
|
7
|
+
import { TopologyRegion, type InterfaceTopologyController } from '@devxcrew/react-ui/features/interface-topology';
|
|
8
|
+
|
|
9
|
+
export type DataTableColumnControl = {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
onVisibleChange: (visible: boolean) => void;
|
|
13
|
+
visible: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function DataTableToolbar({
|
|
17
|
+
columns,
|
|
18
|
+
filters,
|
|
19
|
+
onSearchChange,
|
|
20
|
+
search,
|
|
21
|
+
searchPlaceholder,
|
|
22
|
+
topology,
|
|
23
|
+
topologyIds,
|
|
24
|
+
filterOptions,
|
|
25
|
+
filterValue,
|
|
26
|
+
onFilterChange,
|
|
27
|
+
}: {
|
|
28
|
+
columns: DataTableColumnControl[];
|
|
29
|
+
filters?: ReactNode;
|
|
30
|
+
onSearchChange: (value: string) => void;
|
|
31
|
+
search: string;
|
|
32
|
+
searchPlaceholder: string;
|
|
33
|
+
topology?: InterfaceTopologyController;
|
|
34
|
+
topologyIds?: { searchBar?: string; columnFilter?: string };
|
|
35
|
+
filterOptions?: readonly { label: string; value: string }[];
|
|
36
|
+
filterValue?: string;
|
|
37
|
+
onFilterChange?: (value: string) => void;
|
|
38
|
+
}) {
|
|
39
|
+
const searchBar = (
|
|
40
|
+
<label className="relative w-full min-w-0 sm:max-w-3xl">
|
|
41
|
+
<Search className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
|
|
42
|
+
<span className="sr-only">{searchPlaceholder}</span>
|
|
43
|
+
<Input
|
|
44
|
+
className="h-8 pl-9"
|
|
45
|
+
onChange={(event) => onSearchChange(event.target.value)}
|
|
46
|
+
placeholder={searchPlaceholder}
|
|
47
|
+
value={search}
|
|
48
|
+
/>
|
|
49
|
+
</label>
|
|
50
|
+
);
|
|
51
|
+
const filterMenu =
|
|
52
|
+
filterOptions?.length && onFilterChange ? (
|
|
53
|
+
<DropdownMenu>
|
|
54
|
+
<DataTableIconMenuTrigger active={Boolean(filterValue)} label="Filter">
|
|
55
|
+
<Filter />
|
|
56
|
+
</DataTableIconMenuTrigger>
|
|
57
|
+
<DataTableMenuContent actionLabel="Clear" onAction={() => onFilterChange('')} title="Filter options">
|
|
58
|
+
<DropdownMenuCheckboxItem
|
|
59
|
+
checked={!filterValue}
|
|
60
|
+
className={dataTableMenuOptionClass}
|
|
61
|
+
onCheckedChange={() => onFilterChange('')}
|
|
62
|
+
>
|
|
63
|
+
All records
|
|
64
|
+
</DropdownMenuCheckboxItem>
|
|
65
|
+
{filterOptions.map((option) => (
|
|
66
|
+
<DropdownMenuCheckboxItem
|
|
67
|
+
checked={filterValue === option.value}
|
|
68
|
+
className={dataTableMenuOptionClass}
|
|
69
|
+
key={option.value}
|
|
70
|
+
onCheckedChange={() => onFilterChange(option.value)}
|
|
71
|
+
>
|
|
72
|
+
{option.label}
|
|
73
|
+
</DropdownMenuCheckboxItem>
|
|
74
|
+
))}
|
|
75
|
+
</DataTableMenuContent>
|
|
76
|
+
</DropdownMenu>
|
|
77
|
+
) : null;
|
|
78
|
+
const columnFilter = (
|
|
79
|
+
<DropdownMenu>
|
|
80
|
+
<DataTableIconMenuTrigger label="Columns">
|
|
81
|
+
<Columns3 />
|
|
82
|
+
</DataTableIconMenuTrigger>
|
|
83
|
+
<DataTableMenuContent
|
|
84
|
+
actionLabel="Show all"
|
|
85
|
+
onAction={() => columns.forEach((column) => column.onVisibleChange(true))}
|
|
86
|
+
title="Visible columns"
|
|
87
|
+
>
|
|
88
|
+
{columns.map((column) => (
|
|
89
|
+
<DropdownMenuCheckboxItem
|
|
90
|
+
checked={column.visible}
|
|
91
|
+
className={dataTableMenuOptionClass}
|
|
92
|
+
key={column.id}
|
|
93
|
+
onCheckedChange={(visible) => column.onVisibleChange(Boolean(visible))}
|
|
94
|
+
>
|
|
95
|
+
{column.label}
|
|
96
|
+
</DropdownMenuCheckboxItem>
|
|
97
|
+
))}
|
|
98
|
+
</DataTableMenuContent>
|
|
99
|
+
</DropdownMenu>
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className="flex flex-col gap-2 rounded-md border bg-card p-2 shadow-sm sm:flex-row sm:items-center">
|
|
104
|
+
{topology && topologyIds?.searchBar ? (
|
|
105
|
+
<TopologyRegion as="div" className="w-full min-w-0 sm:max-w-3xl" id={topologyIds.searchBar} topology={topology}>
|
|
106
|
+
{searchBar}
|
|
107
|
+
</TopologyRegion>
|
|
108
|
+
) : (
|
|
109
|
+
searchBar
|
|
110
|
+
)}
|
|
111
|
+
<div className="flex flex-wrap items-center gap-2 sm:ml-auto">
|
|
112
|
+
{filters}
|
|
113
|
+
{filterMenu}
|
|
114
|
+
{topology && topologyIds?.columnFilter ? (
|
|
115
|
+
<TopologyRegion as="div" id={topologyIds.columnFilter} topology={topology}>
|
|
116
|
+
{columnFilter}
|
|
117
|
+
</TopologyRegion>
|
|
118
|
+
) : (
|
|
119
|
+
columnFilter
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { cn } from '@devxcrew/react-ui/lib/utils'
|
|
3
|
+
|
|
4
|
+
export type DataTableTotal = {
|
|
5
|
+
label: string
|
|
6
|
+
value: ReactNode
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function DataTableTotals({
|
|
10
|
+
className,
|
|
11
|
+
items,
|
|
12
|
+
}: {
|
|
13
|
+
className?: string
|
|
14
|
+
items: DataTableTotal[]
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
className={cn(
|
|
19
|
+
'grid gap-x-8 gap-y-2 rounded-md border bg-card px-4 py-3 text-sm shadow-sm sm:grid-cols-2 xl:grid-cols-4',
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
>
|
|
23
|
+
{items.map((item) => (
|
|
24
|
+
<div className="flex items-center justify-between gap-4" key={item.label}>
|
|
25
|
+
<span className="text-muted-foreground">{item.label}</span>
|
|
26
|
+
<strong className="font-semibold tabular-nums text-foreground">{item.value}</strong>
|
|
27
|
+
</div>
|
|
28
|
+
))}
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createDataTableColumnHelper,
|
|
3
|
+
dataTableFeatures,
|
|
4
|
+
DataTableBlock,
|
|
5
|
+
type DataTableBlockProps,
|
|
6
|
+
type DataTableColumn,
|
|
7
|
+
} from './data-table-block'
|
|
8
|
+
export { DataTableFilterMenu, type DataTableFilterOption } from './data-table-filter-menu'
|
|
9
|
+
export { DataTablePagination } from './data-table-pagination'
|
|
10
|
+
export { DataTableRowActions, type DataTableRowAction } from './data-table-row-actions'
|
|
11
|
+
export { DataTableStatus, type DataTableStatusTone } from './data-table-status'
|
|
12
|
+
export { DataTableToolbar, type DataTableColumnControl } from './data-table-toolbar'
|
|
13
|
+
export { DataTableTotals, type DataTableTotal } from './data-table-totals'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { WorkspaceActionCard } from './workspace-action-card'
|
|
2
|
+
export { WorkspaceMetricCard, WorkspaceMetricGrid } from './workspace-metric-card'
|
|
3
|
+
export type { WorkspaceMetricTone, WorkspaceMetricTrend } from './workspace-metric-card'
|
|
4
|
+
export { WorkspacePageHeader } from './workspace-page-header'
|
|
5
|
+
export { WorkspaceSectionCard } from './workspace-section-card'
|
|
6
|
+
export { WorkspaceEntityCard } from './workspace-entity-card'
|
|
7
|
+
export type { WorkspaceEntityCardProps } from './workspace-entity-card'
|
|
8
|
+
export { WorkspaceHealthSummary } from './workspace-health-summary'
|
|
9
|
+
export type { WorkspaceHealthSummaryProps } from './workspace-health-summary'
|
|
10
|
+
export { WorkspacePublishStatus } from './workspace-publish-status'
|
|
11
|
+
export type { WorkspacePublishState } from './workspace-publish-status'
|
|
12
|
+
export { WorkspaceRouteChecklist } from './workspace-route-checklist'
|
|
13
|
+
export type { WorkspaceRouteCheck } from './workspace-route-checklist'
|
|
14
|
+
export { WorkspaceRuntimeStatus } from './workspace-runtime-status'
|
|
15
|
+
export type { WorkspaceRuntimeState, WorkspaceRuntimeStatusProps } from './workspace-runtime-status'
|
|
16
|
+
export {
|
|
17
|
+
WorkspaceApplicationHeader,
|
|
18
|
+
type WorkspaceApplicationHeaderProps,
|
|
19
|
+
} from './workspace-application-header'
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { LucideIcon } from 'lucide-react'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import { Card, CardAction, CardDescription, CardHeader, CardTitle } from '../../components/card'
|
|
5
|
+
import { cn } from '../../lib/utils'
|
|
6
|
+
|
|
7
|
+
type WorkspaceActionCardProps = {
|
|
8
|
+
action?: ReactNode
|
|
9
|
+
className?: string
|
|
10
|
+
description?: ReactNode
|
|
11
|
+
icon: LucideIcon
|
|
12
|
+
iconClassName?: string
|
|
13
|
+
layout?: 'inline' | 'stacked'
|
|
14
|
+
title: ReactNode
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function WorkspaceActionCard({
|
|
18
|
+
action,
|
|
19
|
+
className,
|
|
20
|
+
description,
|
|
21
|
+
icon: Icon,
|
|
22
|
+
iconClassName,
|
|
23
|
+
layout = 'inline',
|
|
24
|
+
title,
|
|
25
|
+
}: WorkspaceActionCardProps) {
|
|
26
|
+
if (layout === 'stacked') {
|
|
27
|
+
return (
|
|
28
|
+
<Card
|
|
29
|
+
className={cn('h-full shadow-xs transition-colors hover:bg-muted/25', className)}
|
|
30
|
+
size="sm"
|
|
31
|
+
>
|
|
32
|
+
<CardHeader className="place-items-center text-center">
|
|
33
|
+
<span
|
|
34
|
+
className={cn(
|
|
35
|
+
'grid size-9 place-items-center rounded-lg bg-primary/10 text-primary',
|
|
36
|
+
iconClassName,
|
|
37
|
+
)}
|
|
38
|
+
>
|
|
39
|
+
<Icon className="size-4" />
|
|
40
|
+
</span>
|
|
41
|
+
<CardTitle>{title}</CardTitle>
|
|
42
|
+
{description ? (
|
|
43
|
+
<CardDescription className="leading-5">{description}</CardDescription>
|
|
44
|
+
) : null}
|
|
45
|
+
{action ? <CardAction>{action}</CardAction> : null}
|
|
46
|
+
</CardHeader>
|
|
47
|
+
</Card>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Card
|
|
53
|
+
className={cn('h-full shadow-xs transition-colors hover:bg-muted/25', className)}
|
|
54
|
+
size="sm"
|
|
55
|
+
>
|
|
56
|
+
<CardHeader className="grid grid-cols-[auto_1fr_auto] items-start gap-x-3">
|
|
57
|
+
<span
|
|
58
|
+
className={cn(
|
|
59
|
+
'row-span-2 grid size-9 place-items-center rounded-lg bg-primary/10 text-primary',
|
|
60
|
+
iconClassName,
|
|
61
|
+
)}
|
|
62
|
+
>
|
|
63
|
+
<Icon className="size-4" />
|
|
64
|
+
</span>
|
|
65
|
+
<CardTitle className="col-start-2">{title}</CardTitle>
|
|
66
|
+
{description ? (
|
|
67
|
+
<CardDescription className="col-start-2 leading-5">{description}</CardDescription>
|
|
68
|
+
) : null}
|
|
69
|
+
{action ? <CardAction className="col-start-3">{action}</CardAction> : null}
|
|
70
|
+
</CardHeader>
|
|
71
|
+
</Card>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { LucideIcon } from 'lucide-react'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/utils'
|
|
5
|
+
|
|
6
|
+
export type WorkspaceApplicationHeaderProps = {
|
|
7
|
+
applicationIcon?: LucideIcon
|
|
8
|
+
applicationLogoUrl?: string
|
|
9
|
+
applicationName: ReactNode
|
|
10
|
+
className?: string
|
|
11
|
+
end?: ReactNode
|
|
12
|
+
meta?: ReactNode
|
|
13
|
+
status?: ReactNode
|
|
14
|
+
showIdentity?: boolean
|
|
15
|
+
showWorkspaceTitle?: boolean
|
|
16
|
+
start?: ReactNode
|
|
17
|
+
title?: ReactNode
|
|
18
|
+
workspaceTitle?: ReactNode
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Title bar for the canvas below the global workspace menu. */
|
|
22
|
+
export function WorkspaceApplicationHeader({
|
|
23
|
+
applicationIcon: ApplicationIcon,
|
|
24
|
+
applicationLogoUrl,
|
|
25
|
+
applicationName,
|
|
26
|
+
className,
|
|
27
|
+
end,
|
|
28
|
+
meta,
|
|
29
|
+
status,
|
|
30
|
+
showIdentity = true,
|
|
31
|
+
showWorkspaceTitle = true,
|
|
32
|
+
start,
|
|
33
|
+
title,
|
|
34
|
+
workspaceTitle,
|
|
35
|
+
}: WorkspaceApplicationHeaderProps) {
|
|
36
|
+
return (
|
|
37
|
+
<header
|
|
38
|
+
aria-label="Workspace application header"
|
|
39
|
+
data-ui-component="WorkspaceApplicationHeader"
|
|
40
|
+
className={cn(
|
|
41
|
+
'sticky top-0 z-20 flex h-10 shrink-0 items-center justify-between gap-3 border-b border-border bg-background px-3 text-[11px] leading-none text-foreground',
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
45
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
46
|
+
{start}
|
|
47
|
+
{status}
|
|
48
|
+
{title ? <span className="truncate text-sm font-semibold">{title}</span> : null}
|
|
49
|
+
{showIdentity ? (
|
|
50
|
+
<>
|
|
51
|
+
{applicationLogoUrl ? (
|
|
52
|
+
<img alt="" className="size-3 shrink-0 rounded-sm object-contain" src={applicationLogoUrl} />
|
|
53
|
+
) : ApplicationIcon ? (
|
|
54
|
+
<ApplicationIcon aria-hidden="true" className="size-3 shrink-0" />
|
|
55
|
+
) : null}
|
|
56
|
+
<span className="truncate font-medium">{applicationName}</span>
|
|
57
|
+
</>
|
|
58
|
+
) : null}
|
|
59
|
+
</div>
|
|
60
|
+
<div className="flex min-w-0 shrink-0 items-center gap-3 text-xs text-muted-foreground">
|
|
61
|
+
{meta}
|
|
62
|
+
{showWorkspaceTitle ? <span className="max-w-[45vw] truncate tracking-[0.12em] uppercase">{workspaceTitle}</span> : null}
|
|
63
|
+
{end}
|
|
64
|
+
</div>
|
|
65
|
+
</header>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '../../components/card'
|
|
4
|
+
import { cn } from '../../lib/utils'
|
|
5
|
+
|
|
6
|
+
export type WorkspaceEntityCardProps = {
|
|
7
|
+
action?: ReactNode
|
|
8
|
+
children?: ReactNode
|
|
9
|
+
className?: string
|
|
10
|
+
description?: ReactNode
|
|
11
|
+
eyebrow?: ReactNode
|
|
12
|
+
footer?: ReactNode
|
|
13
|
+
title: ReactNode
|
|
14
|
+
variant?: 'default' | 'interactive'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function WorkspaceEntityCard({
|
|
18
|
+
action,
|
|
19
|
+
children,
|
|
20
|
+
className,
|
|
21
|
+
description,
|
|
22
|
+
eyebrow,
|
|
23
|
+
footer,
|
|
24
|
+
title,
|
|
25
|
+
variant = 'default',
|
|
26
|
+
}: WorkspaceEntityCardProps) {
|
|
27
|
+
return (
|
|
28
|
+
<Card
|
|
29
|
+
className={cn(
|
|
30
|
+
'h-full min-w-0 shadow-xs',
|
|
31
|
+
variant === 'interactive' ? 'transition-shadow hover:shadow-md' : null,
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
size="sm"
|
|
35
|
+
>
|
|
36
|
+
<CardHeader className="grid grid-cols-[1fr_auto] items-start gap-3">
|
|
37
|
+
<div className="min-w-0">
|
|
38
|
+
{eyebrow ? <div className="mb-2 text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase">{eyebrow}</div> : null}
|
|
39
|
+
<CardTitle>{title}</CardTitle>
|
|
40
|
+
{description ? <p className="mt-2 text-sm leading-6 text-muted-foreground">{description}</p> : null}
|
|
41
|
+
</div>
|
|
42
|
+
{action ? <div className="justify-self-end">{action}</div> : null}
|
|
43
|
+
</CardHeader>
|
|
44
|
+
{children ? <CardContent>{children}</CardContent> : null}
|
|
45
|
+
{footer ? <CardFooter className="mt-auto justify-between gap-3">{footer}</CardFooter> : null}
|
|
46
|
+
</Card>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LucideIcon } from 'lucide-react'
|
|
2
|
+
|
|
3
|
+
import { WorkspaceMetricCard, type WorkspaceMetricTone } from './workspace-metric-card'
|
|
4
|
+
|
|
5
|
+
export type WorkspaceHealthSummaryProps = {
|
|
6
|
+
description?: string
|
|
7
|
+
icon?: LucideIcon
|
|
8
|
+
label: string
|
|
9
|
+
tone?: WorkspaceMetricTone
|
|
10
|
+
value: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function WorkspaceHealthSummary({ description, icon, label, tone = 'neutral', value }: WorkspaceHealthSummaryProps) {
|
|
14
|
+
return <WorkspaceMetricCard description={description} icon={icon} label={label} size="compact" tone={tone} value={value} />
|
|
15
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ArrowDownRightIcon, ArrowRightIcon, ArrowUpRightIcon, type LucideIcon } from 'lucide-react'
|
|
2
|
+
import type { HTMLAttributes, ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import { Card, CardContent, CardHeader } from '../../components/card'
|
|
5
|
+
import { cn } from '../../lib/utils'
|
|
6
|
+
|
|
7
|
+
export type WorkspaceMetricTone = 'accent' | 'danger' | 'neutral' | 'success' | 'warning'
|
|
8
|
+
export type WorkspaceMetricTrend = {
|
|
9
|
+
direction: 'down' | 'flat' | 'up'
|
|
10
|
+
label: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type WorkspaceMetricCardProps = {
|
|
14
|
+
className?: string
|
|
15
|
+
description?: ReactNode
|
|
16
|
+
icon?: LucideIcon
|
|
17
|
+
label: ReactNode
|
|
18
|
+
size?: 'compact' | 'default'
|
|
19
|
+
tone?: WorkspaceMetricTone
|
|
20
|
+
trend?: WorkspaceMetricTrend
|
|
21
|
+
value: ReactNode
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const iconToneClasses: Record<WorkspaceMetricTone, string> = {
|
|
25
|
+
accent: 'bg-primary/10 text-primary',
|
|
26
|
+
danger: 'bg-destructive/10 text-destructive',
|
|
27
|
+
neutral: 'bg-muted text-muted-foreground',
|
|
28
|
+
success: 'bg-success/10 text-success',
|
|
29
|
+
warning: 'bg-warning/15 text-warning-foreground',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const trendIcons: Record<WorkspaceMetricTrend['direction'], LucideIcon> = {
|
|
33
|
+
down: ArrowDownRightIcon,
|
|
34
|
+
flat: ArrowRightIcon,
|
|
35
|
+
up: ArrowUpRightIcon,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const trendClasses: Record<WorkspaceMetricTrend['direction'], string> = {
|
|
39
|
+
down: 'text-destructive',
|
|
40
|
+
flat: 'text-muted-foreground',
|
|
41
|
+
up: 'text-success',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function WorkspaceMetricCard({
|
|
45
|
+
className,
|
|
46
|
+
description,
|
|
47
|
+
icon: Icon,
|
|
48
|
+
label,
|
|
49
|
+
size = 'default',
|
|
50
|
+
tone = 'neutral',
|
|
51
|
+
trend,
|
|
52
|
+
value,
|
|
53
|
+
}: WorkspaceMetricCardProps) {
|
|
54
|
+
const TrendIcon = trend ? trendIcons[trend.direction] : null
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Card className={cn('min-w-0 shadow-xs', size === 'compact' ? 'py-2' : null, className)} size="sm">
|
|
58
|
+
<CardHeader className={cn('flex-row items-start justify-between gap-3', size === 'compact' ? 'px-3' : null)}>
|
|
59
|
+
<div className={cn('min-w-0 font-medium text-muted-foreground', size === 'compact' ? 'text-xs' : 'text-sm')}>{label}</div>
|
|
60
|
+
{Icon ? (
|
|
61
|
+
<span
|
|
62
|
+
className={cn(
|
|
63
|
+
'grid shrink-0 place-items-center rounded-lg',
|
|
64
|
+
size === 'compact' ? 'size-7' : 'size-8',
|
|
65
|
+
iconToneClasses[tone],
|
|
66
|
+
)}
|
|
67
|
+
>
|
|
68
|
+
<Icon className="size-4" />
|
|
69
|
+
</span>
|
|
70
|
+
) : null}
|
|
71
|
+
</CardHeader>
|
|
72
|
+
<CardContent className={cn('grid gap-2', size === 'compact' ? 'px-3 gap-1' : null)}>
|
|
73
|
+
<strong className={cn('font-semibold tracking-tight tabular-nums', size === 'compact' ? 'text-xl' : 'text-2xl')}>{value}</strong>
|
|
74
|
+
<div className="flex min-h-5 items-center gap-2 text-xs">
|
|
75
|
+
{trend && TrendIcon ? (
|
|
76
|
+
<span
|
|
77
|
+
className={cn(
|
|
78
|
+
'inline-flex items-center gap-1 font-medium',
|
|
79
|
+
trendClasses[trend.direction],
|
|
80
|
+
)}
|
|
81
|
+
>
|
|
82
|
+
<TrendIcon className="size-3.5" />
|
|
83
|
+
{trend.label}
|
|
84
|
+
</span>
|
|
85
|
+
) : null}
|
|
86
|
+
{description ? (
|
|
87
|
+
<span className="truncate text-muted-foreground">{description}</span>
|
|
88
|
+
) : null}
|
|
89
|
+
</div>
|
|
90
|
+
</CardContent>
|
|
91
|
+
</Card>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function WorkspaceMetricGrid({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
|
|
96
|
+
return <div className={cn('grid gap-4 sm:grid-cols-2 xl:grid-cols-4', className)} {...props} />
|
|
97
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../lib/utils'
|
|
4
|
+
|
|
5
|
+
type WorkspacePageHeaderProps = {
|
|
6
|
+
actions?: ReactNode
|
|
7
|
+
badge?: ReactNode
|
|
8
|
+
className?: string
|
|
9
|
+
description?: ReactNode
|
|
10
|
+
eyebrow?: ReactNode
|
|
11
|
+
title: ReactNode
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function WorkspacePageHeader({
|
|
15
|
+
actions,
|
|
16
|
+
badge,
|
|
17
|
+
className,
|
|
18
|
+
description,
|
|
19
|
+
eyebrow,
|
|
20
|
+
title,
|
|
21
|
+
}: WorkspacePageHeaderProps) {
|
|
22
|
+
return (
|
|
23
|
+
<header
|
|
24
|
+
className={cn('flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between', className)}
|
|
25
|
+
>
|
|
26
|
+
<div className="min-w-0 max-w-3xl">
|
|
27
|
+
{eyebrow ? (
|
|
28
|
+
<div className="pb-2 text-xs font-semibold tracking-widest text-primary uppercase">
|
|
29
|
+
{eyebrow}
|
|
30
|
+
</div>
|
|
31
|
+
) : null}
|
|
32
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
33
|
+
<h1 className="text-2xl font-semibold tracking-tight sm:text-3xl">{title}</h1>
|
|
34
|
+
{badge}
|
|
35
|
+
</div>
|
|
36
|
+
{description ? (
|
|
37
|
+
<p className="pt-2 text-sm leading-6 text-muted-foreground">{description}</p>
|
|
38
|
+
) : null}
|
|
39
|
+
</div>
|
|
40
|
+
{actions ? <div className="flex shrink-0 flex-wrap items-center gap-2">{actions}</div> : null}
|
|
41
|
+
</header>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CheckCircle2Icon, CircleAlertIcon, CircleDashedIcon } from 'lucide-react'
|
|
2
|
+
|
|
3
|
+
import { Badge } from '../../components/badge'
|
|
4
|
+
|
|
5
|
+
export type WorkspacePublishState = 'draft' | 'published' | 'review'
|
|
6
|
+
|
|
7
|
+
export function WorkspacePublishStatus({ state }: { state: WorkspacePublishState }) {
|
|
8
|
+
const config = {
|
|
9
|
+
draft: { icon: CircleDashedIcon, label: 'Draft', variant: 'secondary' as const },
|
|
10
|
+
published: { icon: CheckCircle2Icon, label: 'Published', variant: 'studio-success' as const },
|
|
11
|
+
review: { icon: CircleAlertIcon, label: 'Needs review', variant: 'studio-warning' as const },
|
|
12
|
+
}[state]
|
|
13
|
+
const Icon = config.icon
|
|
14
|
+
return <Badge aria-label={`Publish status: ${config.label}`} variant={config.variant}><Icon aria-hidden="true" />{config.label}</Badge>
|
|
15
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CheckCircle2Icon, CircleAlertIcon } from 'lucide-react'
|
|
2
|
+
|
|
3
|
+
import { Badge } from '../../components/badge'
|
|
4
|
+
import { Card, CardContent } from '../../components/card'
|
|
5
|
+
|
|
6
|
+
export type WorkspaceRouteCheck = { label: string; status: 'blocked' | 'ready' | 'pending' }
|
|
7
|
+
|
|
8
|
+
export function WorkspaceRouteChecklist({ items }: { items: WorkspaceRouteCheck[] }) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
11
|
+
{items.map((item) => {
|
|
12
|
+
const ready = item.status === 'ready'
|
|
13
|
+
const Icon = ready ? CheckCircle2Icon : CircleAlertIcon
|
|
14
|
+
return (
|
|
15
|
+
<Card key={item.label} size="sm" className="shadow-none">
|
|
16
|
+
<CardContent className="flex items-center gap-3 p-3 text-sm">
|
|
17
|
+
<Icon aria-hidden="true" className={`size-4 ${ready ? 'text-emerald-500' : 'text-amber-500'}`} />
|
|
18
|
+
<span className="flex-1">{item.label}</span>
|
|
19
|
+
<Badge variant={ready ? 'studio-success' : item.status === 'pending' ? 'studio-warning' : 'secondary'}>
|
|
20
|
+
{ready ? 'Ready' : item.status === 'pending' ? 'Pending' : 'Blocked'}
|
|
21
|
+
</Badge>
|
|
22
|
+
</CardContent>
|
|
23
|
+
</Card>
|
|
24
|
+
)
|
|
25
|
+
})}
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CheckCircle2Icon, CircleAlertIcon, CircleOffIcon, type LucideIcon } from 'lucide-react'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import { Badge } from '../../components/badge'
|
|
5
|
+
import { cn } from '../../lib/utils'
|
|
6
|
+
|
|
7
|
+
export type WorkspaceRuntimeState = 'degraded' | 'live' | 'starting' | 'stopped'
|
|
8
|
+
|
|
9
|
+
export type WorkspaceRuntimeStatusProps = {
|
|
10
|
+
action?: ReactNode
|
|
11
|
+
checkedAt?: string
|
|
12
|
+
className?: string
|
|
13
|
+
httpStatus?: number | null
|
|
14
|
+
port?: number
|
|
15
|
+
responseTimeMs?: number | null
|
|
16
|
+
state: WorkspaceRuntimeState
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const stateConfig: Record<WorkspaceRuntimeState, { icon: LucideIcon; label: string; variant: 'secondary' | 'studio-success' | 'studio-warning' }> = {
|
|
20
|
+
degraded: { icon: CircleAlertIcon, label: 'Degraded', variant: 'studio-warning' },
|
|
21
|
+
live: { icon: CheckCircle2Icon, label: 'Live', variant: 'studio-success' },
|
|
22
|
+
starting: { icon: CircleAlertIcon, label: 'Starting', variant: 'studio-warning' },
|
|
23
|
+
stopped: { icon: CircleOffIcon, label: 'Not running', variant: 'secondary' },
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function WorkspaceRuntimeStatus({ action, checkedAt, className, httpStatus, port, responseTimeMs, state }: WorkspaceRuntimeStatusProps) {
|
|
27
|
+
const config = stateConfig[state]
|
|
28
|
+
const Icon = config.icon
|
|
29
|
+
const detail = state === 'live'
|
|
30
|
+
? [port ? `:${port}` : null, responseTimeMs == null ? null : `${responseTimeMs}ms`].filter(Boolean).join(' · ')
|
|
31
|
+
: state === 'degraded' && httpStatus
|
|
32
|
+
? `HTTP ${httpStatus}`
|
|
33
|
+
: null
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div aria-live="polite" className={cn('flex min-w-0 items-center justify-between gap-3', className)}>
|
|
37
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
38
|
+
<Badge aria-label={`Runtime status: ${config.label}`} variant={config.variant}>
|
|
39
|
+
<Icon aria-hidden="true" />
|
|
40
|
+
{config.label}
|
|
41
|
+
</Badge>
|
|
42
|
+
{detail ? <span className="truncate text-xs text-muted-foreground">{detail}</span> : null}
|
|
43
|
+
{checkedAt ? <span className="sr-only">Last checked {checkedAt}</span> : null}
|
|
44
|
+
</div>
|
|
45
|
+
{action}
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Card,
|
|
5
|
+
CardAction,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardFooter,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
} from '../../components/card'
|
|
12
|
+
import { cn } from '../../lib/utils'
|
|
13
|
+
|
|
14
|
+
type WorkspaceSectionCardProps = {
|
|
15
|
+
action?: ReactNode
|
|
16
|
+
children: ReactNode
|
|
17
|
+
className?: string
|
|
18
|
+
description?: ReactNode
|
|
19
|
+
footer?: ReactNode
|
|
20
|
+
title: ReactNode
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function WorkspaceSectionCard({
|
|
24
|
+
action,
|
|
25
|
+
children,
|
|
26
|
+
className,
|
|
27
|
+
description,
|
|
28
|
+
footer,
|
|
29
|
+
title,
|
|
30
|
+
}: WorkspaceSectionCardProps) {
|
|
31
|
+
return (
|
|
32
|
+
<Card className={cn('min-w-0 shadow-xs', className)}>
|
|
33
|
+
<CardHeader className="border-b">
|
|
34
|
+
<CardTitle>{title}</CardTitle>
|
|
35
|
+
{description ? <CardDescription>{description}</CardDescription> : null}
|
|
36
|
+
{action ? <CardAction>{action}</CardAction> : null}
|
|
37
|
+
</CardHeader>
|
|
38
|
+
<CardContent>{children}</CardContent>
|
|
39
|
+
{footer ? <CardFooter>{footer}</CardFooter> : null}
|
|
40
|
+
</Card>
|
|
41
|
+
)
|
|
42
|
+
}
|