@gravitee/graphene-core 3.25.0 → 3.26.0-feat-educational-landing.e5cd2b7
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/USAGE_GUIDE.md +188 -34
- package/dist/composed/DataTable/DataTable.d.ts +14 -1
- package/dist/composed/DataTable/DataTable.d.ts.map +1 -1
- package/dist/composed/EducationalLanding/EducationalLanding.d.ts +62 -0
- package/dist/composed/EducationalLanding/EducationalLanding.d.ts.map +1 -0
- package/dist/composed/EducationalLanding/EducationalLandingComparison.d.ts +29 -0
- package/dist/composed/EducationalLanding/EducationalLandingComparison.d.ts.map +1 -0
- package/dist/composed/EducationalLanding/EducationalLandingFlow.d.ts +42 -0
- package/dist/composed/EducationalLanding/EducationalLandingFlow.d.ts.map +1 -0
- package/dist/composed/EducationalLanding/EducationalLandingPanel.d.ts +37 -0
- package/dist/composed/EducationalLanding/EducationalLandingPanel.d.ts.map +1 -0
- package/dist/composed/EducationalLanding/EducationalLandingPillar.d.ts +26 -0
- package/dist/composed/EducationalLanding/EducationalLandingPillar.d.ts.map +1 -0
- package/dist/composed/EducationalLanding/EducationalLandingPillars.d.ts +31 -0
- package/dist/composed/EducationalLanding/EducationalLandingPillars.d.ts.map +1 -0
- package/dist/composed/EducationalLanding/index.d.ts +13 -0
- package/dist/composed/EducationalLanding/index.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8232 -8010
- package/dist/styles/globals.css +1 -1
- package/package.json +4 -1
- package/snippets/educational-landing/EntitiesLanding.tsx +89 -0
- package/snippets/educational-landing/EntitiesLandingPage.tsx +108 -0
- package/snippets/educational-landing/snippet.json +5 -0
- package/snippets/list-page/DeleteEntityDialog.tsx +71 -0
- package/snippets/list-page/EntitiesPage.tsx +221 -0
- package/snippets/list-page/EntityActionsMenu.tsx +69 -0
- package/snippets/list-page/entities.service.ts +108 -0
- package/snippets/list-page/entityColumns.tsx +79 -0
- package/snippets/list-page/snippet.json +6 -0
- package/snippets/list-page/useEntityList.ts +142 -0
- package/snippets/data-table-list-page.tsx +0 -316
package/USAGE_GUIDE.md
CHANGED
|
@@ -482,7 +482,7 @@ Follow these steps to apply the layout width system to an existing consumer modu
|
|
|
482
482
|
4. **Remove ad-hoc width constraints** — delete any `max-w-*` classes on page-level wrapper divs. The design system owns content width.
|
|
483
483
|
5. **Verify** — resize the browser to confirm content stays centered without clipping on narrow viewports.
|
|
484
484
|
|
|
485
|
-
See `node_modules/@gravitee/graphene-core/snippets/
|
|
485
|
+
See the folder `node_modules/@gravitee/graphene-core/snippets/list-page/` and `creation-wizard-page.tsx` for complete examples.
|
|
486
486
|
|
|
487
487
|
### Page header
|
|
488
488
|
|
|
@@ -602,11 +602,137 @@ export function ApisPage() {
|
|
|
602
602
|
- Leave the placeholder as the only name: it disappears as soon as the user types. `aria-label` says what is searched.
|
|
603
603
|
- Give the field a fixed width in a shared wrapper: the page sets it with `className` where the field lives, `w-64` in a toolbar, nothing in a full-width column.
|
|
604
604
|
|
|
605
|
+
### Educational landing
|
|
606
|
+
|
|
607
|
+
A flagship page opens, on first use, with `EducationalLanding`: a card that asks why the entity exists ("Why create an LLM proxy?"), answers in two sentences, compares life without and with it in two tinted panels, and ends with its key capabilities. Use it when the entity is a product concept worth explaining (proxies, clusters, applications, alerts). The page renders it **instead of** the table once the list query has settled on an empty list, never while it loads, and keeps its `PageHeader` with the Add button above it. The same card can also show **on demand** above a populated list, with a dismiss button.
|
|
608
|
+
|
|
609
|
+
Do not use it for the empty state of a secondary list (`DataTableEmptyState variant="first-use"`), for a search or filter with no results (`variant="no-results"`), or on the home page.
|
|
610
|
+
|
|
611
|
+
The page composes the card from six parts and chooses their order; no part takes a style variant.
|
|
612
|
+
|
|
613
|
+
| Part | Props | Renders |
|
|
614
|
+
| --- | --- | --- |
|
|
615
|
+
| `EducationalLanding` | `heading`, `description`, `onDismiss?`, `className?`, `children` | The card, as a `<section>` named by its `<h2>` heading; the answer; when `onDismiss` is set, a dismiss button that hides the card and calls it; the children stacked with a rule between two |
|
|
616
|
+
| `EducationalLandingComparison` | `children`, `className?` | Two `Panel` side by side once the card's content is 44rem wide, the arrow between them; stacked below |
|
|
617
|
+
| `EducationalLandingPanel` | `tone` (`'before'` \| `'after'`), `label`, `points`, `children?`, `className?` | A tinted box: the label (`<h3>`), the free content (a `Flow`), the ✗ or ✓ points |
|
|
618
|
+
| `EducationalLandingFlow` | `steps` (`{ icon, label, emphasized?, multi? }[]`), `accent?` (`'primary'` \| `'highlight'`), `className?` | An ordered list of icon steps joined by arrows, stacked vertically when one row does not fit; `emphasized` boxes the entity, `multi` stacks three chips for a 1 to n step |
|
|
619
|
+
| `EducationalLandingPillars` | `columns?` (2, 3 or 4, default 3), `children`, `className?` | A grid list, one column in a narrow card |
|
|
620
|
+
| `EducationalLandingPillar` | `icon`, `title`, `description`, `className?` | An icon chip, a title (`<h3>`), one sentence |
|
|
621
|
+
|
|
622
|
+
A `Panel` or a `Flow` alone is a valid part, and two `Comparison` in a row simply stack. The parts lay out on the card's width, not the viewport's: the card sits next to the sidebar. The canonical composition, the one the snippet reproduces:
|
|
623
|
+
|
|
624
|
+
```tsx
|
|
625
|
+
import {
|
|
626
|
+
Button,
|
|
627
|
+
EducationalLanding,
|
|
628
|
+
EducationalLandingComparison,
|
|
629
|
+
EducationalLandingFlow,
|
|
630
|
+
EducationalLandingPanel,
|
|
631
|
+
EducationalLandingPillar,
|
|
632
|
+
EducationalLandingPillars,
|
|
633
|
+
PageHeader,
|
|
634
|
+
} from '@gravitee/graphene-core';
|
|
635
|
+
import {
|
|
636
|
+
BarChart3Icon,
|
|
637
|
+
GlobeIcon,
|
|
638
|
+
MonitorIcon,
|
|
639
|
+
PlusIcon,
|
|
640
|
+
ServerIcon,
|
|
641
|
+
ShieldIcon,
|
|
642
|
+
WaypointsIcon,
|
|
643
|
+
} from '@gravitee/graphene-core/icons';
|
|
644
|
+
import { Link } from 'react-router-dom';
|
|
645
|
+
|
|
646
|
+
export function LlmProxiesLandingPage() {
|
|
647
|
+
// Your module's permission check. Gamma modules use `useHasPermission` from `@gravitee/gamma-modules-sdk`.
|
|
648
|
+
const canCreate = true;
|
|
649
|
+
|
|
650
|
+
return (
|
|
651
|
+
<div className="flex flex-col gap-6">
|
|
652
|
+
<PageHeader
|
|
653
|
+
title="LLM Proxies"
|
|
654
|
+
description="Smart routing and governance for AI model requests"
|
|
655
|
+
actions={
|
|
656
|
+
canCreate && (
|
|
657
|
+
<Button asChild>
|
|
658
|
+
<Link to="new">
|
|
659
|
+
<PlusIcon />
|
|
660
|
+
Create LLM proxy
|
|
661
|
+
</Link>
|
|
662
|
+
</Button>
|
|
663
|
+
)
|
|
664
|
+
}
|
|
665
|
+
/>
|
|
666
|
+
<EducationalLanding
|
|
667
|
+
heading="Why create an LLM proxy?"
|
|
668
|
+
description="An LLM proxy puts the AI Gateway between your applications and the model providers, so every model request goes through one place you control."
|
|
669
|
+
>
|
|
670
|
+
<EducationalLandingComparison>
|
|
671
|
+
<EducationalLandingPanel
|
|
672
|
+
tone="before"
|
|
673
|
+
label="Direct model calls"
|
|
674
|
+
points={['Each app wired to its own provider', 'Provider API keys spread across every app', 'No record of who sent which request']}
|
|
675
|
+
>
|
|
676
|
+
<EducationalLandingFlow
|
|
677
|
+
steps={[
|
|
678
|
+
{ icon: MonitorIcon, label: 'App' },
|
|
679
|
+
{ icon: ServerIcon, label: 'Model provider' },
|
|
680
|
+
]}
|
|
681
|
+
/>
|
|
682
|
+
</EducationalLandingPanel>
|
|
683
|
+
<EducationalLandingPanel
|
|
684
|
+
tone="after"
|
|
685
|
+
label="With an LLM proxy"
|
|
686
|
+
points={['One endpoint for every model provider', 'Provider API keys kept in the gateway', 'Every request logged with its tokens and cost']}
|
|
687
|
+
>
|
|
688
|
+
<EducationalLandingFlow
|
|
689
|
+
steps={[
|
|
690
|
+
{ icon: MonitorIcon, label: 'App' },
|
|
691
|
+
{ icon: GlobeIcon, label: 'LLM proxy', emphasized: true },
|
|
692
|
+
{ icon: ServerIcon, label: 'Model providers' },
|
|
693
|
+
]}
|
|
694
|
+
/>
|
|
695
|
+
</EducationalLandingPanel>
|
|
696
|
+
</EducationalLandingComparison>
|
|
697
|
+
<EducationalLandingPillars>
|
|
698
|
+
<EducationalLandingPillar icon={WaypointsIcon} title="Routing" description="Pick the model for each request by cost, latency, load or semantic fit." />
|
|
699
|
+
<EducationalLandingPillar icon={BarChart3Icon} title="Cost control" description="Budgets, rate limits and spend alerts keep each application's LLM costs predictable." />
|
|
700
|
+
<EducationalLandingPillar icon={ShieldIcon} title="Resilience" description="Health-aware backends take a failing provider out of rotation until it recovers." />
|
|
701
|
+
</EducationalLandingPillars>
|
|
702
|
+
</EducationalLanding>
|
|
703
|
+
</div>
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
The page decides between the landing and the table once the list query has settled, never from the previous page kept while the next one loads (`isPlaceholderData`): `settled && totalCount === 0 && !hasCriteria` renders the landing, anything else renders the table with its own loading, error and no-results states. `useEntityList` in the list page snippet returns that `settled` flag. Deciding on `isSuccess` alone shows the landing for a moment when a search with no results is cleared.
|
|
709
|
+
|
|
710
|
+
**On demand.** Above a populated list, pass `onDismiss`: the card shows a dismiss button, and a click hides the card, then calls `onDismiss`. A reload, or the page rendering the card again, shows it again. To reopen it without a reload, the page keeps a `showInfo` state and a toggle in `PageHeader` `actions` (an icon button with an `aria-label` and `aria-pressed={showInfo}`), renders the landing when `showInfo` is true, and passes `onDismiss={() => { setShowInfo(false); toggleRef.current?.focus(); }}` so focus goes back to the toggle. Remembering the dismissal (localStorage) stays in the module.
|
|
711
|
+
|
|
712
|
+
Copy-paste starting point: `node_modules/@gravitee/graphene-core/snippets/educational-landing/` (`npx graphene-sync-ai-rules` copies it to `.claude/graphene-snippets/` and `.cursor/rules/graphene-snippets/`), the page and the card in two files, with the on-demand variant as a commented addendum. Storybook: **Patterns/Educational Landing** renders the snippet's page, **Composed/EducationalLanding** shows the card and the layouts its parts allow.
|
|
713
|
+
|
|
714
|
+
**Do not:**
|
|
715
|
+
|
|
716
|
+
- Put a button or a link in the card besides the dismiss button: the Add button stays in `PageHeader`, in every state.
|
|
717
|
+
- Use it as an empty state: not for a secondary list, not for a search with no results.
|
|
718
|
+
- Render more than one per page, or change its heading: it is always an `<h2>` under the page's `<h1>`.
|
|
719
|
+
- Tint or restyle it through `className`: no custom color, no shadow. `className` is for the page's layout.
|
|
720
|
+
- Build a flow by hand, icons and arrows in a `div`: pass `steps` to `EducationalLandingFlow`.
|
|
721
|
+
- Repeat a ✓ point as a pillar: the points say what changes for the user, the pillars name the capabilities behind it.
|
|
722
|
+
|
|
723
|
+
**Migrating a landing from the field:**
|
|
724
|
+
|
|
725
|
+
| Today | With the parts |
|
|
726
|
+
| --- | --- |
|
|
727
|
+
| aim `EducationalEmptyState` | `title`, `tagline`, `cta` → `PageHeader` `title`, `description`, `actions`; `why.question`, `why.answer` → `heading`, `description`; `without`, `withCatalog` → `Panel tone="before"`, `Panel tone="after"` in a `Comparison` (`bullets` → `points`, `flow` → `Flow` steps); `pillars` → `Pillars`; `FlowNode` `emphasized` → the step's `emphasized`, with `accent="highlight"` to keep the violet |
|
|
728
|
+
| esm `EducationalLanding` (`LandingConfig`) | `heading`, `description` → the same props; `without`, `with` → two `Panel` in a `Comparison` (`flow` → `Flow` steps, `highlighted` → `emphasized`, `multi` unchanged); `features` → `Pillars`; `whatIs` → a block of the module's own, first child of the card; `onDismiss` unchanged, `useDismissed` stays in the module |
|
|
729
|
+
| apim and platform hand-written landings | The card → `EducationalLanding`, its "Why …?" line → `heading`; tinted boxes → `Panel`; icon rows → `Flow`; feature tiles → `Pillars`; an extra diagram or comparison stays a child block of the card |
|
|
730
|
+
|
|
605
731
|
### Data table (entity list pages)
|
|
606
732
|
|
|
607
733
|
Use `DataTable` for any entity list — whether the data is fetched page-by-page from an API or loaded in full on the client. The component provides sorting, filtering, pagination, column visibility, row selection, and bulk actions through a composable slot API.
|
|
608
734
|
|
|
609
|
-
**To implement:** copy the
|
|
735
|
+
**To implement:** copy the folder `node_modules/@gravitee/graphene-core/snippets/list-page/` into your module, rename `Entity` and `entities` after your resource, and point `useEntityList.ts` at your API client instead of the sample `entities.service.ts`. Its `snippet.json` lists the packages it needs beyond Graphene's peers (`@tanstack/react-query`, `react-router-dom`). See Storybook **Patterns/List Page** for the page in its five states.
|
|
610
736
|
|
|
611
737
|
#### When to use DataTable vs raw Table
|
|
612
738
|
|
|
@@ -656,11 +782,12 @@ Use `DataTable` for any entity list — whether the data is fetched page-by-page
|
|
|
656
782
|
|
|
657
783
|
#### Search and filters
|
|
658
784
|
|
|
659
|
-
- Search and filters live in the DataTable `toolbar` slot.
|
|
660
|
-
- Search: `SearchField`
|
|
661
|
-
- **Do NOT add a search field to a server-paginated table unless the backend endpoint supports a text search parameter** (`query`, `q`, or equivalent). Client-side search on a single page of server-paginated data is misleading
|
|
662
|
-
-
|
|
663
|
-
-
|
|
785
|
+
- Search and filters live in the DataTable `toolbar` slot, inside `<div className="flex min-w-0 flex-wrap items-center gap-2">`: DataTable's own toolbar row does not wrap, this container does. Group the filters and Reset in a nested `flex flex-wrap items-center gap-2` so that they wrap together.
|
|
786
|
+
- Search: `SearchField` first, with an `aria-label` that says what is searched, a placeholder, and `className="w-64"`. It debounces 300 ms and reports the settled query: reset the page to 1 in `onValueChange`.
|
|
787
|
+
- **Do NOT add a search field to a server-paginated table unless the backend endpoint supports a text search parameter** (`query`, `q`, or equivalent). Client-side search on a single page of server-paginated data is misleading: users will miss matches on other pages. If the backend does not support search, either request the backend change or omit the search field entirely.
|
|
788
|
+
- Single-choice filter: a `Select` whose default option is "All ..." ("All statuses"), named by a `Label` in `sr-only`, with a minimum width (`min-w-32`) so that it does not jump as its value changes.
|
|
789
|
+
- Multiple-choice filter: a `FacetedFilter`.
|
|
790
|
+
- Reset: a `Button variant="ghost" size="sm"`, rendered only while a criterion is active. It clears every criterion and the page, then moves the focus to the search field (pass a `ref` to `SearchField`).
|
|
664
791
|
|
|
665
792
|
#### Row navigation
|
|
666
793
|
|
|
@@ -675,6 +802,9 @@ Use **name-column-as-link**: the primary identifier column (always leftmost) is
|
|
|
675
802
|
- **Quick-action escape hatch:** one high-discoverability action (e.g. "Deploy") may appear as an inline icon *alongside* the dropdown, but the dropdown must still exist.
|
|
676
803
|
- Use `MoreVerticalIcon` (⋮) for table rows. Do not use `MoreHorizontalIcon` — the horizontal variant is for toolbars and card headers.
|
|
677
804
|
- Destructive actions go last in the menu, after a `DropdownMenuSeparator`.
|
|
805
|
+
- Name the ⋮ button after its row: ``aria-label={`Actions for ${entity.name}`}``.
|
|
806
|
+
- A destructive action asks for confirmation in a `Dialog` before it runs, then reports the outcome in a toast (`toast` from `@gravitee/graphene-core`, shown by the app's `Toaster`). When the dialog closes, give the focus back to the ⋮ if the user cancelled. Once the entity is deleted, its row and ⋮ go: move the focus to the search field, as Reset does. See `EntityActionsMenu.tsx` and `DeleteEntityDialog.tsx` in the list page snippet.
|
|
807
|
+
- Key the rows by entity (`tableOptions={{ getRowId: (entity) => entity.id }}`), so that a removed row never hands its focused ⋮ to the next entity. Not with `selectionMode="multi"` for now: `onSelectionChange` still maps the selection by row index.
|
|
678
808
|
|
|
679
809
|
#### Column ordering
|
|
680
810
|
|
|
@@ -688,7 +818,9 @@ Use **name-column-as-link**: the primary identifier column (always leftmost) is
|
|
|
688
818
|
|
|
689
819
|
**Minimum 3 data columns** (excluding actions). If an entity has fewer than 3 meaningful attributes, use a simpler component (Item list, card grid) instead of DataTable.
|
|
690
820
|
|
|
691
|
-
Target **4-7 visible columns**.
|
|
821
|
+
Target **4-7 visible columns**. List pages show the View menu (`enableColumnVisibility`), with the name and the actions always visible (`enableHiding: false`); more than 7 → hide the low-priority columns by default. Do NOT show raw entity IDs as visible columns.
|
|
822
|
+
|
|
823
|
+
`enableColumnVisibility` adds the View menu, which names each column by its `meta.label`, else by its header when that is text: give a column whose header is a component, such as a sortable `DataTableColumnHeader`, a `meta: { label: 'Created' }`, or the menu shows its id.
|
|
692
824
|
|
|
693
825
|
#### Cell renderers
|
|
694
826
|
|
|
@@ -704,56 +836,61 @@ Import from `@gravitee/graphene-core`:
|
|
|
704
836
|
|
|
705
837
|
#### Empty states
|
|
706
838
|
|
|
707
|
-
|
|
839
|
+
A flagship list renders `EducationalLanding` instead of the first-use empty state: see [Educational landing](#educational-landing).
|
|
840
|
+
|
|
841
|
+
Use `DataTableEmptyState` for both cases, and decide only once the query has settled: never conclude "empty" from the previous page kept while the next one loads (`isPlaceholderData`).
|
|
708
842
|
|
|
709
|
-
**`variant="first-use"
|
|
710
|
-
- Render **instead of** `DataTable
|
|
711
|
-
-
|
|
712
|
-
-
|
|
713
|
-
- Decision: `totalCount === 0 && !
|
|
843
|
+
**`variant="first-use"`**: the collection is empty and no criterion is active.
|
|
844
|
+
- Render it **instead of** `DataTable`, wrapped in `<div className="rounded-lg border">`.
|
|
845
|
+
- In a list page: an icon, a title, one sentence, and **no button**. The header's Add button is the one creation CTA (see below).
|
|
846
|
+
- `primaryAction`, `secondaryAction` and `children` exist for other surfaces. The first use of a flagship page is not one of them: it renders `EducationalLanding`, a card without a button (see [Educational landing](#educational-landing)).
|
|
847
|
+
- Decision: `totalCount === 0 && !hasCriteria`.
|
|
714
848
|
|
|
715
|
-
**`variant="no-results"
|
|
716
|
-
- Render **inside** `DataTable` as
|
|
717
|
-
-
|
|
718
|
-
-
|
|
849
|
+
**`variant="no-results"`**: a search or a filter matched nothing.
|
|
850
|
+
- Render it **inside** `DataTable` as `emptyMessage`, so the toolbar stays.
|
|
851
|
+
- `icon={<SearchIcon />}`, a title ("No APIs found"), one sentence inviting to adjust the search or the filters, and **no button**: the search field's clear button and the toolbar's Reset clear the criteria.
|
|
852
|
+
- Its `action` slot exists for the rare table without a Reset in its toolbar.
|
|
853
|
+
- Decision: `totalCount === 0 && hasCriteria`.
|
|
719
854
|
|
|
720
855
|
```tsx
|
|
721
|
-
// Page-level decision
|
|
856
|
+
// Page-level decision, once the query has settled
|
|
722
857
|
{isFirstUse ? (
|
|
723
858
|
<div className="rounded-lg border">
|
|
724
|
-
<DataTableEmptyState variant="first-use" icon={<GlobeIcon />} title="No APIs yet" ... />
|
|
859
|
+
<DataTableEmptyState variant="first-use" icon={<GlobeIcon />} title="No APIs yet" description="..." />
|
|
725
860
|
</div>
|
|
726
861
|
) : (
|
|
727
862
|
<DataTable
|
|
728
|
-
emptyMessage={<DataTableEmptyState variant="no-results" icon={<SearchIcon />} ... />}
|
|
863
|
+
emptyMessage={<DataTableEmptyState variant="no-results" icon={<SearchIcon />} title="No APIs found" description="..." />}
|
|
729
864
|
...
|
|
730
865
|
/>
|
|
731
866
|
)}
|
|
732
867
|
```
|
|
733
868
|
|
|
734
|
-
See Storybook **
|
|
869
|
+
See Storybook **Patterns/List Page** (states `empty` and `no-results`) for the full interactive example.
|
|
735
870
|
|
|
736
871
|
#### Header button coordination
|
|
737
872
|
|
|
738
|
-
|
|
873
|
+
The Add button stays in the header in every state: one creation CTA on screen.
|
|
739
874
|
|
|
740
|
-
| State | Header
|
|
875
|
+
| State | Header | Content area |
|
|
741
876
|
|---|---|---|
|
|
742
|
-
| **First
|
|
743
|
-
| **
|
|
744
|
-
| **No results** (
|
|
877
|
+
| **First use** (`totalCount === 0`, no criterion) | Add | `DataTableEmptyState variant="first-use"` instead of the table, without a button |
|
|
878
|
+
| **Data** (`totalCount > 0`) | Add | `DataTable` |
|
|
879
|
+
| **No results** (a criterion active, zero matches) | Add | `DataTable` with `DataTableEmptyState variant="no-results"` inside, without a button |
|
|
745
880
|
|
|
746
|
-
|
|
881
|
+
Gate the button on the permission, never on the list's state:
|
|
747
882
|
|
|
748
883
|
```tsx
|
|
749
884
|
<PageHeader
|
|
750
|
-
title="
|
|
751
|
-
description="
|
|
885
|
+
title="APIs"
|
|
886
|
+
description="Design, deploy and secure the APIs your gateways expose to consumers."
|
|
752
887
|
actions={
|
|
753
|
-
|
|
754
|
-
<Button
|
|
755
|
-
<
|
|
756
|
-
|
|
888
|
+
canCreate && (
|
|
889
|
+
<Button asChild>
|
|
890
|
+
<Link to="new">
|
|
891
|
+
<PlusIcon />
|
|
892
|
+
Add API
|
|
893
|
+
</Link>
|
|
757
894
|
</Button>
|
|
758
895
|
)
|
|
759
896
|
}
|
|
@@ -762,9 +899,26 @@ Both the header button and the empty state CTA trigger the same creation flow. N
|
|
|
762
899
|
|
|
763
900
|
#### Loading state
|
|
764
901
|
|
|
902
|
+
- Fetch with TanStack Query and `placeholderData: keepPreviousData`, and pass `loading={isFetching}`: the first load shows skeleton rows, and later fetches keep the rows under DataTable's overlay. There is no code to write for either.
|
|
765
903
|
- Pass `skeletonCount={pageSize}` to prevent layout shift.
|
|
766
904
|
- `loadingDelay={200}` (default) prevents flash on fast responses.
|
|
767
905
|
|
|
906
|
+
#### Error state
|
|
907
|
+
|
|
908
|
+
- A destructive `Alert` above the table: a short title ("Could not load APIs") and a Retry button that calls `refetch()`, disabled while it runs.
|
|
909
|
+
- Keep the last loaded page on screen under the alert, with the toolbar and the pagination, and say that it may be out of date. `keepPreviousData` holds the previous page only while the next one loads, so keep the last successful response in your list hook: the search field must never disappear while the user types.
|
|
910
|
+
- When nothing has loaded yet, the alert alone, with the error message.
|
|
911
|
+
|
|
912
|
+
#### List state in the URL
|
|
913
|
+
|
|
914
|
+
Keep the list state in the URL with `useSearchParams` (react-router-dom), so that a reload, a shared link and the Back button restore it:
|
|
915
|
+
|
|
916
|
+
- Keys: `q` for the search, one key per filter (`status`), repeated for a multiple choice (`type=internal&type=partner`), `page`, `size`, and `sort` as `field:asc` or `field:desc`.
|
|
917
|
+
- Defaults are not written, and every value is read with a fallback: an unknown status, size or sort gives the default.
|
|
918
|
+
- A new criterion (search, filter, sort) deletes `page` and replaces the history entry; changing the page or its size adds one.
|
|
919
|
+
- Give the search back to `SearchField` exactly as it reported it, and trim it where it is used (the request).
|
|
920
|
+
- A page past the last one (its last row deleted, an old link) goes to the last page.
|
|
921
|
+
|
|
768
922
|
#### `serverSide` prop
|
|
769
923
|
|
|
770
924
|
Set `serverSide` when data is fetched page-by-page from an API. It disables TanStack's built-in sort/filter/pagination models so you control everything via your fetch logic.
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import { ColumnDef, OnChangeFn, SortingState, TableOptions } from '@tanstack/react-table';
|
|
1
|
+
import { ColumnDef, OnChangeFn, RowData, SortingState, TableOptions } from '@tanstack/react-table';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
+
declare module '@tanstack/react-table' {
|
|
4
|
+
interface ColumnMeta<TData extends RowData, TValue> {
|
|
5
|
+
/**
|
|
6
|
+
* The column's name in the View menu (`enableColumnVisibility`), for a header that is not text,
|
|
7
|
+
* such as a sortable `DataTableColumnHeader`. Without it, the menu shows the column id.
|
|
8
|
+
*/
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
3
12
|
interface DataTablePaginationConfig {
|
|
4
13
|
readonly page: number;
|
|
5
14
|
readonly pageSize: number;
|
|
@@ -21,6 +30,10 @@ interface DataTableProps<TData> {
|
|
|
21
30
|
/** Called when the user clicks a sortable column header. Use to trigger a server re-fetch with the new sort params. */
|
|
22
31
|
readonly onSortingChange?: OnChangeFn<SortingState>;
|
|
23
32
|
readonly emptyMessage?: ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Adds the View menu, which shows and hides the columns whose `enableHiding` is not false. It names a
|
|
35
|
+
* column by its `meta.label`, else by its header when that is text, else by its id.
|
|
36
|
+
*/
|
|
24
37
|
readonly enableColumnVisibility?: boolean;
|
|
25
38
|
readonly enableColumnResizing?: boolean;
|
|
26
39
|
/** Makes the table header sticky. Consumer should set a max-height via className. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/composed/DataTable/DataTable.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAMd,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/composed/DataTable/DataTable.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
interface EducationalLandingProps {
|
|
3
|
+
/** Why the entity exists, as a question: "Why create an LLM proxy?". Rendered as an `<h2>` that names the section. */
|
|
4
|
+
readonly heading: string;
|
|
5
|
+
/** The answer, two sentences at most, in 14px muted text. */
|
|
6
|
+
readonly description: string;
|
|
7
|
+
/**
|
|
8
|
+
* Shows a dismiss button at the top right: the card shown on demand above a populated list. A click
|
|
9
|
+
* hides the card, then calls `onDismiss`, where the page remembers the choice and moves focus to
|
|
10
|
+
* the control that reopens the card. A reload, or the page rendering the card again, shows it again.
|
|
11
|
+
*/
|
|
12
|
+
readonly onDismiss?: () => void;
|
|
13
|
+
/** Merged on the root `<section>`. */
|
|
14
|
+
readonly className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The parts, stacked in the order written with a rule between two: `EducationalLandingComparison`,
|
|
17
|
+
* `EducationalLandingPanel`, `EducationalLandingFlow`, `EducationalLandingPillars`.
|
|
18
|
+
*/
|
|
19
|
+
readonly children: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The educational landing of a flagship page: a card that asks why the entity exists, answers in
|
|
23
|
+
* two sentences, then shows the parts the page composes (a comparison of two tinted panels, a flow,
|
|
24
|
+
* pillars). On first use the page renders it instead of the table, under its `PageHeader`; on
|
|
25
|
+
* demand, above the table with `onDismiss`, and its dismiss button hides it.
|
|
26
|
+
*
|
|
27
|
+
* The heading is always an `<h2>`: the page's `<h1>` and its Add button stay in `PageHeader`, and
|
|
28
|
+
* the card holds no button or link besides the dismiss button. It is not an empty state: the empty
|
|
29
|
+
* state of a secondary list is `DataTableEmptyState`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <div className="flex flex-col gap-6">
|
|
34
|
+
* <PageHeader title="LLM Proxies" actions={canCreate && <Button><PlusIcon />Create LLM proxy</Button>} />
|
|
35
|
+
* <EducationalLanding
|
|
36
|
+
* heading="Why create an LLM proxy?"
|
|
37
|
+
* description="An LLM proxy routes your applications' model requests through the AI Gateway."
|
|
38
|
+
* >
|
|
39
|
+
* <EducationalLandingComparison>
|
|
40
|
+
* <EducationalLandingPanel tone="before" label="Direct model calls" points={withoutPoints}>
|
|
41
|
+
* <EducationalLandingFlow steps={[{ icon: MonitorIcon, label: 'App' }, { icon: ServerIcon, label: 'Model provider' }]} />
|
|
42
|
+
* </EducationalLandingPanel>
|
|
43
|
+
* <EducationalLandingPanel tone="after" label="With an LLM proxy" points={withPoints}>
|
|
44
|
+
* <EducationalLandingFlow steps={withSteps} />
|
|
45
|
+
* </EducationalLandingPanel>
|
|
46
|
+
* </EducationalLandingComparison>
|
|
47
|
+
* <EducationalLandingPillars>
|
|
48
|
+
* <EducationalLandingPillar icon={WaypointsIcon} title="Routing" description="Steer each request to the right model." />
|
|
49
|
+
* <EducationalLandingPillar icon={BarChart3Icon} title="Cost control" description="Keep LLM spend predictable." />
|
|
50
|
+
* <EducationalLandingPillar icon={ShieldIcon} title="Resilience" description="Fail over when a provider is down." />
|
|
51
|
+
* </EducationalLandingPillars>
|
|
52
|
+
* </EducationalLanding>
|
|
53
|
+
* </div>
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function EducationalLanding({ heading, description, onDismiss, className, children }: EducationalLandingProps): import("react").JSX.Element | null;
|
|
57
|
+
declare namespace EducationalLanding {
|
|
58
|
+
var displayName: string;
|
|
59
|
+
}
|
|
60
|
+
export { EducationalLanding };
|
|
61
|
+
export type { EducationalLandingProps };
|
|
62
|
+
//# sourceMappingURL=EducationalLanding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EducationalLanding.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLanding.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,OAAO,CAAC;AAKxD,UAAU,uBAAuB;IAC/B,sHAAsH;IACtH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,sCAAsC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,iBAAS,kBAAkB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,uBAAuB,sCAsC5G;kBAtCQ,kBAAkB;;;AA0C3B,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/** True inside `EducationalLandingComparison`: the "after" panel then draws the arrow on its left. */
|
|
3
|
+
declare const ComparisonContext: import('react').Context<boolean>;
|
|
4
|
+
interface EducationalLandingComparisonProps {
|
|
5
|
+
/** Two `EducationalLandingPanel`: the "before" panel first, then the "after" one. */
|
|
6
|
+
readonly children: ReactNode;
|
|
7
|
+
readonly className?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Life without and with the entity, side by side: two `EducationalLandingPanel` of equal width
|
|
11
|
+
* once the card's content is 44rem wide, with an arrow between them drawn by the "after" panel;
|
|
12
|
+
* stacked, without the arrow, in a narrower card. 44rem is where a three-step flow fits in each
|
|
13
|
+
* panel. The page writes the "before" panel first.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <EducationalLandingComparison>
|
|
18
|
+
* <EducationalLandingPanel tone="before" label="Direct model calls" points={withoutPoints} />
|
|
19
|
+
* <EducationalLandingPanel tone="after" label="With an LLM proxy" points={withPoints} />
|
|
20
|
+
* </EducationalLandingComparison>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare function EducationalLandingComparison({ children, className }: EducationalLandingComparisonProps): import("react").JSX.Element;
|
|
24
|
+
declare namespace EducationalLandingComparison {
|
|
25
|
+
var displayName: string;
|
|
26
|
+
}
|
|
27
|
+
export { ComparisonContext, EducationalLandingComparison };
|
|
28
|
+
export type { EducationalLandingComparisonProps };
|
|
29
|
+
//# sourceMappingURL=EducationalLandingComparison.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EducationalLandingComparison.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingComparison.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,sGAAsG;AACtG,QAAA,MAAM,iBAAiB,kCAAuB,CAAC;AAE/C,UAAU,iCAAiC;IACzC,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAS,4BAA4B,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,iCAAiC,+BAc/F;kBAdQ,4BAA4B;;;AAkBrC,OAAO,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,CAAC;AAC3D,YAAY,EAAE,iCAAiC,EAAE,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { LucideIcon } from '../../icons';
|
|
2
|
+
interface EducationalLandingFlowStep {
|
|
3
|
+
/** A Lucide icon from `@gravitee/graphene-core/icons`, drawn in a chip over the label. */
|
|
4
|
+
readonly icon: LucideIcon;
|
|
5
|
+
/** One or two words: "App", "LLM proxy", "Model providers". */
|
|
6
|
+
readonly label: string;
|
|
7
|
+
/** Boxes the step and tints it with the accent: the entity the landing is about. One per flow. */
|
|
8
|
+
readonly emphasized?: boolean;
|
|
9
|
+
/** Three chips, one solid and two dashed: a step that stands for many of one kind (1 to n clusters). */
|
|
10
|
+
readonly multi?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface EducationalLandingFlowProps {
|
|
13
|
+
/** The steps, in order; an arrow joins two consecutive steps. */
|
|
14
|
+
readonly steps: readonly EducationalLandingFlowStep[];
|
|
15
|
+
/** The tint of the emphasized step: `primary` by default, `highlight` (violet) where a product uses it. */
|
|
16
|
+
readonly accent?: 'primary' | 'highlight';
|
|
17
|
+
readonly className?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A row of icon steps joined by arrows: how a request or an event travels, without and with the
|
|
21
|
+
* entity. Rendered as an ordered list, centered; when the steps lack room for one row, they stack,
|
|
22
|
+
* arrows pointing down. Usually the content of an `EducationalLandingPanel`; also valid alone, as a
|
|
23
|
+
* direct child of `EducationalLanding`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <EducationalLandingFlow
|
|
28
|
+
* steps={[
|
|
29
|
+
* { icon: MonitorIcon, label: 'App' },
|
|
30
|
+
* { icon: GlobeIcon, label: 'LLM proxy', emphasized: true },
|
|
31
|
+
* { icon: ServerIcon, label: 'Model providers' },
|
|
32
|
+
* ]}
|
|
33
|
+
* />
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function EducationalLandingFlow({ steps, accent, className }: EducationalLandingFlowProps): import("react").JSX.Element;
|
|
37
|
+
declare namespace EducationalLandingFlow {
|
|
38
|
+
var displayName: string;
|
|
39
|
+
}
|
|
40
|
+
export { EducationalLandingFlow };
|
|
41
|
+
export type { EducationalLandingFlowProps, EducationalLandingFlowStep };
|
|
42
|
+
//# sourceMappingURL=EducationalLandingFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EducationalLandingFlow.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingFlow.tsx"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAShF,UAAU,0BAA0B;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,wGAAwG;IACxG,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,2BAA2B;IACnC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACtD,2GAA2G;IAC3G,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAwCD;;;;;;;;;;;;;;;;GAgBG;AACH,iBAAS,sBAAsB,CAAC,EAAE,KAAK,EAAE,MAAkB,EAAE,SAAS,EAAE,EAAE,2BAA2B,+BAuDpG;kBAvDQ,sBAAsB;;;AA2D/B,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAClC,YAAY,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
interface EducationalLandingPanelProps {
|
|
3
|
+
/** `before`: life without the entity, tinted red with ✗ points. `after`: life with it, tinted with the primary color and ✓ points. */
|
|
4
|
+
readonly tone: 'before' | 'after';
|
|
5
|
+
/** A short noun phrase naming the situation: "Direct model calls", "With an LLM proxy". Rendered as an `<h3>`. */
|
|
6
|
+
readonly label: string;
|
|
7
|
+
/** Three short points, one line each, listed under the content. */
|
|
8
|
+
readonly points: readonly string[];
|
|
9
|
+
/** Free content between the label and the points: an `EducationalLandingFlow` in the canonical composition. */
|
|
10
|
+
readonly children?: ReactNode;
|
|
11
|
+
/** Merged on the tinted box. */
|
|
12
|
+
readonly className?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A tinted box that describes one situation: its label, optional content (a flow), then a ✗ or ✓
|
|
16
|
+
* list. Two of them make an `EducationalLandingComparison`; one alone, as a direct child of
|
|
17
|
+
* `EducationalLanding`, describes a single situation. Inside a comparison laid out side by side,
|
|
18
|
+
* the `after` panel draws the arrow from the panel before it.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <EducationalLandingPanel
|
|
23
|
+
* tone="after"
|
|
24
|
+
* label="With an LLM proxy"
|
|
25
|
+
* points={['Routing across providers', 'Budgets and rate limits', 'Logs with token and cost']}
|
|
26
|
+
* >
|
|
27
|
+
* <EducationalLandingFlow steps={withSteps} />
|
|
28
|
+
* </EducationalLandingPanel>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function EducationalLandingPanel({ tone, label, points, children, className }: EducationalLandingPanelProps): import("react").JSX.Element;
|
|
32
|
+
declare namespace EducationalLandingPanel {
|
|
33
|
+
var displayName: string;
|
|
34
|
+
}
|
|
35
|
+
export { EducationalLandingPanel };
|
|
36
|
+
export type { EducationalLandingPanelProps };
|
|
37
|
+
//# sourceMappingURL=EducationalLandingPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EducationalLandingPanel.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAO,MAAM,OAAO,CAAC;AAuB5C,UAAU,4BAA4B;IACpC,sIAAsI;IACtI,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,kHAAkH;IAClH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,+GAA+G;IAC/G,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAC9B,gCAAgC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,iBAAS,uBAAuB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,4BAA4B,+BA+B1G;kBA/BQ,uBAAuB;;;AAmChC,OAAO,EAAE,uBAAuB,EAAE,CAAC;AACnC,YAAY,EAAE,4BAA4B,EAAE,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LucideIcon } from '../../icons';
|
|
2
|
+
interface EducationalLandingPillarProps {
|
|
3
|
+
/** A Lucide icon from `@gravitee/graphene-core/icons`, drawn in a tinted chip next to the title. */
|
|
4
|
+
readonly icon: LucideIcon;
|
|
5
|
+
/** One or two words: "Routing", "Cost control". Rendered as an `<h3>`. */
|
|
6
|
+
readonly title: string;
|
|
7
|
+
/** One sentence on what the capability does for the user. */
|
|
8
|
+
readonly description: string;
|
|
9
|
+
readonly className?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* One key capability of the entity: an icon chip next to its title, then one sentence. A list item
|
|
13
|
+
* of `EducationalLandingPillars`; it holds no link or button.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <EducationalLandingPillar icon={WaypointsIcon} title="Routing" description="Steer each request to the right model." />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function EducationalLandingPillar({ icon: Icon, title, description, className }: EducationalLandingPillarProps): import("react").JSX.Element;
|
|
21
|
+
declare namespace EducationalLandingPillar {
|
|
22
|
+
var displayName: string;
|
|
23
|
+
}
|
|
24
|
+
export { EducationalLandingPillar };
|
|
25
|
+
export type { EducationalLandingPillarProps };
|
|
26
|
+
//# sourceMappingURL=EducationalLandingPillar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EducationalLandingPillar.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingPillar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGhE,UAAU,6BAA6B;IACrC,oGAAoG;IACpG,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,iBAAS,wBAAwB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,6BAA6B,+BAY7G;kBAZQ,wBAAwB;;;AAgBjC,OAAO,EAAE,wBAAwB,EAAE,CAAC;AACpC,YAAY,EAAE,6BAA6B,EAAE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
interface EducationalLandingPillarsProps {
|
|
3
|
+
/**
|
|
4
|
+
* Columns in a wide card, 3 by default; one column in a narrow card, and 4 go through 2. Four
|
|
5
|
+
* pillars in 2 columns make two rows of two.
|
|
6
|
+
*/
|
|
7
|
+
readonly columns?: 2 | 3 | 4;
|
|
8
|
+
/** The `EducationalLandingPillar` items. */
|
|
9
|
+
readonly children: ReactNode;
|
|
10
|
+
readonly className?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The key capabilities of the entity, as a list laid out in a grid: usually three
|
|
14
|
+
* `EducationalLandingPillar`, last in the card.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <EducationalLandingPillars>
|
|
19
|
+
* <EducationalLandingPillar icon={WaypointsIcon} title="Routing" description="Steer each request to the right model." />
|
|
20
|
+
* <EducationalLandingPillar icon={BarChart3Icon} title="Cost control" description="Keep LLM spend predictable." />
|
|
21
|
+
* <EducationalLandingPillar icon={ShieldIcon} title="Resilience" description="Fail over when a provider is down." />
|
|
22
|
+
* </EducationalLandingPillars>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function EducationalLandingPillars({ columns, children, className }: EducationalLandingPillarsProps): import("react").JSX.Element;
|
|
26
|
+
declare namespace EducationalLandingPillars {
|
|
27
|
+
var displayName: string;
|
|
28
|
+
}
|
|
29
|
+
export { EducationalLandingPillars };
|
|
30
|
+
export type { EducationalLandingPillarsProps };
|
|
31
|
+
//# sourceMappingURL=EducationalLandingPillars.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EducationalLandingPillars.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingPillars.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAWvC,UAAU,8BAA8B;IACtC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,iBAAS,yBAAyB,CAAC,EAAE,OAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,8BAA8B,+BAMtG;kBANQ,yBAAyB;;;AAUlC,OAAO,EAAE,yBAAyB,EAAE,CAAC;AACrC,YAAY,EAAE,8BAA8B,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { EducationalLanding } from './EducationalLanding';
|
|
2
|
+
export type { EducationalLandingProps } from './EducationalLanding';
|
|
3
|
+
export { EducationalLandingComparison } from './EducationalLandingComparison';
|
|
4
|
+
export type { EducationalLandingComparisonProps } from './EducationalLandingComparison';
|
|
5
|
+
export { EducationalLandingFlow } from './EducationalLandingFlow';
|
|
6
|
+
export type { EducationalLandingFlowProps, EducationalLandingFlowStep } from './EducationalLandingFlow';
|
|
7
|
+
export { EducationalLandingPanel } from './EducationalLandingPanel';
|
|
8
|
+
export type { EducationalLandingPanelProps } from './EducationalLandingPanel';
|
|
9
|
+
export { EducationalLandingPillar } from './EducationalLandingPillar';
|
|
10
|
+
export type { EducationalLandingPillarProps } from './EducationalLandingPillar';
|
|
11
|
+
export { EducationalLandingPillars } from './EducationalLandingPillars';
|
|
12
|
+
export type { EducationalLandingPillarsProps } from './EducationalLandingPillars';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|