@wealthx/shadcn 1.5.12 → 1.5.13
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/.turbo/turbo-build.log +74 -74
- package/CHANGELOG.md +6 -0
- package/dist/{chunk-CPM6P63C.mjs → chunk-BF5FKUF6.mjs} +53 -24
- package/dist/chunk-KICT4VQL.mjs +508 -0
- package/dist/chunk-V23CBULF.mjs +432 -0
- package/dist/components/ui/appointment-calendar-view.js +177 -176
- package/dist/components/ui/appointment-calendar-view.mjs +1 -1
- package/dist/components/ui/bank-statement-generate-dialog.js +163 -76
- package/dist/components/ui/bank-statement-generate-dialog.mjs +2 -1
- package/dist/components/ui/resource-center/index.js +1030 -0
- package/dist/components/ui/resource-center/index.mjs +29 -0
- package/dist/index.js +540 -364
- package/dist/index.mjs +15 -13
- package/dist/styles.css +1 -1
- package/package.json +4 -4
- package/src/components/index.tsx +2 -0
- package/src/components/ui/appointment-calendar-view.tsx +211 -199
- package/src/components/ui/bank-statement-generate-dialog.tsx +125 -97
- package/src/components/ui/resource-center/index.tsx +35 -0
- package/src/components/ui/resource-center/resource-cards.tsx +218 -0
- package/src/components/ui/resource-center/resource-carousel.tsx +122 -0
- package/src/components/ui/resource-center/resource-center-header.tsx +95 -0
- package/src/components/ui/resource-center/resource-email-editor-dialog.tsx +131 -0
- package/src/components/ui/resource-center/resource-modal.tsx +76 -0
- package/src/components/ui/resource-center/types.ts +81 -0
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +1 -1
- package/dist/chunk-IODGRCQG.mjs +0 -438
- package/dist/chunk-XYWEGBAA.mjs +0 -348
- package/dist/components/ui/resource-center.js +0 -748
- package/dist/components/ui/resource-center.mjs +0 -24
- package/src/components/ui/resource-center.tsx +0 -539
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Play } from "lucide-react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { ResourceModal } from "./resource-modal";
|
|
6
|
+
import type { ResourceCenterHeaderProps } from "./types";
|
|
7
|
+
|
|
8
|
+
export function ResourceCenterHeader({
|
|
9
|
+
title,
|
|
10
|
+
description,
|
|
11
|
+
backgroundImageUrl,
|
|
12
|
+
backgroundVideoUrl,
|
|
13
|
+
watchVideoUrl,
|
|
14
|
+
}: ResourceCenterHeaderProps) {
|
|
15
|
+
const [watchModalOpen, setWatchModalOpen] = useState(false);
|
|
16
|
+
|
|
17
|
+
const hasBackground = !!backgroundImageUrl || !!backgroundVideoUrl;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<div
|
|
22
|
+
className={cn(
|
|
23
|
+
"relative flex min-h-[480px] w-full items-center overflow-hidden max-[900px]:min-h-[360px] max-[600px]:min-h-[260px]",
|
|
24
|
+
)}
|
|
25
|
+
>
|
|
26
|
+
{/* Background base */}
|
|
27
|
+
<div
|
|
28
|
+
className={cn(
|
|
29
|
+
"absolute inset-0 z-[0]",
|
|
30
|
+
hasBackground
|
|
31
|
+
? "bg-foreground"
|
|
32
|
+
: "bg-gradient-to-br from-primary to-foreground",
|
|
33
|
+
)}
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
{/* Background image */}
|
|
37
|
+
{backgroundImageUrl && !backgroundVideoUrl && (
|
|
38
|
+
<img
|
|
39
|
+
src={backgroundImageUrl}
|
|
40
|
+
alt=""
|
|
41
|
+
aria-hidden="true"
|
|
42
|
+
className="absolute inset-0 h-full w-full object-cover z-[1]"
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
|
|
46
|
+
{/* Background video */}
|
|
47
|
+
{backgroundVideoUrl && (
|
|
48
|
+
<video
|
|
49
|
+
className="absolute inset-0 h-full w-full object-cover z-[1]"
|
|
50
|
+
src={backgroundVideoUrl}
|
|
51
|
+
autoPlay
|
|
52
|
+
muted
|
|
53
|
+
loop
|
|
54
|
+
playsInline
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{/* Overlay tint */}
|
|
59
|
+
<div
|
|
60
|
+
className={cn(
|
|
61
|
+
"absolute inset-0 z-[2]",
|
|
62
|
+
hasBackground ? "bg-black/50" : "bg-black/20",
|
|
63
|
+
)}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
{/* Content */}
|
|
67
|
+
<div className="relative z-[3] flex flex-col gap-4 px-8 py-12 md:px-16 max-w-3xl">
|
|
68
|
+
<h1 className="text-[56px] font-bold text-white leading-none max-[900px]:text-[40px] max-[600px]:text-[32px]">
|
|
69
|
+
{title}
|
|
70
|
+
</h1>
|
|
71
|
+
<p className="text-body-medium text-white/80 leading-relaxed">
|
|
72
|
+
{description}
|
|
73
|
+
</p>
|
|
74
|
+
{watchVideoUrl && (
|
|
75
|
+
<div className="pt-2">
|
|
76
|
+
<Button variant="default" onClick={() => setWatchModalOpen(true)}>
|
|
77
|
+
<Play className="mr-2 size-4" />
|
|
78
|
+
Watch Now
|
|
79
|
+
</Button>
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{watchVideoUrl && (
|
|
86
|
+
<ResourceModal
|
|
87
|
+
open={watchModalOpen}
|
|
88
|
+
onClose={() => setWatchModalOpen(false)}
|
|
89
|
+
title={title}
|
|
90
|
+
videoUrl={watchVideoUrl}
|
|
91
|
+
/>
|
|
92
|
+
)}
|
|
93
|
+
</>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Button } from "@/components/ui/button";
|
|
3
|
+
import { Input } from "@/components/ui/input";
|
|
4
|
+
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import type { ResourceEmailEditorDialogProps } from "./types";
|
|
7
|
+
|
|
8
|
+
const EDITOR_CONTENT_TYPES = [
|
|
9
|
+
"Columns",
|
|
10
|
+
"Button",
|
|
11
|
+
"Divider",
|
|
12
|
+
"Heading",
|
|
13
|
+
"Paragraph",
|
|
14
|
+
"Image",
|
|
15
|
+
"Social",
|
|
16
|
+
"Menu",
|
|
17
|
+
"HTML",
|
|
18
|
+
] as const;
|
|
19
|
+
|
|
20
|
+
export function ResourceEmailEditorDialog({
|
|
21
|
+
open,
|
|
22
|
+
onClose,
|
|
23
|
+
onSave,
|
|
24
|
+
editorSlot,
|
|
25
|
+
}: ResourceEmailEditorDialogProps) {
|
|
26
|
+
const [templateName, setTemplateName] = useState("");
|
|
27
|
+
|
|
28
|
+
function handleSave() {
|
|
29
|
+
onSave(templateName);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function handleClose() {
|
|
33
|
+
setTemplateName("");
|
|
34
|
+
onClose();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Dialog open={open} onOpenChange={(v) => !v && handleClose()}>
|
|
39
|
+
<DialogContent
|
|
40
|
+
size="4xl"
|
|
41
|
+
className="flex flex-col gap-0 p-0 overflow-hidden h-[90vh]"
|
|
42
|
+
>
|
|
43
|
+
{/* Template name header */}
|
|
44
|
+
<div className="shrink-0 border-b border-border px-6 py-4">
|
|
45
|
+
<p className="text-label-small text-muted-foreground mb-1">
|
|
46
|
+
Template Name
|
|
47
|
+
</p>
|
|
48
|
+
<Input
|
|
49
|
+
placeholder="Enter template name"
|
|
50
|
+
value={templateName}
|
|
51
|
+
onChange={(e) => setTemplateName(e.target.value)}
|
|
52
|
+
className="border-none shadow-none px-0 text-body-medium h-auto focus-visible:ring-0 bg-transparent"
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
{/* Editor area */}
|
|
57
|
+
<div className="flex-1 overflow-hidden">
|
|
58
|
+
{editorSlot ?? <EditorPlaceholder />}
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Footer */}
|
|
62
|
+
<div className="shrink-0 flex items-center justify-between border-t border-border px-6 py-4">
|
|
63
|
+
<Button variant="outline" onClick={handleClose}>
|
|
64
|
+
Back
|
|
65
|
+
</Button>
|
|
66
|
+
<Button variant="default" onClick={handleSave}>
|
|
67
|
+
Save Email
|
|
68
|
+
</Button>
|
|
69
|
+
</div>
|
|
70
|
+
</DialogContent>
|
|
71
|
+
</Dialog>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Shown when no editorSlot is provided (e.g. in Storybook). */
|
|
76
|
+
function EditorPlaceholder() {
|
|
77
|
+
return (
|
|
78
|
+
<div className="flex h-full">
|
|
79
|
+
{/* Canvas */}
|
|
80
|
+
<div className="flex flex-1 items-center justify-center bg-[#f4f4f4]">
|
|
81
|
+
<div className="max-w-[500px] w-full border-2 border-dashed border-[#9ecbf0] bg-[#e8f4ff] px-16 py-10 text-center">
|
|
82
|
+
<p className="text-body-small text-[#5c9fd8]">
|
|
83
|
+
No content here. Drag content from right.
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
{/* Content sidebar */}
|
|
89
|
+
<div className="w-[280px] shrink-0 flex flex-col border-l border-border bg-background">
|
|
90
|
+
<div className="flex border-b border-border">
|
|
91
|
+
{(["Content", "Blocks", "Body"] as const).map((tab, i) => (
|
|
92
|
+
<button
|
|
93
|
+
key={tab}
|
|
94
|
+
type="button"
|
|
95
|
+
className={cn(
|
|
96
|
+
"flex flex-1 flex-col items-center gap-1 py-3 text-caption",
|
|
97
|
+
i === 0
|
|
98
|
+
? "text-foreground border-b-2 border-foreground"
|
|
99
|
+
: "text-muted-foreground",
|
|
100
|
+
)}
|
|
101
|
+
>
|
|
102
|
+
{tab}
|
|
103
|
+
</button>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div className="grid grid-cols-3 gap-2 p-3">
|
|
108
|
+
{EDITOR_CONTENT_TYPES.map((item) => (
|
|
109
|
+
<div
|
|
110
|
+
key={item}
|
|
111
|
+
className="flex flex-col items-center gap-1.5 border border-border p-2 hover:bg-muted transition-colors cursor-grab"
|
|
112
|
+
>
|
|
113
|
+
<div className="size-6 bg-foreground/10 flex items-center justify-center">
|
|
114
|
+
<span className="text-[10px] text-foreground/40">▤</span>
|
|
115
|
+
</div>
|
|
116
|
+
<span className="text-[11px] text-foreground leading-none text-center">
|
|
117
|
+
{item}
|
|
118
|
+
</span>
|
|
119
|
+
</div>
|
|
120
|
+
))}
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div className="mt-auto border-t border-border py-2.5 text-center">
|
|
124
|
+
<span className="text-caption text-muted-foreground">
|
|
125
|
+
⚡ by Unlayer Editor
|
|
126
|
+
</span>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Download } from "lucide-react";
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
4
|
+
import {
|
|
5
|
+
Dialog,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogHeader,
|
|
8
|
+
DialogTitle,
|
|
9
|
+
} from "@/components/ui/dialog";
|
|
10
|
+
import type { ResourceModalProps } from "./types";
|
|
11
|
+
|
|
12
|
+
export function ResourceModal({
|
|
13
|
+
open,
|
|
14
|
+
onClose,
|
|
15
|
+
title,
|
|
16
|
+
videoUrl,
|
|
17
|
+
tags,
|
|
18
|
+
attachments,
|
|
19
|
+
}: ResourceModalProps) {
|
|
20
|
+
return (
|
|
21
|
+
<Dialog open={open} onOpenChange={(v) => !v && onClose()}>
|
|
22
|
+
<DialogContent
|
|
23
|
+
size="2xl"
|
|
24
|
+
className="flex flex-col gap-4 p-0 overflow-hidden"
|
|
25
|
+
>
|
|
26
|
+
<DialogHeader className="px-6 pt-6 pb-0">
|
|
27
|
+
<DialogTitle className="text-h5">{title}</DialogTitle>
|
|
28
|
+
{tags && tags.length > 0 && (
|
|
29
|
+
<div className="flex flex-wrap gap-1.5 pt-1">
|
|
30
|
+
{tags.map((tag) => (
|
|
31
|
+
<Badge key={tag} variant="secondary">
|
|
32
|
+
{tag}
|
|
33
|
+
</Badge>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
)}
|
|
37
|
+
</DialogHeader>
|
|
38
|
+
|
|
39
|
+
{videoUrl && (
|
|
40
|
+
<div className="aspect-video w-full bg-foreground">
|
|
41
|
+
<iframe
|
|
42
|
+
src={videoUrl}
|
|
43
|
+
title={title}
|
|
44
|
+
className="h-full w-full"
|
|
45
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
46
|
+
allowFullScreen
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
)}
|
|
50
|
+
|
|
51
|
+
{attachments && attachments.length > 0 && (
|
|
52
|
+
<div className="flex flex-col gap-2 px-6 pb-6">
|
|
53
|
+
<p className="text-body-small font-medium text-foreground">
|
|
54
|
+
Attachments
|
|
55
|
+
</p>
|
|
56
|
+
<div className="flex flex-col gap-1">
|
|
57
|
+
{attachments.map((attachment, i) => (
|
|
58
|
+
<a
|
|
59
|
+
key={i}
|
|
60
|
+
href={attachment.url}
|
|
61
|
+
download
|
|
62
|
+
className="flex items-center gap-2 text-body-small text-primary hover:underline"
|
|
63
|
+
>
|
|
64
|
+
<Download className="size-3.5 shrink-0" />
|
|
65
|
+
{attachment.name}
|
|
66
|
+
</a>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
|
|
72
|
+
{(!attachments || attachments.length === 0) && <div className="pb-2" />}
|
|
73
|
+
</DialogContent>
|
|
74
|
+
</Dialog>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
|
|
3
|
+
export interface ResourceModalAttachment {
|
|
4
|
+
name: string;
|
|
5
|
+
url: string;
|
|
6
|
+
type?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ResourceModalProps {
|
|
10
|
+
open: boolean;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
title: string;
|
|
13
|
+
videoUrl?: string;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
attachments?: ResourceModalAttachment[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ResourceVideoItem {
|
|
19
|
+
id: string;
|
|
20
|
+
title: string;
|
|
21
|
+
thumbnailUrl?: string;
|
|
22
|
+
videoUrl?: string;
|
|
23
|
+
duration?: string;
|
|
24
|
+
tags?: string[];
|
|
25
|
+
attachments?: ResourceModalAttachment[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ResourceVideoCardProps {
|
|
29
|
+
video: ResourceVideoItem;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ResourceEmailTemplateItem {
|
|
33
|
+
id: string;
|
|
34
|
+
title: string;
|
|
35
|
+
htmlContent?: string;
|
|
36
|
+
type?: "system" | "company";
|
|
37
|
+
isAddTemplate?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ResourceEmailTemplateCardProps {
|
|
41
|
+
template: ResourceEmailTemplateItem;
|
|
42
|
+
onClick?: () => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ResourceEmailEditorDialogProps {
|
|
46
|
+
open: boolean;
|
|
47
|
+
onClose: () => void;
|
|
48
|
+
onSave: (templateName: string) => void;
|
|
49
|
+
/** Inject the real email editor (e.g. Unlayer) from the consuming app. */
|
|
50
|
+
editorSlot?: React.ReactNode;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ResourceDocumentItem {
|
|
54
|
+
id: string;
|
|
55
|
+
title: string;
|
|
56
|
+
thumbnailUrl?: string;
|
|
57
|
+
tags?: string[];
|
|
58
|
+
downloadUrl?: string;
|
|
59
|
+
pdfUrl?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ResourceDocumentCardProps {
|
|
63
|
+
document: ResourceDocumentItem;
|
|
64
|
+
onClick?: () => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ResourceCarouselProps {
|
|
68
|
+
title?: React.ReactNode;
|
|
69
|
+
items: React.ReactNode[];
|
|
70
|
+
headerAction?: React.ReactNode;
|
|
71
|
+
onViewModeChange?: (mode: "compact" | "grid") => void;
|
|
72
|
+
className?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ResourceCenterHeaderProps {
|
|
76
|
+
title: string;
|
|
77
|
+
description: string;
|
|
78
|
+
backgroundImageUrl?: string;
|
|
79
|
+
backgroundVideoUrl?: string;
|
|
80
|
+
watchVideoUrl?: string;
|
|
81
|
+
}
|