@carlonicora/nextjs-jsonapi 3.3.9 → 3.4.0
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/dist/{BlockNoteEditor-BYMRWUVG.mjs → BlockNoteEditor-3D5D7YL7.mjs} +2 -2
- package/dist/{BlockNoteEditor-USRMFZGP.js → BlockNoteEditor-F6TGKMTW.js} +9 -9
- package/dist/{BlockNoteEditor-USRMFZGP.js.map → BlockNoteEditor-F6TGKMTW.js.map} +1 -1
- package/dist/billing/index.js +347 -347
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +27 -27
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-CFZW6GU4.mjs → chunk-G5LSXG5K.mjs} +2572 -2482
- package/dist/chunk-G5LSXG5K.mjs.map +1 -0
- package/dist/{chunk-5WFYQE22.js → chunk-KT4CTWCV.js} +825 -735
- package/dist/chunk-KT4CTWCV.js.map +1 -0
- package/dist/client/index.d.mts +5 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/index.d.mts +12 -6
- package/dist/components/index.d.ts +12 -6
- package/dist/components/index.js +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/contexts/index.d.mts +13 -1
- package/dist/contexts/index.d.ts +13 -1
- package/dist/contexts/index.js +6 -2
- package/dist/contexts/index.js.map +1 -1
- package/dist/contexts/index.mjs +5 -1
- package/dist/features/help/index.js +35 -35
- package/dist/features/help/index.js.map +1 -1
- package/dist/features/help/index.mjs +6 -6
- package/dist/features/help/index.mjs.map +1 -1
- package/dist/features/tokenusage/index.js +135 -129
- package/dist/features/tokenusage/index.js.map +1 -1
- package/dist/features/tokenusage/index.mjs +74 -68
- package/dist/features/tokenusage/index.mjs.map +1 -1
- package/package.json +2 -2
- package/scripts/check-rtl-classes.mjs +60 -0
- package/scripts/check-rtl-classes.test.ts +25 -0
- package/src/components/TableCellAvatar.tsx +1 -1
- package/src/components/containers/PageContainer.tsx +1 -1
- package/src/components/containers/ReactMarkdownContainer.tsx +20 -4
- package/src/components/containers/RoundPageContainer.tsx +7 -7
- package/src/components/contents/AttributeElement.tsx +1 -1
- package/src/components/editors/BlockNoteDiffInlineContent.tsx +2 -2
- package/src/components/editors/BlockNoteEditorMentionHoverCard.tsx +1 -0
- package/src/components/forms/CommonDeleter.tsx +1 -1
- package/src/components/forms/CommonEditorButtons.tsx +1 -1
- package/src/components/forms/DatePickerPopover.tsx +2 -2
- package/src/components/forms/DateRangeSelector.tsx +2 -2
- package/src/components/forms/EditorSheet.tsx +18 -9
- package/src/components/forms/EntityMultiSelector.tsx +7 -7
- package/src/components/forms/EntitySelector.tsx +8 -8
- package/src/components/forms/FileUploader.tsx +1 -0
- package/src/components/forms/FormCheckbox.tsx +3 -3
- package/src/components/forms/FormDateTime.tsx +3 -3
- package/src/components/forms/FormFieldWrapper.tsx +1 -1
- package/src/components/forms/FormPlaceAutocomplete.tsx +2 -2
- package/src/components/forms/GdprConsentCheckbox.tsx +1 -1
- package/src/components/forms/MultiFileUploader.tsx +1 -1
- package/src/components/forms/PasswordInput.tsx +1 -1
- package/src/components/forms/__tests__/FormCheckbox.test.tsx +4 -4
- package/src/components/navigations/Header.tsx +2 -2
- package/src/components/navigations/PageSection.tsx +1 -1
- package/src/components/pages/PageContainerContentDetails.tsx +1 -1
- package/src/components/pages/PageContentContainer.tsx +5 -2
- package/src/components/tables/ContentListTable.tsx +4 -4
- package/src/components/tables/ContentTableSearch.tsx +5 -5
- package/src/contexts/DirectionContext.tsx +25 -0
- package/src/contexts/__tests__/DirectionContext.test.tsx +22 -0
- package/src/contexts/index.ts +1 -0
- package/src/features/ai-connection/components/lists/AiConnectionTypeCard.tsx +1 -1
- package/src/features/assistant/components/containers/AssistantPageContainer.tsx +1 -1
- package/src/features/assistant/components/parts/AssistantBlockNoteComposer.tsx +2 -2
- package/src/features/assistant/components/parts/AssistantComposer.tsx +2 -2
- package/src/features/assistant/components/parts/AssistantEmptyState.tsx +1 -1
- package/src/features/assistant/components/parts/AssistantSidebar.tsx +3 -3
- package/src/features/assistant-action/components/ApprovalActionCard.tsx +1 -1
- package/src/features/assistant-action/components/AssistantContainerWithApprovals.tsx +1 -1
- package/src/features/assistant-message/components/MessageItem.tsx +3 -3
- package/src/features/assistant-message/components/__tests__/MessageItem.spec.tsx +1 -0
- package/src/features/assistant-message/components/parts/MessageSourcesPanel.tsx +2 -2
- package/src/features/assistant-message/components/parts/RelevanceMeter.tsx +1 -1
- package/src/features/assistant-message/components/parts/tabs/SuggestedQuestionsTab.tsx +1 -1
- package/src/features/auth/components/buttons/GoogleSignInButton.tsx +1 -1
- package/src/features/auth/components/forms/Register.tsx +2 -2
- package/src/features/auth/components/two-factor/BackupCodesDialog.tsx +3 -3
- package/src/features/auth/utils/__tests__/clearClientStorage.spec.ts +4 -4
- package/src/features/billing/components/cards/BillingUsageSummaryCard.tsx +1 -1
- package/src/features/billing/components/cards/CustomerInfoCard.tsx +1 -1
- package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +1 -1
- package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +1 -1
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +1 -1
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +1 -1
- package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +1 -1
- package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +6 -6
- package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +2 -2
- package/src/features/billing/stripe-price/hooks/useStripePriceTableStructure.tsx +3 -3
- package/src/features/billing/stripe-product/hooks/useStripeProductTableStructure.tsx +2 -2
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +4 -4
- package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +2 -2
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx +1 -1
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +2 -2
- package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +2 -2
- package/src/features/company/components/forms/CompanyDeleter.tsx +4 -4
- package/src/features/content/hooks/useContentTableStructure.tsx +1 -1
- package/src/features/feature/components/forms/FormFeatures.tsx +2 -2
- package/src/features/help/components/HelpAssistantSheet.tsx +3 -2
- package/src/features/help/components/HelpHint.tsx +2 -2
- package/src/features/help/components/HelpTOC.tsx +1 -1
- package/src/features/how-to/components/containers/HowToCommand.tsx +2 -2
- package/src/features/how-to/components/containers/HowToCommandViewer.tsx +5 -5
- package/src/features/how-to/components/forms/HowToEditor.tsx +3 -3
- package/src/features/how-to/components/forms/HowToSelector.tsx +5 -5
- package/src/features/notification/components/lists/NotificationsList.tsx +2 -2
- package/src/features/notification/components/modals/NotificationModal.tsx +1 -1
- package/src/features/oauth/components/OAuthClientCard.tsx +2 -2
- package/src/features/oauth/components/OAuthClientDetail.tsx +2 -2
- package/src/features/oauth/components/OAuthClientList.tsx +3 -3
- package/src/features/oauth/components/OAuthRedirectUriInput.tsx +1 -1
- package/src/features/oauth/components/OAuthScopeSelector.tsx +2 -2
- package/src/features/onboarding/components/OnboardingCard.tsx +1 -7
- package/src/features/rbac/components/RbacByRoleContainer.tsx +3 -3
- package/src/features/rbac/components/RbacContainer.tsx +3 -3
- package/src/features/referral/components/ReferralWidget.tsx +1 -0
- package/src/features/role/components/forms/FormRoles.tsx +1 -1
- package/src/features/role/components/forms/RemoveUserFromRole.tsx +1 -1
- package/src/features/role/components/forms/UserRoleAdd.tsx +1 -1
- package/src/features/tokenusage/components/TokenUsageBreakdownTable.tsx +9 -9
- package/src/features/tokenusage/components/TokenUsageRankedBar.tsx +8 -3
- package/src/features/tokenusage/components/TokenUsageTimelineChart.tsx +5 -1
- package/src/features/user/components/details/UserStandaloneDetails.tsx +1 -1
- package/src/features/user/components/forms/UserReactivator.tsx +2 -2
- package/src/features/user/components/forms/UserResentInvitationEmail.tsx +1 -1
- package/src/features/user/components/forms/UserSelector.tsx +7 -7
- package/src/features/user/components/lists/ContributorsList.tsx +1 -1
- package/src/features/user/components/lists/UserListInAdd.tsx +1 -1
- package/src/features/user/components/widgets/UserSearchPopover.tsx +5 -5
- package/src/features/user/hooks/useUserTableStructure.tsx +1 -1
- package/src/features/waitlist/components/forms/WaitlistQuestionnaireRenderer.tsx +1 -1
- package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +1 -1
- package/src/hooks/useCustomD3Graph.tsx +16 -0
- package/src/lib/__tests__/direction.test.ts +18 -0
- package/src/lib/direction.ts +8 -0
- package/src/shadcnui/custom/multi-select.tsx +6 -6
- package/src/shadcnui/custom/multiple-selector.tsx +6 -2
- package/src/shadcnui/ui/accordion.tsx +1 -1
- package/src/shadcnui/ui/alert-dialog.tsx +2 -1
- package/src/shadcnui/ui/alert.tsx +2 -2
- package/src/shadcnui/ui/avatar.tsx +1 -1
- package/src/shadcnui/ui/badge.tsx +1 -1
- package/src/shadcnui/ui/breadcrumb.tsx +1 -1
- package/src/shadcnui/ui/button.tsx +4 -4
- package/src/shadcnui/ui/calendar.tsx +9 -8
- package/src/shadcnui/ui/carousel.tsx +18 -11
- package/src/shadcnui/ui/combobox.tsx +3 -3
- package/src/shadcnui/ui/command.tsx +2 -2
- package/src/shadcnui/ui/context-menu.tsx +15 -11
- package/src/shadcnui/ui/dialog.tsx +2 -1
- package/src/shadcnui/ui/drawer.tsx +19 -3
- package/src/shadcnui/ui/dropdown-menu.tsx +12 -10
- package/src/shadcnui/ui/field.tsx +2 -2
- package/src/shadcnui/ui/input-group.tsx +3 -3
- package/src/shadcnui/ui/input-otp.tsx +2 -0
- package/src/shadcnui/ui/navigation-menu.tsx +2 -2
- package/src/shadcnui/ui/progress.tsx +1 -1
- package/src/shadcnui/ui/radio-group.tsx +1 -0
- package/src/shadcnui/ui/resizable.tsx +1 -0
- package/src/shadcnui/ui/scroll-area.tsx +1 -1
- package/src/shadcnui/ui/select.tsx +2 -2
- package/src/shadcnui/ui/sheet.tsx +11 -4
- package/src/shadcnui/ui/sidebar.tsx +37 -20
- package/src/shadcnui/ui/sonner.tsx +6 -1
- package/src/shadcnui/ui/switch.tsx +1 -1
- package/src/shadcnui/ui/table.tsx +2 -2
- package/src/shadcnui/ui/tabs.tsx +1 -1
- package/dist/chunk-5WFYQE22.js.map +0 -1
- package/dist/chunk-CFZW6GU4.mjs.map +0 -1
- /package/dist/{BlockNoteEditor-BYMRWUVG.mjs.map → BlockNoteEditor-3D5D7YL7.mjs.map} +0 -0
|
@@ -37,8 +37,8 @@ export function Header({ children, mobileChildren, leftContent, logo, className
|
|
|
37
37
|
<header
|
|
38
38
|
className={`bg-sidebar sticky top-0 z-10 flex h-[var(--app-header-h,calc(3rem_+_env(safe-area-inset-top)))] flex-col items-center justify-start gap-x-4 pt-[env(safe-area-inset-top)] ${className ?? ""}`}
|
|
39
39
|
>
|
|
40
|
-
<div className="bg-sidebar flex h-12 w-full flex-row items-center justify-between
|
|
41
|
-
{isMobile && logo && <div className="flex shrink-0 flex-row items-center
|
|
40
|
+
<div className="bg-sidebar flex h-12 w-full flex-row items-center justify-between ps-2 pe-4">
|
|
41
|
+
{isMobile && logo && <div className="flex shrink-0 flex-row items-center pe-1">{logo}</div>}
|
|
42
42
|
<SidebarTrigger aria-label="Toggle sidebar" id="sidebar-trigger" />
|
|
43
43
|
{leftContent}
|
|
44
44
|
<div className="flex w-full flex-row items-center justify-start">
|
|
@@ -47,7 +47,7 @@ export function PageSection({ children, title, options, open, small, onToggle }:
|
|
|
47
47
|
{isOpen ? (
|
|
48
48
|
<ChevronDownIcon className={`text-primary h-4 w-4`} />
|
|
49
49
|
) : (
|
|
50
|
-
<ChevronRightIcon className="text-primary h-4 w-4" />
|
|
50
|
+
<ChevronRightIcon className="text-primary h-4 w-4 rtl:rotate-180" />
|
|
51
51
|
)}
|
|
52
52
|
<h2 className={`flex w-full ${small === true ? `text-sm` : `text-lg`} text-primary font-semibold`}>
|
|
53
53
|
{title}
|
|
@@ -30,6 +30,7 @@ export function PageContentContainer({ header, details, footer, content, fullBle
|
|
|
30
30
|
// Returns saved layout and callback to save changes
|
|
31
31
|
const { defaultLayout, onLayoutChanged } = useDefaultLayout({
|
|
32
32
|
id: layoutId,
|
|
33
|
+
// rtl-ok: persisted panel identifiers, not CSS
|
|
33
34
|
panelIds: ["left-panel", "right-panel"],
|
|
34
35
|
storage: typeof window !== "undefined" ? localStorage : undefined,
|
|
35
36
|
});
|
|
@@ -47,7 +48,7 @@ export function PageContentContainer({ header, details, footer, content, fullBle
|
|
|
47
48
|
|
|
48
49
|
return (
|
|
49
50
|
<div className="flex h-[calc(100dvh-4rem)] w-full flex-col transition-opacity duration-150 animate-in fade-in">
|
|
50
|
-
{header && <div className="mb-4 flex w-full shrink-0 border-b
|
|
51
|
+
{header && <div className="mb-4 flex w-full shrink-0 border-b pe-4">{header}</div>}
|
|
51
52
|
<div className="min-h-0 flex-1">
|
|
52
53
|
{details || footer ? (
|
|
53
54
|
<ResizablePanelGroup
|
|
@@ -57,18 +58,20 @@ export function PageContentContainer({ header, details, footer, content, fullBle
|
|
|
57
58
|
onLayoutChanged={onLayoutChanged}
|
|
58
59
|
>
|
|
59
60
|
<ResizablePanel
|
|
61
|
+
// rtl-ok: persisted panel identifier, not CSS
|
|
60
62
|
id="left-panel"
|
|
61
63
|
defaultSize={leftPanelDefaultSize}
|
|
62
64
|
minSize={leftPanelMinSize}
|
|
63
65
|
maxSize={leftPanelMaxSize}
|
|
64
66
|
>
|
|
65
|
-
<div className={`@container flex h-full flex-col ${isMobile ? "pb-4" : "
|
|
67
|
+
<div className={`@container flex h-full flex-col ${isMobile ? "pb-4" : "pe-4"}`}>
|
|
66
68
|
<div className="flex-1 overflow-y-auto">{details}</div>
|
|
67
69
|
{footer && <div className="flex flex-col gap-y-2 pt-2 pb-2">{footer}</div>}
|
|
68
70
|
</div>
|
|
69
71
|
</ResizablePanel>
|
|
70
72
|
<ResizableHandle withHandle />
|
|
71
73
|
<ResizablePanel
|
|
74
|
+
// rtl-ok: persisted panel identifier, not CSS
|
|
72
75
|
id="right-panel"
|
|
73
76
|
defaultSize={rightPanelDefaultSize}
|
|
74
77
|
className={cn("w-full", isMobile ? "pt-4" : "")}
|
|
@@ -159,7 +159,7 @@ export const ContentListTable = memo(function ContentListTable(props: ContentLis
|
|
|
159
159
|
{props.title && (
|
|
160
160
|
<TableRow>
|
|
161
161
|
<TableHead
|
|
162
|
-
className="bg-card rounded-t-lg text-primary p-4 text-
|
|
162
|
+
className="bg-card rounded-t-lg text-primary p-4 text-start font-bold"
|
|
163
163
|
colSpan={tableColumns.length}
|
|
164
164
|
>
|
|
165
165
|
<div className="flex w-full items-center justify-between gap-x-2">
|
|
@@ -271,7 +271,7 @@ export const ContentListTable = memo(function ContentListTable(props: ContentLis
|
|
|
271
271
|
{showFooter && (
|
|
272
272
|
<TableFooter>
|
|
273
273
|
<TableRow>
|
|
274
|
-
<TableCell colSpan={tableColumns.length} className="bg-card py-4 text-
|
|
274
|
+
<TableCell colSpan={tableColumns.length} className="bg-card py-4 text-end">
|
|
275
275
|
<div className="flex items-center justify-end space-x-2">
|
|
276
276
|
<Button
|
|
277
277
|
variant="outline"
|
|
@@ -286,7 +286,7 @@ export const ContentListTable = memo(function ContentListTable(props: ContentLis
|
|
|
286
286
|
{pendingDirection === "prev" ? (
|
|
287
287
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
288
288
|
) : (
|
|
289
|
-
<ChevronLeft className="h-4 w-4" />
|
|
289
|
+
<ChevronLeft className="h-4 w-4 rtl:rotate-180" />
|
|
290
290
|
)}
|
|
291
291
|
</Button>
|
|
292
292
|
{data.pageInfo && (
|
|
@@ -307,7 +307,7 @@ export const ContentListTable = memo(function ContentListTable(props: ContentLis
|
|
|
307
307
|
{pendingDirection === "next" ? (
|
|
308
308
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
309
309
|
) : (
|
|
310
|
-
<ChevronRight className="h-4 w-4" />
|
|
310
|
+
<ChevronRight className="h-4 w-4 rtl:rotate-180" />
|
|
311
311
|
)}
|
|
312
312
|
</Button>
|
|
313
313
|
</div>
|
|
@@ -74,13 +74,13 @@ export function ContentTableSearch({ data }: ContentTableSearchProps) {
|
|
|
74
74
|
Labels reuse keys that exist in every consuming app; a new package-level key would
|
|
75
75
|
throw MISSING_MESSAGE wherever it had not been added. */}
|
|
76
76
|
{isExpanded ? (
|
|
77
|
-
<Search aria-hidden className="text-muted-foreground absolute top-1
|
|
77
|
+
<Search aria-hidden className="text-muted-foreground absolute top-1 start-1 size-4" />
|
|
78
78
|
) : (
|
|
79
79
|
<Button
|
|
80
80
|
data-testid="content-table-search-trigger"
|
|
81
81
|
variant="ghost"
|
|
82
82
|
size="icon-sm"
|
|
83
|
-
className="text-muted-foreground hover:text-foreground absolute top-0
|
|
83
|
+
className="text-muted-foreground hover:text-foreground absolute top-0 start-0"
|
|
84
84
|
onClick={handleSearchIconClick}
|
|
85
85
|
>
|
|
86
86
|
<Search className="size-4" />
|
|
@@ -100,19 +100,19 @@ export function ContentTableSearch({ data }: ContentTableSearchProps) {
|
|
|
100
100
|
onBlur={handleBlur}
|
|
101
101
|
placeholder={t(`ui.search.placeholder_global`)}
|
|
102
102
|
type="text"
|
|
103
|
-
className="border-border/50 focus-visible:border-border h-10 w-full
|
|
103
|
+
className="border-border/50 focus-visible:border-border h-10 w-full pe-8 ps-8 text-xs shadow-none focus-visible:ring-0"
|
|
104
104
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
105
105
|
value={searchTerm}
|
|
106
106
|
/>
|
|
107
107
|
)}
|
|
108
108
|
{isExpanded && isSearching && (
|
|
109
|
-
<RefreshCw className="text-muted-foreground absolute top-1
|
|
109
|
+
<RefreshCw className="text-muted-foreground absolute top-1 end-1 h-4 w-4 animate-spin" />
|
|
110
110
|
)}
|
|
111
111
|
{isExpanded && !isSearching && searchTermRef.current && (
|
|
112
112
|
<Button
|
|
113
113
|
variant="ghost"
|
|
114
114
|
size="icon-sm"
|
|
115
|
-
className="text-muted-foreground hover:text-foreground absolute top-0
|
|
115
|
+
className="text-muted-foreground hover:text-foreground absolute top-0 end-0"
|
|
116
116
|
onClick={handleClear}
|
|
117
117
|
>
|
|
118
118
|
<X className="size-4" />
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { DirectionProvider as BaseUIDirectionProvider } from "@base-ui/react/direction-provider";
|
|
5
|
+
|
|
6
|
+
export type Direction = "ltr" | "rtl";
|
|
7
|
+
|
|
8
|
+
const DirectionContext = React.createContext<Direction>("ltr");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* App-owned direction. The consumer sets <html dir> AND mounts this provider
|
|
12
|
+
* with the same value; the package never derives direction from locale.
|
|
13
|
+
* Wraps Base UI's DirectionProvider so popups resolve align/side logically.
|
|
14
|
+
*/
|
|
15
|
+
export function DirectionProvider({ dir = "ltr", children }: { dir?: Direction; children: React.ReactNode }) {
|
|
16
|
+
return (
|
|
17
|
+
<DirectionContext.Provider value={dir}>
|
|
18
|
+
<BaseUIDirectionProvider direction={dir}>{children}</BaseUIDirectionProvider>
|
|
19
|
+
</DirectionContext.Provider>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useDir(): Direction {
|
|
24
|
+
return React.useContext(DirectionContext);
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { DirectionProvider, useDir } from "../DirectionContext";
|
|
4
|
+
|
|
5
|
+
function Probe() {
|
|
6
|
+
return <span data-testid="dir">{useDir()}</span>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe("DirectionContext", () => {
|
|
10
|
+
it("defaults to ltr without a provider", () => {
|
|
11
|
+
render(<Probe />);
|
|
12
|
+
expect(screen.getByTestId("dir").textContent).toBe("ltr");
|
|
13
|
+
});
|
|
14
|
+
it("provides rtl", () => {
|
|
15
|
+
render(
|
|
16
|
+
<DirectionProvider dir="rtl">
|
|
17
|
+
<Probe />
|
|
18
|
+
</DirectionProvider>,
|
|
19
|
+
);
|
|
20
|
+
expect(screen.getByTestId("dir").textContent).toBe("rtl");
|
|
21
|
+
});
|
|
22
|
+
});
|
package/src/contexts/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from "../features/how-to/contexts/HowToContext";
|
|
|
8
8
|
export * from "../features/rbac/contexts/RbacContext";
|
|
9
9
|
export * from "../features/assistant/contexts/AssistantContext";
|
|
10
10
|
export * from "./CommonContext";
|
|
11
|
+
export * from "./DirectionContext";
|
|
11
12
|
export * from "./HeaderChildrenContext";
|
|
12
13
|
export * from "./HeaderLeftContentContext";
|
|
13
14
|
export * from "./HeaderLogoContext";
|
|
@@ -123,7 +123,7 @@ export function AiConnectionTypeCard({ connectionType }: AiConnectionTypeCardPro
|
|
|
123
123
|
className={cn("flex items-center gap-3 px-3 py-2", readOnly && "opacity-50")}
|
|
124
124
|
data-testid={`ai-connection-row-${connection.id}`}
|
|
125
125
|
>
|
|
126
|
-
<span className="text-muted-foreground w-5 text-
|
|
126
|
+
<span className="text-muted-foreground w-5 text-end text-xs tabular-nums">{index + 1}</span>
|
|
127
127
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
128
128
|
<span className="truncate text-sm">{connection.name}</span>
|
|
129
129
|
<span className="text-muted-foreground truncate text-xs">
|
|
@@ -70,7 +70,7 @@ export function AssistantPageContainer({
|
|
|
70
70
|
details={
|
|
71
71
|
// The panel supplies its own padding and border; cancel both so the
|
|
72
72
|
// sidebar reads as the panel body rather than a card inside it.
|
|
73
|
-
<div className="-m-4 flex h-full min-h-0 [&>aside]:w-full [&>aside]:border-
|
|
73
|
+
<div className="-m-4 flex h-full min-h-0 [&>aside]:w-full [&>aside]:border-e-0 [&>aside]:bg-transparent">
|
|
74
74
|
<AssistantSidebar
|
|
75
75
|
threads={ctx.threads}
|
|
76
76
|
activeId={ctx.assistant?.id}
|
|
@@ -126,10 +126,10 @@ export function AssistantBlockNoteComposer({
|
|
|
126
126
|
/>
|
|
127
127
|
</div>
|
|
128
128
|
<Button onClick={submit} disabled={!canSend} size="sm" className="h-8">
|
|
129
|
-
<ArrowUp className="
|
|
129
|
+
<ArrowUp className="me-1 h-4 w-4" /> {t("features.assistant.send")}
|
|
130
130
|
</Button>
|
|
131
131
|
</div>
|
|
132
|
-
<div className="text-muted-foreground text-
|
|
132
|
+
<div className="text-muted-foreground text-end text-xs">{t("features.assistant.keyboard_hint")}</div>
|
|
133
133
|
</div>
|
|
134
134
|
);
|
|
135
135
|
}
|
|
@@ -47,10 +47,10 @@ export function AssistantComposer({ onSend, disabled, value: controlled, onValue
|
|
|
47
47
|
className="min-h-[48px] resize-none border-0 bg-transparent focus-visible:ring-0"
|
|
48
48
|
/>
|
|
49
49
|
<Button onClick={submit} disabled={!canSend} size="sm" className="h-8">
|
|
50
|
-
<ArrowUp className="
|
|
50
|
+
<ArrowUp className="me-1 h-4 w-4" /> {t("ui.buttons.save")}
|
|
51
51
|
</Button>
|
|
52
52
|
</div>
|
|
53
|
-
<div className="text-muted-foreground text-
|
|
53
|
+
<div className="text-muted-foreground text-end text-xs">{t("features.assistant.keyboard_hint")}</div>
|
|
54
54
|
</div>
|
|
55
55
|
);
|
|
56
56
|
}
|
|
@@ -55,7 +55,7 @@ export function AssistantEmptyState({ onSend, operatorMode = false, onOperatorMo
|
|
|
55
55
|
<button
|
|
56
56
|
key={k}
|
|
57
57
|
type="button"
|
|
58
|
-
className="border-border bg-muted/30 hover:bg-muted rounded-lg border p-3 text-
|
|
58
|
+
className="border-border bg-muted/30 hover:bg-muted rounded-lg border p-3 text-start text-sm"
|
|
59
59
|
onClick={() => setDraft(text)}
|
|
60
60
|
>
|
|
61
61
|
{text}
|
|
@@ -29,7 +29,7 @@ export function AssistantSidebar({ threads, activeId, onSelect, onNew }: Props)
|
|
|
29
29
|
type="button"
|
|
30
30
|
onClick={() => onSelect(thread.id)}
|
|
31
31
|
className={
|
|
32
|
-
"block w-full truncate rounded-md px-2 py-1.5 text-
|
|
32
|
+
"block w-full truncate rounded-md px-2 py-1.5 text-start text-sm " +
|
|
33
33
|
(activeId === thread.id ? "bg-primary/10 text-primary" : "hover:bg-muted text-foreground")
|
|
34
34
|
}
|
|
35
35
|
>
|
|
@@ -41,10 +41,10 @@ export function AssistantSidebar({ threads, activeId, onSelect, onNew }: Props)
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
<aside className="bg-muted/30 flex w-64 flex-col border-
|
|
44
|
+
<aside className="bg-muted/30 flex w-64 flex-col border-e">
|
|
45
45
|
<div className="border-b p-3">
|
|
46
46
|
<Button onClick={onNew} className="w-full" size="sm">
|
|
47
|
-
<Plus className="
|
|
47
|
+
<Plus className="me-1 h-4 w-4" /> {t("features.assistant.new")}
|
|
48
48
|
</Button>
|
|
49
49
|
</div>
|
|
50
50
|
<div className="flex-1 overflow-y-auto p-2">
|
|
@@ -103,7 +103,7 @@ export function ApprovalActionCard({ actionId, summary, onResolved }: Props) {
|
|
|
103
103
|
<ShieldAlert className="text-warning h-3.5 w-3.5" />
|
|
104
104
|
<span>{t("features.assistant.approval.title")}</span>
|
|
105
105
|
{status && status !== "pending" && (
|
|
106
|
-
<Badge variant={STATUS_VARIANT[status]} className="
|
|
106
|
+
<Badge variant={STATUS_VARIANT[status]} className="ms-auto tracking-normal normal-case">
|
|
107
107
|
{t(STATUS_LABEL_KEY[status])}
|
|
108
108
|
</Badge>
|
|
109
109
|
)}
|
|
@@ -24,7 +24,7 @@ export function AssistantContainerWithApprovals() {
|
|
|
24
24
|
onResolved={ctx.appendResolvedMessage}
|
|
25
25
|
/>
|
|
26
26
|
) : (
|
|
27
|
-
<div className="bg-muted text-foreground rounded-2xl rounded-
|
|
27
|
+
<div className="bg-muted text-foreground rounded-2xl rounded-es-sm px-3.5 py-2.5 text-sm leading-relaxed">
|
|
28
28
|
{message.content}
|
|
29
29
|
</div>
|
|
30
30
|
)
|
|
@@ -82,7 +82,7 @@ export function MessageItem({
|
|
|
82
82
|
renders invisible here, so force the on-primary colour and lean on
|
|
83
83
|
weight for the affordance (links are never underlined in this UI).
|
|
84
84
|
*/}
|
|
85
|
-
<div className="bg-primary text-primary-foreground max-w-[72%] rounded-2xl rounded-
|
|
85
|
+
<div className="bg-primary text-primary-foreground max-w-[72%] rounded-2xl rounded-ee-sm px-3.5 py-2 text-sm [&_a]:text-primary-foreground [&_a]:font-semibold [&_p]:m-0">
|
|
86
86
|
<ReactMarkdown remarkPlugins={[remarkGfm]} urlTransform={mentionUrlTransform} components={markdownComponents}>
|
|
87
87
|
{message.content}
|
|
88
88
|
</ReactMarkdown>
|
|
@@ -104,7 +104,7 @@ export function MessageItem({
|
|
|
104
104
|
|
|
105
105
|
return (
|
|
106
106
|
<div className="flex min-w-0 max-w-[78%] flex-col gap-1.5">
|
|
107
|
-
<div className="text-muted-foreground flex items-center gap-2
|
|
107
|
+
<div className="text-muted-foreground flex items-center gap-2 ps-1 text-xs">
|
|
108
108
|
<span className="flex h-4 w-4 items-center justify-center rounded-full bg-gradient-to-br from-blue-400 to-violet-500 text-white">
|
|
109
109
|
<Sparkles className="h-2.5 w-2.5" />
|
|
110
110
|
</span>
|
|
@@ -117,7 +117,7 @@ export function MessageItem({
|
|
|
117
117
|
// zeroed): restore flow spacing between sibling blocks and give
|
|
118
118
|
// blockquotes a callout treatment — assistant answers use them for
|
|
119
119
|
// proposals and asides that must stand apart from factual prose.
|
|
120
|
-
<div className="bg-muted text-foreground rounded-2xl rounded-
|
|
120
|
+
<div className="bg-muted text-foreground rounded-2xl rounded-es-sm px-3.5 py-2.5 text-sm leading-relaxed [&>*+*]:mt-3 [&_blockquote]:border-s-2 [&_blockquote]:border-primary/50 [&_blockquote]:bg-primary/5 [&_blockquote]:rounded-md [&_blockquote]:px-3 [&_blockquote]:py-1.5">
|
|
121
121
|
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents} urlTransform={mentionUrlTransform}>
|
|
122
122
|
{message.content}
|
|
123
123
|
</ReactMarkdown>
|
|
@@ -59,6 +59,7 @@ function buildMessageStub(p: {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
describe("MessageItem", () => {
|
|
62
|
+
// rtl-ok: prose in the test name, not a CSS class
|
|
62
63
|
it("user message is in a right-aligned bubble with plain text", () => {
|
|
63
64
|
const msg = buildMessageStub({ role: "user", content: "hello" });
|
|
64
65
|
render(<MessageItem message={msg} isLatestAssistant={false} onSelectFollowUp={vi.fn()} />);
|
|
@@ -96,7 +96,7 @@ export function MessageSourcesPanel({ message, isLatestAssistant, onSelectFollow
|
|
|
96
96
|
aria-controls={panelId}
|
|
97
97
|
className="text-primary inline-flex items-center gap-1 text-xs font-medium"
|
|
98
98
|
>
|
|
99
|
-
{open ? <ChevronDown className="h-3 w-3" /> : <ChevronRight className="h-3 w-3" />}
|
|
99
|
+
{open ? <ChevronDown className="h-3 w-3" /> : <ChevronRight className="h-3 w-3 rtl:rotate-180" />}
|
|
100
100
|
{open
|
|
101
101
|
? t("features.assistant.message.sources.toggle_hide")
|
|
102
102
|
: t("features.assistant.message.sources.toggle", { count: total })}
|
|
@@ -109,7 +109,7 @@ export function MessageSourcesPanel({ message, isLatestAssistant, onSelectFollow
|
|
|
109
109
|
{visibleTabs.map((key) => (
|
|
110
110
|
<TabsTrigger key={key} value={key}>
|
|
111
111
|
{t(`features.assistant.message.sources.tabs.${key === "suggested" ? "suggested_questions" : key}`)}
|
|
112
|
-
<span className="text-muted-foreground
|
|
112
|
+
<span className="text-muted-foreground ms-1.5 text-xs">
|
|
113
113
|
{key === "suggested" && suggestionsCount}
|
|
114
114
|
{key === "references" && refsCount}
|
|
115
115
|
{key === "citations" && citationsCount}
|
|
@@ -18,7 +18,7 @@ export function RelevanceMeter({ value, className = "" }: Props) {
|
|
|
18
18
|
aria-label={`Relevance ${label}`}
|
|
19
19
|
className={`bg-muted relative mx-auto flex h-5 w-20 items-center justify-center overflow-hidden rounded border ${className}`}
|
|
20
20
|
>
|
|
21
|
-
<div className="bg-accent absolute top-0
|
|
21
|
+
<div className="bg-accent absolute top-0 start-0 h-full" style={{ width: `${pct}%` }} />
|
|
22
22
|
<span
|
|
23
23
|
className={`relative text-xs ${pct < 40 ? "text-muted-foreground" : "text-accent-foreground font-semibold"}`}
|
|
24
24
|
>
|
|
@@ -14,7 +14,7 @@ export function SuggestedQuestionsTab({ questions, onSelect }: Props) {
|
|
|
14
14
|
key={q}
|
|
15
15
|
type="button"
|
|
16
16
|
onClick={() => onSelect(q)}
|
|
17
|
-
className="border-border bg-muted/30 hover:bg-muted rounded-md border px-3 py-1.5 text-
|
|
17
|
+
className="border-border bg-muted/30 hover:bg-muted rounded-md border px-3 py-1.5 text-start text-sm"
|
|
18
18
|
>
|
|
19
19
|
{q}
|
|
20
20
|
</button>
|
|
@@ -27,7 +27,7 @@ export function GoogleSignInButton({ referralCode }: GoogleSignInButtonProps) {
|
|
|
27
27
|
variant="outline"
|
|
28
28
|
data-testid="page-login-button-google"
|
|
29
29
|
>
|
|
30
|
-
<svg className="
|
|
30
|
+
<svg className="me-2 h-5 w-5" viewBox="0 0 24 24">
|
|
31
31
|
<path
|
|
32
32
|
fill="#4285F4"
|
|
33
33
|
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
@@ -304,7 +304,7 @@ export default function Register() {
|
|
|
304
304
|
variant="outline"
|
|
305
305
|
type="button"
|
|
306
306
|
>
|
|
307
|
-
<svg className="
|
|
307
|
+
<svg className="me-2 h-5 w-5" viewBox="0 0 24 24">
|
|
308
308
|
<path
|
|
309
309
|
fill="#4285F4"
|
|
310
310
|
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
@@ -332,7 +332,7 @@ export default function Register() {
|
|
|
332
332
|
className="flex w-full"
|
|
333
333
|
>
|
|
334
334
|
<Button className="w-full" variant="outline" type="button">
|
|
335
|
-
<svg className="
|
|
335
|
+
<svg className="me-2 h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
|
|
336
336
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
|
|
337
337
|
</svg>
|
|
338
338
|
{t("waitlist.buttons.register_with_discord")}
|
|
@@ -101,7 +101,7 @@ export function BackupCodesDialog({ remainingCodes, onRegenerate, trigger }: Bac
|
|
|
101
101
|
<AlertDialogTrigger
|
|
102
102
|
render={
|
|
103
103
|
<Button variant="outline" className="w-full">
|
|
104
|
-
<RefreshCw className="h-4 w-4
|
|
104
|
+
<RefreshCw className="h-4 w-4 me-2" />
|
|
105
105
|
{t("auth.two_factor.generate_new_codes")}
|
|
106
106
|
</Button>
|
|
107
107
|
}
|
|
@@ -131,11 +131,11 @@ export function BackupCodesDialog({ remainingCodes, onRegenerate, trigger }: Bac
|
|
|
131
131
|
</div>
|
|
132
132
|
<div className="flex gap-2">
|
|
133
133
|
<Button variant="outline" className="flex-1" onClick={handleCopyAll}>
|
|
134
|
-
<Copy className="h-4 w-4
|
|
134
|
+
<Copy className="h-4 w-4 me-2" />
|
|
135
135
|
{t("auth.two_factor.copy_all")}
|
|
136
136
|
</Button>
|
|
137
137
|
<Button variant="outline" className="flex-1" onClick={handleDownload}>
|
|
138
|
-
<Download className="h-4 w-4
|
|
138
|
+
<Download className="h-4 w-4 me-2" />
|
|
139
139
|
{t("auth.two_factor.download")}
|
|
140
140
|
</Button>
|
|
141
141
|
</div>
|
|
@@ -63,18 +63,18 @@ describe("clearClientStorage", () => {
|
|
|
63
63
|
describe("Scenario: Handles keys with special characters", () => {
|
|
64
64
|
it("should properly remove keys containing colons", () => {
|
|
65
65
|
const keys = [
|
|
66
|
-
"react-resizable-panels:page-content-container-desktop:left-panel:right-panel",
|
|
67
|
-
"react-resizable-panels:page-content-container-mobile:left-panel:right-panel",
|
|
66
|
+
"react-resizable-panels:page-content-container-desktop:left-panel:right-panel", // rtl-ok: localStorage key, not a CSS class
|
|
67
|
+
"react-resizable-panels:page-content-container-mobile:left-panel:right-panel", // rtl-ok: localStorage key, not a CSS class
|
|
68
68
|
];
|
|
69
69
|
|
|
70
70
|
clearClientStorage(keys);
|
|
71
71
|
|
|
72
72
|
expect(window.localStorage.removeItem).toHaveBeenCalledTimes(2);
|
|
73
73
|
expect(window.localStorage.removeItem).toHaveBeenCalledWith(
|
|
74
|
-
"react-resizable-panels:page-content-container-desktop:left-panel:right-panel",
|
|
74
|
+
"react-resizable-panels:page-content-container-desktop:left-panel:right-panel", // rtl-ok: localStorage key, not a CSS class
|
|
75
75
|
);
|
|
76
76
|
expect(window.localStorage.removeItem).toHaveBeenCalledWith(
|
|
77
|
-
"react-resizable-panels:page-content-container-mobile:left-panel:right-panel",
|
|
77
|
+
"react-resizable-panels:page-content-container-mobile:left-panel:right-panel", // rtl-ok: localStorage key, not a CSS class
|
|
78
78
|
);
|
|
79
79
|
});
|
|
80
80
|
});
|
|
@@ -87,7 +87,7 @@ export function BillingUsageSummaryCard({
|
|
|
87
87
|
{meters.length > 1 && <p className="text-xs text-muted-foreground">Across {meters.length} meters</p>}
|
|
88
88
|
<span className="flex items-center text-xs text-muted-foreground">
|
|
89
89
|
View details
|
|
90
|
-
<ChevronRight className="h-3 w-3
|
|
90
|
+
<ChevronRight className="h-3 w-3 ms-1 rtl:rotate-180" />
|
|
91
91
|
</span>
|
|
92
92
|
</div>
|
|
93
93
|
)}
|
|
@@ -103,7 +103,7 @@ export function CustomerInfoCard({ customer, loading, error }: CustomerInfoCardP
|
|
|
103
103
|
)}
|
|
104
104
|
<Button variant="outline" size="sm" className="mt-2" onClick={handlePortalClick} disabled={portalLoading}>
|
|
105
105
|
{portalLoading ? "Loading..." : "Manage in Stripe Portal"}
|
|
106
|
-
<ExternalLink className="h-4 w-4
|
|
106
|
+
<ExternalLink className="h-4 w-4 ms-1" />
|
|
107
107
|
</Button>
|
|
108
108
|
</div>
|
|
109
109
|
</CardContent>
|
|
@@ -103,7 +103,7 @@ export function InvoicesSummaryCard({ invoices, loading, error, onViewAllClick }
|
|
|
103
103
|
{openInvoices.length > 0 && <span className="text-warning">{openInvoices.length} open</span>}
|
|
104
104
|
<span className="flex items-center">
|
|
105
105
|
View all
|
|
106
|
-
<ChevronRight className="h-3 w-3
|
|
106
|
+
<ChevronRight className="h-3 w-3 ms-1 rtl:rotate-180" />
|
|
107
107
|
</span>
|
|
108
108
|
</div>
|
|
109
109
|
</div>
|
|
@@ -360,7 +360,7 @@ export function BillingDashboardContainer() {
|
|
|
360
360
|
<Button onClick={handleCreateCustomer} disabled={creatingCustomer} size="lg">
|
|
361
361
|
{creatingCustomer ? (
|
|
362
362
|
<>
|
|
363
|
-
<Loader2 className="
|
|
363
|
+
<Loader2 className="me-2 h-4 w-4 animate-spin" />
|
|
364
364
|
Setting up...
|
|
365
365
|
</>
|
|
366
366
|
) : (
|
|
@@ -91,7 +91,7 @@ export function PaymentMethodCard({ paymentMethod, onUpdate }: PaymentMethodCard
|
|
|
91
91
|
<Card className="relative">
|
|
92
92
|
{/* Default Badge */}
|
|
93
93
|
{isDefault && (
|
|
94
|
-
<Badge variant="softGreen" className="absolute
|
|
94
|
+
<Badge variant="softGreen" className="absolute end-2 top-2">
|
|
95
95
|
Default
|
|
96
96
|
</Badge>
|
|
97
97
|
)}
|
|
@@ -95,14 +95,14 @@ export function InvoiceDetails({
|
|
|
95
95
|
<table className="w-full">
|
|
96
96
|
<thead className="bg-muted">
|
|
97
97
|
<tr>
|
|
98
|
-
<th className="text-
|
|
99
|
-
<th className="text-
|
|
98
|
+
<th className="text-start p-3 text-sm font-medium">Description</th>
|
|
99
|
+
<th className="text-end p-3 text-sm font-medium">Amount</th>
|
|
100
100
|
</tr>
|
|
101
101
|
</thead>
|
|
102
102
|
<tbody>
|
|
103
103
|
<tr className="border-t">
|
|
104
104
|
<td className="p-3">{productName}</td>
|
|
105
|
-
<td className="p-3 text-
|
|
105
|
+
<td className="p-3 text-end">{formatCurrency(invoice.subtotal, invoice.currency)}</td>
|
|
106
106
|
</tr>
|
|
107
107
|
</tbody>
|
|
108
108
|
</table>
|
|
@@ -149,21 +149,21 @@ export function InvoiceDetails({
|
|
|
149
149
|
<div className="flex flex-wrap gap-2 pt-4 border-t">
|
|
150
150
|
{invoice.stripePdfUrl && (
|
|
151
151
|
<Button variant="outline" onClick={handleDownloadPDF}>
|
|
152
|
-
<Download className="
|
|
152
|
+
<Download className="me-2 h-4 w-4" />
|
|
153
153
|
Download PDF
|
|
154
154
|
</Button>
|
|
155
155
|
)}
|
|
156
156
|
|
|
157
157
|
{invoice.status === InvoiceStatus.OPEN && invoice.attempted && (
|
|
158
158
|
<Button variant="default" onClick={handleRetryPayment}>
|
|
159
|
-
<RefreshCw className="
|
|
159
|
+
<RefreshCw className="me-2 h-4 w-4" />
|
|
160
160
|
Retry Payment
|
|
161
161
|
</Button>
|
|
162
162
|
)}
|
|
163
163
|
|
|
164
164
|
{invoice.stripeHostedInvoiceUrl && (
|
|
165
165
|
<Button variant="outline" onClick={handleViewInStripe}>
|
|
166
|
-
<ExternalLink className="
|
|
166
|
+
<ExternalLink className="me-2 h-4 w-4" />
|
|
167
167
|
View in Stripe
|
|
168
168
|
</Button>
|
|
169
169
|
)}
|
|
@@ -36,7 +36,7 @@ export function InvoicesList({ invoices, onInvoicesChange }: InvoicesListProps)
|
|
|
36
36
|
<TableHead>Invoice #</TableHead>
|
|
37
37
|
<TableHead>Date</TableHead>
|
|
38
38
|
<TableHead>Status</TableHead>
|
|
39
|
-
<TableHead className="text-
|
|
39
|
+
<TableHead className="text-end">Amount</TableHead>
|
|
40
40
|
<TableHead>Period</TableHead>
|
|
41
41
|
</TableRow>
|
|
42
42
|
</TableHeader>
|
|
@@ -58,7 +58,7 @@ export function InvoicesList({ invoices, onInvoicesChange }: InvoicesListProps)
|
|
|
58
58
|
<TableCell>
|
|
59
59
|
<InvoiceStatusBadge status={invoice.status} />
|
|
60
60
|
</TableCell>
|
|
61
|
-
<TableCell className="text-
|
|
61
|
+
<TableCell className="text-end font-medium">{amount}</TableCell>
|
|
62
62
|
<TableCell className="text-muted-foreground text-xs">{period}</TableCell>
|
|
63
63
|
</TableRow>
|
|
64
64
|
);
|