@carlonicora/nextjs-jsonapi 1.34.0 → 1.36.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.
Files changed (83) hide show
  1. package/dist/{BlockNoteEditor-DFHJWZZB.mjs → BlockNoteEditor-KFUTQVUK.mjs} +3 -3
  2. package/dist/{BlockNoteEditor-TS5AN6W2.js → BlockNoteEditor-N3J42SBY.js} +13 -13
  3. package/dist/{BlockNoteEditor-TS5AN6W2.js.map → BlockNoteEditor-N3J42SBY.js.map} +1 -1
  4. package/dist/BlockNoteEditor-YOAJRPWU.css +29 -0
  5. package/dist/BlockNoteEditor-YOAJRPWU.css.map +1 -0
  6. package/dist/billing/index.css +29 -0
  7. package/dist/billing/index.css.map +1 -0
  8. package/dist/billing/index.d.mts +1 -1
  9. package/dist/billing/index.d.ts +1 -1
  10. package/dist/billing/index.js +374 -358
  11. package/dist/billing/index.js.map +1 -1
  12. package/dist/billing/index.mjs +22 -6
  13. package/dist/billing/index.mjs.map +1 -1
  14. package/dist/{chunk-GFRODCD7.mjs → chunk-DLJTN632.mjs} +1242 -981
  15. package/dist/chunk-DLJTN632.mjs.map +1 -0
  16. package/dist/{chunk-TZRAOUAR.js → chunk-NNCTRU4O.js} +29 -1
  17. package/dist/chunk-NNCTRU4O.js.map +1 -0
  18. package/dist/{chunk-UPA67DQF.js → chunk-S2RZBQP4.js} +977 -716
  19. package/dist/chunk-S2RZBQP4.js.map +1 -0
  20. package/dist/{chunk-HWQBSVBT.mjs → chunk-YZV24UWN.mjs} +29 -1
  21. package/dist/chunk-YZV24UWN.mjs.map +1 -0
  22. package/dist/client/index.css +29 -0
  23. package/dist/client/index.css.map +1 -0
  24. package/dist/client/index.js +3 -3
  25. package/dist/client/index.mjs +2 -2
  26. package/dist/components/index.css +29 -0
  27. package/dist/components/index.css.map +1 -0
  28. package/dist/components/index.d.mts +4 -1
  29. package/dist/components/index.d.ts +4 -1
  30. package/dist/components/index.js +5 -3
  31. package/dist/components/index.js.map +1 -1
  32. package/dist/components/index.mjs +4 -2
  33. package/dist/contexts/index.css +29 -0
  34. package/dist/contexts/index.css.map +1 -0
  35. package/dist/contexts/index.d.mts +9 -2
  36. package/dist/contexts/index.d.ts +9 -2
  37. package/dist/contexts/index.js +9 -3
  38. package/dist/contexts/index.js.map +1 -1
  39. package/dist/contexts/index.mjs +8 -2
  40. package/dist/core/index.d.mts +6 -4
  41. package/dist/core/index.d.ts +6 -4
  42. package/dist/core/index.js +2 -2
  43. package/dist/core/index.mjs +1 -1
  44. package/dist/index.d.mts +2 -2
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.js +2 -2
  47. package/dist/index.mjs +1 -1
  48. package/dist/onboarding.interface-Djyl9qYu.d.mts +71 -0
  49. package/dist/onboarding.interface-Djyl9qYu.d.ts +71 -0
  50. package/dist/{s3.service-DcqkGrKD.d.ts → s3.service-DXkDoMf1.d.ts} +3 -0
  51. package/dist/{s3.service-ag6M_7GO.d.mts → s3.service-hnTPVTm2.d.mts} +3 -0
  52. package/dist/server/index.d.mts +1 -1
  53. package/dist/server/index.d.ts +1 -1
  54. package/dist/server/index.js +3 -3
  55. package/dist/server/index.mjs +1 -1
  56. package/dist/{stripe-subscription.interface-_VWPY2AA.d.mts → stripe-subscription.interface-C8uhCYIZ.d.mts} +2 -0
  57. package/dist/{stripe-subscription.interface-Dm__xmvE.d.ts → stripe-subscription.interface-DK7BJaNd.d.ts} +2 -0
  58. package/package.json +3 -1
  59. package/src/components/index.ts +1 -0
  60. package/src/components/navigations/Header.tsx +2 -32
  61. package/src/contexts/index.ts +1 -0
  62. package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +22 -6
  63. package/src/features/billing/stripe-price/data/stripe-price.interface.ts +2 -0
  64. package/src/features/billing/stripe-price/data/stripe-price.ts +9 -0
  65. package/src/features/billing/stripe-subscription/components/widgets/ProductPricingList.tsx +3 -0
  66. package/src/features/company/components/forms/CompanyDeleter.tsx +157 -70
  67. package/src/features/company/contexts/CompanyContext.tsx +1 -1
  68. package/src/features/company/data/company.service.ts +23 -0
  69. package/src/features/onboarding/components/OnboardingCard.tsx +45 -0
  70. package/src/features/onboarding/components/index.ts +1 -0
  71. package/src/features/onboarding/contexts/OnboardingContext.tsx +213 -0
  72. package/src/features/onboarding/contexts/index.ts +2 -0
  73. package/src/features/onboarding/index.ts +3 -0
  74. package/src/features/onboarding/interfaces/index.ts +1 -0
  75. package/src/features/onboarding/interfaces/onboarding.interface.ts +93 -0
  76. package/src/features/onboarding/styles/onboarding.css +38 -0
  77. package/src/features/user/contexts/CurrentUserContext.tsx +35 -20
  78. package/src/shadcnui/ui/card.tsx +22 -47
  79. package/dist/chunk-GFRODCD7.mjs.map +0 -1
  80. package/dist/chunk-HWQBSVBT.mjs.map +0 -1
  81. package/dist/chunk-TZRAOUAR.js.map +0 -1
  82. package/dist/chunk-UPA67DQF.js.map +0 -1
  83. /package/dist/{BlockNoteEditor-DFHJWZZB.mjs.map → BlockNoteEditor-KFUTQVUK.mjs.map} +0 -0
@@ -0,0 +1,213 @@
1
+ "use client";
2
+
3
+ import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
4
+ import { createRoot, Root } from "react-dom/client";
5
+ import Shepherd from "shepherd.js";
6
+ import "shepherd.js/dist/css/shepherd.css";
7
+ import { OnboardingCard } from "../components/OnboardingCard";
8
+ import {
9
+ DEFAULT_ONBOARDING_LABELS,
10
+ OnboardingContextValue,
11
+ OnboardingProviderProps,
12
+ OnboardingStepConfig,
13
+ } from "../interfaces";
14
+ import "../styles/onboarding.css";
15
+
16
+ const OnboardingContext = createContext<OnboardingContextValue | null>(null);
17
+
18
+ export function OnboardingProvider({
19
+ children,
20
+ tours = [],
21
+ tourPaths = {},
22
+ labels = DEFAULT_ONBOARDING_LABELS,
23
+ renderCard,
24
+ zIndex = 9999,
25
+ }: OnboardingProviderProps) {
26
+ const [isTourActive, setIsTourActive] = useState(false);
27
+ const [activeTourId, setActiveTourId] = useState<string | null>(null);
28
+ const [currentStepIndex, setCurrentStepIndex] = useState(0);
29
+ const [totalSteps, setTotalSteps] = useState(0);
30
+
31
+ const tourRef = useRef<InstanceType<typeof Shepherd.Tour> | null>(null);
32
+ const rootsRef = useRef<Map<string, Root>>(new Map());
33
+
34
+ const cleanupRoots = useCallback(() => {
35
+ rootsRef.current.forEach((root) => {
36
+ try {
37
+ root.unmount();
38
+ } catch (e) {
39
+ // Root may already be unmounted
40
+ }
41
+ });
42
+ rootsRef.current.clear();
43
+ }, []);
44
+
45
+ const closeTour = useCallback(() => {
46
+ if (tourRef.current) {
47
+ tourRef.current.cancel();
48
+ tourRef.current = null;
49
+ }
50
+ cleanupRoots();
51
+ setIsTourActive(false);
52
+ setActiveTourId(null);
53
+ setCurrentStepIndex(0);
54
+ setTotalSteps(0);
55
+ }, [cleanupRoots]);
56
+
57
+ const nextStep = useCallback(() => {
58
+ if (tourRef.current) {
59
+ tourRef.current.next();
60
+ }
61
+ }, []);
62
+
63
+ const previousStep = useCallback(() => {
64
+ if (tourRef.current) {
65
+ tourRef.current.back();
66
+ }
67
+ }, []);
68
+
69
+ const goToStep = useCallback((index: number) => {
70
+ if (tourRef.current) {
71
+ tourRef.current.show(index);
72
+ }
73
+ }, []);
74
+
75
+ const startTour = useCallback(
76
+ (tourId: string, steps?: OnboardingStepConfig[]) => {
77
+ if (typeof window === "undefined") return;
78
+
79
+ // Close any existing tour
80
+ if (tourRef.current) {
81
+ closeTour();
82
+ }
83
+
84
+ // Get steps from provided steps or find tour by ID
85
+ let tourSteps = steps;
86
+ if (!tourSteps) {
87
+ const tour = tours.find((t) => t.id === tourId);
88
+ tourSteps = tour?.steps;
89
+ }
90
+
91
+ if (!tourSteps || tourSteps.length === 0) {
92
+ console.warn(`No steps found for tour: ${tourId}`);
93
+ return;
94
+ }
95
+
96
+ setTotalSteps(tourSteps.length);
97
+ setActiveTourId(tourId);
98
+
99
+ const tour = new Shepherd.Tour({
100
+ useModalOverlay: true,
101
+ defaultStepOptions: {
102
+ classes: "shepherd-theme-custom",
103
+ scrollTo: { behavior: "smooth", block: "center" },
104
+ cancelIcon: { enabled: false },
105
+ },
106
+ });
107
+
108
+ // Set up tour event listeners
109
+ tour.on("show", () => setIsTourActive(true));
110
+ tour.on("cancel", () => {
111
+ cleanupRoots();
112
+ setIsTourActive(false);
113
+ setActiveTourId(null);
114
+ setCurrentStepIndex(0);
115
+ setTotalSteps(0);
116
+ tourRef.current = null;
117
+ });
118
+ tour.on("complete", () => {
119
+ cleanupRoots();
120
+ setIsTourActive(false);
121
+ setActiveTourId(null);
122
+ setCurrentStepIndex(0);
123
+ setTotalSteps(0);
124
+ tourRef.current = null;
125
+ });
126
+
127
+ tourSteps.forEach((stepConfig, index) => {
128
+ const stepId = stepConfig.id || `step-${index}`;
129
+
130
+ tour.addStep({
131
+ id: stepId,
132
+ attachTo: stepConfig.selector ? { element: stepConfig.selector, on: stepConfig.side || "bottom" } : undefined,
133
+ arrow: true,
134
+ // Use text callback to return DOM element with React content
135
+ text: () => {
136
+ const container = document.createElement("div");
137
+ const root = createRoot(container);
138
+ rootsRef.current.set(stepId, root);
139
+
140
+ const cardProps = {
141
+ step: stepConfig,
142
+ currentIndex: index,
143
+ totalSteps: tourSteps!.length,
144
+ labels,
145
+ onNext: () => tour.next(),
146
+ onPrevious: () => tour.back(),
147
+ onClose: () => tour.cancel(),
148
+ onSkip: () => tour.cancel(),
149
+ isFirst: index === 0,
150
+ isLast: index === tourSteps!.length - 1,
151
+ };
152
+
153
+ root.render(renderCard ? renderCard(cardProps) : <OnboardingCard {...cardProps} />);
154
+
155
+ return container;
156
+ },
157
+ buttons: [], // Empty - our card handles navigation
158
+ beforeShowPromise: stepConfig.showDelay
159
+ ? () => new Promise((resolve) => setTimeout(resolve, stepConfig.showDelay))
160
+ : undefined,
161
+ canClickTarget: stepConfig.canClickTarget ?? false,
162
+ modalOverlayOpeningPadding: 8,
163
+ modalOverlayOpeningRadius: 8,
164
+ when: {
165
+ show: () => {
166
+ setCurrentStepIndex(index);
167
+ stepConfig.onShow?.();
168
+ },
169
+ hide: () => {
170
+ stepConfig.onHide?.();
171
+ },
172
+ },
173
+ });
174
+ });
175
+
176
+ tourRef.current = tour;
177
+ tour.start();
178
+ },
179
+ [tours, labels, renderCard, closeTour, cleanupRoots],
180
+ );
181
+
182
+ // Cleanup on unmount
183
+ useEffect(() => {
184
+ return () => {
185
+ if (tourRef.current) {
186
+ tourRef.current.cancel();
187
+ }
188
+ cleanupRoots();
189
+ };
190
+ }, [cleanupRoots]);
191
+
192
+ const value: OnboardingContextValue = {
193
+ startTour,
194
+ closeTour,
195
+ nextStep,
196
+ previousStep,
197
+ goToStep,
198
+ isTourActive,
199
+ activeTourId,
200
+ currentStepIndex,
201
+ totalSteps,
202
+ };
203
+
204
+ return <OnboardingContext.Provider value={value}>{children}</OnboardingContext.Provider>;
205
+ }
206
+
207
+ export function useOnboarding(): OnboardingContextValue {
208
+ const context = useContext(OnboardingContext);
209
+ if (!context) {
210
+ throw new Error("useOnboarding must be used within an OnboardingProvider");
211
+ }
212
+ return context;
213
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./OnboardingContext";
2
+ export * from "../interfaces";
@@ -0,0 +1,3 @@
1
+ export * from "./interfaces";
2
+ export * from "./contexts";
3
+ export * from "./components";
@@ -0,0 +1 @@
1
+ export * from "./onboarding.interface";
@@ -0,0 +1,93 @@
1
+ import { ReactNode } from "react";
2
+
3
+ export type OnboardingStepSide =
4
+ | "top"
5
+ | "top-start"
6
+ | "top-end"
7
+ | "bottom"
8
+ | "bottom-start"
9
+ | "bottom-end"
10
+ | "left"
11
+ | "left-start"
12
+ | "left-end"
13
+ | "right"
14
+ | "right-start"
15
+ | "right-end";
16
+
17
+ export interface OnboardingStepConfig {
18
+ id: string;
19
+ title: string;
20
+ content: ReactNode;
21
+ selector?: string;
22
+ side?: OnboardingStepSide;
23
+ onShow?: () => void;
24
+ onHide?: () => void;
25
+ action?: { label: string; onClick: () => void };
26
+ showDelay?: number;
27
+ canClickTarget?: boolean;
28
+ className?: string;
29
+ }
30
+
31
+ export interface OnboardingTourConfig {
32
+ id: string;
33
+ name: string;
34
+ description?: string;
35
+ steps: OnboardingStepConfig[];
36
+ routes?: string[];
37
+ }
38
+
39
+ export interface OnboardingTourPaths {
40
+ [routePattern: string]: string;
41
+ }
42
+
43
+ export interface OnboardingLabels {
44
+ next: string;
45
+ previous: string;
46
+ finish: string;
47
+ skip: string;
48
+ close: string;
49
+ stepCounter: (current: number, total: number) => string;
50
+ }
51
+
52
+ export const DEFAULT_ONBOARDING_LABELS: OnboardingLabels = {
53
+ next: "Next",
54
+ previous: "Previous",
55
+ finish: "Finish",
56
+ skip: "Skip",
57
+ close: "Close",
58
+ stepCounter: (current, total) => `${current} of ${total}`,
59
+ };
60
+
61
+ export interface OnboardingContextValue {
62
+ startTour: (tourId: string, steps?: OnboardingStepConfig[]) => void;
63
+ closeTour: () => void;
64
+ nextStep: () => void;
65
+ previousStep: () => void;
66
+ goToStep: (index: number) => void;
67
+ isTourActive: boolean;
68
+ activeTourId: string | null;
69
+ currentStepIndex: number;
70
+ totalSteps: number;
71
+ }
72
+
73
+ export interface OnboardingProviderProps {
74
+ children: React.ReactNode;
75
+ tours?: OnboardingTourConfig[];
76
+ tourPaths?: OnboardingTourPaths;
77
+ labels?: OnboardingLabels;
78
+ renderCard?: (props: OnboardingCardRenderProps) => ReactNode;
79
+ zIndex?: number;
80
+ }
81
+
82
+ export interface OnboardingCardRenderProps {
83
+ step: OnboardingStepConfig;
84
+ currentIndex: number;
85
+ totalSteps: number;
86
+ labels: OnboardingLabels;
87
+ onNext: () => void;
88
+ onPrevious: () => void;
89
+ onClose: () => void;
90
+ onSkip: () => void;
91
+ isFirst: boolean;
92
+ isLast: boolean;
93
+ }
@@ -0,0 +1,38 @@
1
+ /* Shepherd Modal Overlay */
2
+ .shepherd-modal-overlay-container.shepherd-modal-is-visible {
3
+ opacity: 1 !important;
4
+ }
5
+
6
+ .shepherd-modal-overlay-container path {
7
+ fill: rgba(0, 0, 0, 0.75) !important;
8
+ }
9
+
10
+ /* Remove default shepherd styling - we use custom React card */
11
+ .shepherd-element {
12
+ background: transparent !important;
13
+ border: none !important;
14
+ box-shadow: none !important;
15
+ padding: 0 !important;
16
+ max-width: none !important;
17
+ }
18
+
19
+ .shepherd-content,
20
+ .shepherd-text {
21
+ padding: 0 !important;
22
+ }
23
+
24
+ /* Arrow styling to match card background */
25
+ .shepherd-arrow::before {
26
+ background: hsl(var(--card)) !important;
27
+ }
28
+
29
+ /* Hide default Shepherd header and footer - we render custom ones */
30
+ .shepherd-header,
31
+ .shepherd-footer {
32
+ display: none !important;
33
+ }
34
+
35
+ /* Dark mode support */
36
+ .dark .shepherd-arrow::before {
37
+ background: hsl(var(--card)) !important;
38
+ }
@@ -33,7 +33,7 @@ export interface CurrentUserContextType<T extends UserInterface = UserInterface>
33
33
  hasAccesToFeature: (featureIdentifier: string) => boolean;
34
34
  matchUrlToModule: (prarms?: { path: string }) => ModuleWithPermissions | undefined;
35
35
  hasRole: (roleId: string) => boolean;
36
- refreshUser: () => Promise<void>;
36
+ refreshUser: (options?: { skipCookieUpdate?: boolean }) => Promise<void>;
37
37
  isRefreshing: boolean;
38
38
  }
39
39
 
@@ -136,27 +136,36 @@ export const CurrentUserProvider = ({ children }: { children: React.ReactNode })
136
136
  const [isRefreshing, setIsRefreshing] = useState(false);
137
137
 
138
138
  // Function to refresh user data from the API
139
- const refreshUser = useCallback(async (): Promise<void> => {
140
- if (isRefreshing) return;
139
+ // skipCookieUpdate: When true, only updates React state without calling the Server Action
140
+ // This prevents page reloads when refresh is triggered by WebSocket events
141
+ const refreshUser = useCallback(async (options?: { skipCookieUpdate?: boolean }): Promise<void> => {
142
+ if (isRefreshing) {
143
+ return;
144
+ }
141
145
 
142
146
  setIsRefreshing(true);
143
147
  try {
144
148
  const fullUser = await UserService.findFullUser();
145
149
  if (fullUser) {
146
- setDehydratedUser(fullUser.dehydrate() as any);
150
+ const dehydrated = fullUser.dehydrate();
151
+
152
+ setDehydratedUser(dehydrated as any);
147
153
  setUser(fullUser);
148
154
 
149
155
  // Update authentication cookies with fresh user data
150
- await getTokenHandler()?.updateToken({
151
- userId: fullUser.id,
152
- companyId: fullUser.company?.id,
153
- roles: fullUser.roles.map((role) => role.id),
154
- features: fullUser.company?.features?.map((feature) => feature.id) ?? [],
155
- modules: fullUser.modules.map((module) => ({
156
- id: module.id,
157
- permissions: module.permissions,
158
- })),
159
- });
156
+ // Skip when triggered by WebSocket to prevent page reload (Server Actions modify cookies)
157
+ if (!options?.skipCookieUpdate) {
158
+ await getTokenHandler()?.updateToken({
159
+ userId: fullUser.id,
160
+ companyId: fullUser.company?.id,
161
+ roles: fullUser.roles.map((role) => role.id),
162
+ features: fullUser.company?.features?.map((feature) => feature.id) ?? [],
163
+ modules: fullUser.modules.map((module) => ({
164
+ id: module.id,
165
+ permissions: module.permissions,
166
+ })),
167
+ });
168
+ }
160
169
  }
161
170
  } catch (error) {
162
171
  console.error("Failed to refresh user data:", error);
@@ -175,23 +184,29 @@ export const CurrentUserProvider = ({ children }: { children: React.ReactNode })
175
184
  // Track refresh in progress to prevent duplicate API calls
176
185
  const isRefreshingRef = useRef(false);
177
186
 
178
- // Listen for company:tokens_updated WebSocket events
187
+ // Listen for company WebSocket events that trigger user refresh
179
188
  useEffect(() => {
180
- if (!socket || !isConnected || !currentUser?.company?.id) return;
189
+ if (!socket || !isConnected || !currentUser?.company?.id) {
190
+ return;
191
+ }
181
192
 
182
- const handleTokensUpdated = (data: { companyId: string; type: string }) => {
193
+ const handleCompanyUpdate = (data: { companyId: string; type: string }) => {
183
194
  if (data.companyId === currentUser.company?.id && !isRefreshingRef.current) {
184
195
  isRefreshingRef.current = true;
185
- refreshUserRef.current().finally(() => {
196
+ // Skip cookie update to prevent page reload - only update React state
197
+ refreshUserRef.current({ skipCookieUpdate: true }).finally(() => {
186
198
  isRefreshingRef.current = false;
187
199
  });
188
200
  }
189
201
  };
190
202
 
191
- socket.on("company:tokens_updated", handleTokensUpdated);
203
+ // Both events trigger the same refresh behavior
204
+ socket.on("company:tokens_updated", handleCompanyUpdate);
205
+ socket.on("company:subscription_updated", handleCompanyUpdate);
192
206
 
193
207
  return () => {
194
- socket.off("company:tokens_updated", handleTokensUpdated);
208
+ socket.off("company:tokens_updated", handleCompanyUpdate);
209
+ socket.off("company:subscription_updated", handleCompanyUpdate);
195
210
  };
196
211
  }, [socket, isConnected, currentUser?.company?.id]);
197
212
 
@@ -1,20 +1,19 @@
1
- import * as React from "react"
1
+ import * as React from "react";
2
2
 
3
- import { cn } from "@/lib/utils"
3
+ import { cn } from "@/lib/utils";
4
4
 
5
- function Card({
6
- className,
7
- size = "default",
8
- ...props
9
- }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
5
+ function Card({ className, size = "default", ...props }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
10
6
  return (
11
7
  <div
12
8
  data-slot="card"
13
9
  data-size={size}
14
- className={cn("ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-lg py-4 text-xs/relaxed ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg group/card flex flex-col", className)}
10
+ className={cn(
11
+ "ring-foreground/10 bg-card text-card-foreground gap-4 overflow-clip rounded-lg py-4 text-xs/relaxed ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg group/card flex flex-col",
12
+ className,
13
+ )}
15
14
  {...props}
16
15
  />
17
- )
16
+ );
18
17
  }
19
18
 
20
19
  function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -23,72 +22,48 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
23
22
  data-slot="card-header"
24
23
  className={cn(
25
24
  "gap-1 rounded-t-lg px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
26
- className
25
+ className,
27
26
  )}
28
27
  {...props}
29
28
  />
30
- )
29
+ );
31
30
  }
32
31
 
33
32
  function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
34
- return (
35
- <div
36
- data-slot="card-title"
37
- className={cn("text-sm font-medium", className)}
38
- {...props}
39
- />
40
- )
33
+ return <div data-slot="card-title" className={cn("text-sm font-medium", className)} {...props} />;
41
34
  }
42
35
 
43
36
  function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
44
37
  return (
45
- <div
46
- data-slot="card-description"
47
- className={cn("text-muted-foreground text-xs/relaxed", className)}
48
- {...props}
49
- />
50
- )
38
+ <div data-slot="card-description" className={cn("text-muted-foreground text-xs/relaxed", className)} {...props} />
39
+ );
51
40
  }
52
41
 
53
42
  function CardAction({ className, ...props }: React.ComponentProps<"div">) {
54
43
  return (
55
44
  <div
56
45
  data-slot="card-action"
57
- className={cn(
58
- "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
59
- className
60
- )}
46
+ className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
61
47
  {...props}
62
48
  />
63
- )
49
+ );
64
50
  }
65
51
 
66
52
  function CardContent({ className, ...props }: React.ComponentProps<"div">) {
67
- return (
68
- <div
69
- data-slot="card-content"
70
- className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
71
- {...props}
72
- />
73
- )
53
+ return <div data-slot="card-content" className={cn("px-4 group-data-[size=sm]/card:px-3", className)} {...props} />;
74
54
  }
75
55
 
76
56
  function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
77
57
  return (
78
58
  <div
79
59
  data-slot="card-footer"
80
- className={cn("rounded-b-lg px-4 group-data-[size=sm]/card:px-3 [.border-t]:pt-4 group-data-[size=sm]/card:[.border-t]:pt-3 flex items-center", className)}
60
+ className={cn(
61
+ "rounded-b-lg px-4 group-data-[size=sm]/card:px-3 [.border-t]:pt-4 group-data-[size=sm]/card:[.border-t]:pt-3 flex items-center",
62
+ className,
63
+ )}
81
64
  {...props}
82
65
  />
83
- )
66
+ );
84
67
  }
85
68
 
86
- export {
87
- Card,
88
- CardHeader,
89
- CardFooter,
90
- CardTitle,
91
- CardAction,
92
- CardDescription,
93
- CardContent,
94
- }
69
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };