hazo_notify 1.0.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/.cursor/rules/db_schema.mdc +0 -0
- package/.cursor/rules/design.mdc +16 -0
- package/.cursor/rules/general.mdc +49 -0
- package/README.md +765 -0
- package/components/emailer-html-editor.tsx +94 -0
- package/components/ui/button.tsx +53 -0
- package/components/ui/card.tsx +78 -0
- package/components/ui/input.tsx +24 -0
- package/components/ui/label.tsx +21 -0
- package/components/ui/sidebar.tsx +121 -0
- package/components/ui/spinner.tsx +54 -0
- package/components/ui/textarea.tsx +23 -0
- package/components/ui/tooltip.tsx +30 -0
- package/components.json +20 -0
- package/hazo_notify_config.ini +153 -0
- package/jest.config.js +27 -0
- package/jest.setup.js +1 -0
- package/next.config.js +22 -0
- package/package.json +72 -0
- package/postcss.config.js +6 -0
- package/src/app/api/hazo_notify/emailer/send/__tests__/route.test.ts +227 -0
- package/src/app/api/hazo_notify/emailer/send/route.ts +537 -0
- package/src/app/editor-00/page.tsx +47 -0
- package/src/app/globals.css +69 -0
- package/src/app/hazo_notify/emailer_test/layout.tsx +53 -0
- package/src/app/hazo_notify/emailer_test/page.tsx +369 -0
- package/src/app/hazo_notify/layout.tsx +77 -0
- package/src/app/hazo_notify/page.tsx +12 -0
- package/src/app/layout.tsx +26 -0
- package/src/app/page.tsx +14 -0
- package/src/components/blocks/editor-00/editor.tsx +61 -0
- package/src/components/blocks/editor-00/nodes.ts +11 -0
- package/src/components/blocks/editor-00/plugins.tsx +36 -0
- package/src/components/editor/editor-ui/content-editable.tsx +34 -0
- package/src/components/editor/themes/editor-theme.css +91 -0
- package/src/components/editor/themes/editor-theme.ts +130 -0
- package/src/components/ui/button.tsx +53 -0
- package/src/components/ui/card.tsx +78 -0
- package/src/components/ui/input.tsx +24 -0
- package/src/components/ui/label.tsx +21 -0
- package/src/components/ui/sidebar.tsx +121 -0
- package/src/components/ui/spinner.tsx +54 -0
- package/src/components/ui/textarea.tsx +23 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/lib/emailer/__tests__/emailer.test.ts +200 -0
- package/src/lib/emailer/emailer.ts +263 -0
- package/src/lib/emailer/index.ts +11 -0
- package/src/lib/emailer/providers/__tests__/zeptomail_provider.test.ts +196 -0
- package/src/lib/emailer/providers/index.ts +33 -0
- package/src/lib/emailer/providers/pop3_provider.ts +30 -0
- package/src/lib/emailer/providers/smtp_provider.ts +30 -0
- package/src/lib/emailer/providers/zeptomail_provider.ts +299 -0
- package/src/lib/emailer/types.ts +119 -0
- package/src/lib/emailer/utils/constants.ts +24 -0
- package/src/lib/emailer/utils/index.ts +9 -0
- package/src/lib/emailer/utils/logger.ts +71 -0
- package/src/lib/emailer/utils/validation.ts +84 -0
- package/src/lib/index.ts +6 -0
- package/src/lib/utils.ts +6 -0
- package/tailwind.config.ts +65 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
.EditorTheme__code {
|
|
2
|
+
background-color: transparent;
|
|
3
|
+
font-family: Menlo, Consolas, Monaco, monospace;
|
|
4
|
+
display: block;
|
|
5
|
+
padding: 8px 8px 8px 52px;
|
|
6
|
+
line-height: 1.53;
|
|
7
|
+
font-size: 13px;
|
|
8
|
+
margin: 0;
|
|
9
|
+
margin-top: 8px;
|
|
10
|
+
margin-bottom: 8px;
|
|
11
|
+
overflow-x: auto;
|
|
12
|
+
border: 1px solid #ccc;
|
|
13
|
+
position: relative;
|
|
14
|
+
border-radius: 8px;
|
|
15
|
+
tab-size: 2;
|
|
16
|
+
}
|
|
17
|
+
.EditorTheme__code:before {
|
|
18
|
+
content: attr(data-gutter);
|
|
19
|
+
position: absolute;
|
|
20
|
+
background-color: transparent;
|
|
21
|
+
border-right: 1px solid #ccc;
|
|
22
|
+
left: 0;
|
|
23
|
+
top: 0;
|
|
24
|
+
padding: 8px;
|
|
25
|
+
color: #777;
|
|
26
|
+
white-space: pre-wrap;
|
|
27
|
+
text-align: right;
|
|
28
|
+
min-width: 25px;
|
|
29
|
+
}
|
|
30
|
+
.EditorTheme__table {
|
|
31
|
+
border-collapse: collapse;
|
|
32
|
+
border-spacing: 0;
|
|
33
|
+
overflow-y: scroll;
|
|
34
|
+
overflow-x: scroll;
|
|
35
|
+
table-layout: fixed;
|
|
36
|
+
width: fit-content;
|
|
37
|
+
width: 100%;
|
|
38
|
+
margin: 0px 0px 30px 0px;
|
|
39
|
+
}
|
|
40
|
+
.EditorTheme__tokenComment {
|
|
41
|
+
color: slategray;
|
|
42
|
+
}
|
|
43
|
+
.EditorTheme__tokenPunctuation {
|
|
44
|
+
color: #999;
|
|
45
|
+
}
|
|
46
|
+
.EditorTheme__tokenProperty {
|
|
47
|
+
color: #905;
|
|
48
|
+
}
|
|
49
|
+
.EditorTheme__tokenSelector {
|
|
50
|
+
color: #690;
|
|
51
|
+
}
|
|
52
|
+
.EditorTheme__tokenOperator {
|
|
53
|
+
color: #9a6e3a;
|
|
54
|
+
}
|
|
55
|
+
.EditorTheme__tokenAttr {
|
|
56
|
+
color: #07a;
|
|
57
|
+
}
|
|
58
|
+
.EditorTheme__tokenVariable {
|
|
59
|
+
color: #e90;
|
|
60
|
+
}
|
|
61
|
+
.EditorTheme__tokenFunction {
|
|
62
|
+
color: #dd4a68;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.Collapsible__container {
|
|
66
|
+
background-color: var(--background);
|
|
67
|
+
border: 1px solid #ccc;
|
|
68
|
+
border-radius: 0.5rem;
|
|
69
|
+
margin-bottom: 0.5rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.Collapsible__title{
|
|
73
|
+
padding: 0.25rem;
|
|
74
|
+
padding-left: 1rem;
|
|
75
|
+
position: relative;
|
|
76
|
+
font-weight: bold;
|
|
77
|
+
outline: none;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
list-style-type: disclosure-closed;
|
|
80
|
+
list-style-position: inside;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.Collapsible__title p{
|
|
84
|
+
display: inline-flex;
|
|
85
|
+
}
|
|
86
|
+
.Collapsible__title::marker{
|
|
87
|
+
color: lightgray;
|
|
88
|
+
}
|
|
89
|
+
.Collapsible__container[open] >.Collapsible__title {
|
|
90
|
+
list-style-type: disclosure-open;
|
|
91
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { EditorThemeClasses } from "lexical"
|
|
2
|
+
|
|
3
|
+
import "./editor-theme.css"
|
|
4
|
+
|
|
5
|
+
export const editorTheme: EditorThemeClasses = {
|
|
6
|
+
ltr: "text-left",
|
|
7
|
+
rtl: "text-right",
|
|
8
|
+
heading: {
|
|
9
|
+
h1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl",
|
|
10
|
+
h2: "scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0",
|
|
11
|
+
h3: "scroll-m-20 text-2xl font-semibold tracking-tight",
|
|
12
|
+
h4: "scroll-m-20 text-xl font-semibold tracking-tight",
|
|
13
|
+
h5: "scroll-m-20 text-lg font-semibold tracking-tight",
|
|
14
|
+
h6: "scroll-m-20 text-base font-semibold tracking-tight",
|
|
15
|
+
},
|
|
16
|
+
paragraph: "leading-7 [&:not(:first-child)]:mt-6",
|
|
17
|
+
quote: "mt-6 border-l-2 pl-6 italic",
|
|
18
|
+
link: "text-blue-600 hover:underline hover:cursor-pointer",
|
|
19
|
+
list: {
|
|
20
|
+
checklist: "relative",
|
|
21
|
+
listitem: "mx-8",
|
|
22
|
+
listitemChecked:
|
|
23
|
+
'relative mx-2 px-6 list-none outline-none line-through before:content-[""] before:w-4 before:h-4 before:top-0.5 before:left-0 before:cursor-pointer before:block before:bg-cover before:absolute before:border before:border-primary before:rounded before:bg-primary before:bg-no-repeat after:content-[""] after:cursor-pointer after:border-white after:border-solid after:absolute after:block after:top-[6px] after:w-[3px] after:left-[7px] after:right-[7px] after:h-[6px] after:rotate-45 after:border-r-2 after:border-b-2 after:border-l-0 after:border-t-0',
|
|
24
|
+
listitemUnchecked:
|
|
25
|
+
'relative mx-2 px-6 list-none outline-none before:content-[""] before:w-4 before:h-4 before:top-0.5 before:left-0 before:cursor-pointer before:block before:bg-cover before:absolute before:border before:border-primary before:rounded',
|
|
26
|
+
nested: {
|
|
27
|
+
listitem: "list-none before:hidden after:hidden",
|
|
28
|
+
},
|
|
29
|
+
ol: "m-0 p-0 list-decimal [&>li]:mt-2",
|
|
30
|
+
olDepth: [
|
|
31
|
+
"list-outside !list-decimal",
|
|
32
|
+
"list-outside !list-[upper-roman]",
|
|
33
|
+
"list-outside !list-[lower-roman]",
|
|
34
|
+
"list-outside !list-[upper-alpha]",
|
|
35
|
+
"list-outside !list-[lower-alpha]",
|
|
36
|
+
],
|
|
37
|
+
ul: "m-0 p-0 list-outside [&>li]:mt-2",
|
|
38
|
+
ulDepth: [
|
|
39
|
+
"list-outside !list-disc",
|
|
40
|
+
"list-outside !list-disc",
|
|
41
|
+
"list-outside !list-disc",
|
|
42
|
+
"list-outside !list-disc",
|
|
43
|
+
"list-outside !list-disc",
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
hashtag: "text-blue-600 bg-blue-100 rounded-md px-1",
|
|
47
|
+
text: {
|
|
48
|
+
bold: "font-bold",
|
|
49
|
+
code: "bg-gray-100 p-1 rounded-md",
|
|
50
|
+
italic: "italic",
|
|
51
|
+
strikethrough: "line-through",
|
|
52
|
+
subscript: "sub",
|
|
53
|
+
superscript: "sup",
|
|
54
|
+
underline: "underline",
|
|
55
|
+
underlineStrikethrough: "underline line-through",
|
|
56
|
+
},
|
|
57
|
+
image: "relative inline-block user-select-none cursor-default editor-image",
|
|
58
|
+
inlineImage:
|
|
59
|
+
"relative inline-block user-select-none cursor-default inline-editor-image",
|
|
60
|
+
keyword: "text-purple-900 font-bold",
|
|
61
|
+
code: "EditorTheme__code",
|
|
62
|
+
codeHighlight: {
|
|
63
|
+
atrule: "EditorTheme__tokenAttr",
|
|
64
|
+
attr: "EditorTheme__tokenAttr",
|
|
65
|
+
boolean: "EditorTheme__tokenProperty",
|
|
66
|
+
builtin: "EditorTheme__tokenSelector",
|
|
67
|
+
cdata: "EditorTheme__tokenComment",
|
|
68
|
+
char: "EditorTheme__tokenSelector",
|
|
69
|
+
class: "EditorTheme__tokenFunction",
|
|
70
|
+
"class-name": "EditorTheme__tokenFunction",
|
|
71
|
+
comment: "EditorTheme__tokenComment",
|
|
72
|
+
constant: "EditorTheme__tokenProperty",
|
|
73
|
+
deleted: "EditorTheme__tokenProperty",
|
|
74
|
+
doctype: "EditorTheme__tokenComment",
|
|
75
|
+
entity: "EditorTheme__tokenOperator",
|
|
76
|
+
function: "EditorTheme__tokenFunction",
|
|
77
|
+
important: "EditorTheme__tokenVariable",
|
|
78
|
+
inserted: "EditorTheme__tokenSelector",
|
|
79
|
+
keyword: "EditorTheme__tokenAttr",
|
|
80
|
+
namespace: "EditorTheme__tokenVariable",
|
|
81
|
+
number: "EditorTheme__tokenProperty",
|
|
82
|
+
operator: "EditorTheme__tokenOperator",
|
|
83
|
+
prolog: "EditorTheme__tokenComment",
|
|
84
|
+
property: "EditorTheme__tokenProperty",
|
|
85
|
+
punctuation: "EditorTheme__tokenPunctuation",
|
|
86
|
+
regex: "EditorTheme__tokenVariable",
|
|
87
|
+
selector: "EditorTheme__tokenSelector",
|
|
88
|
+
string: "EditorTheme__tokenSelector",
|
|
89
|
+
symbol: "EditorTheme__tokenProperty",
|
|
90
|
+
tag: "EditorTheme__tokenProperty",
|
|
91
|
+
url: "EditorTheme__tokenOperator",
|
|
92
|
+
variable: "EditorTheme__tokenVariable",
|
|
93
|
+
},
|
|
94
|
+
characterLimit: "!bg-destructive/50",
|
|
95
|
+
table: "EditorTheme__table w-fit overflow-scroll border-collapse",
|
|
96
|
+
tableCell:
|
|
97
|
+
'EditorTheme__tableCell w-24 relative border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right"',
|
|
98
|
+
tableCellActionButton:
|
|
99
|
+
"EditorTheme__tableCellActionButton bg-background block border-0 rounded-2xl w-5 h-5 text-foreground cursor-pointer",
|
|
100
|
+
tableCellActionButtonContainer:
|
|
101
|
+
"EditorTheme__tableCellActionButtonContainer block right-1 top-1.5 absolute z-10 w-5 h-5",
|
|
102
|
+
tableCellEditing: "EditorTheme__tableCellEditing rounded-sm shadow-sm",
|
|
103
|
+
tableCellHeader:
|
|
104
|
+
"EditorTheme__tableCellHeader bg-muted border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right",
|
|
105
|
+
tableCellPrimarySelected:
|
|
106
|
+
"EditorTheme__tableCellPrimarySelected border border-primary border-solid block h-[calc(100%-2px)] w-[calc(100%-2px)] absolute -left-[1px] -top-[1px] z-10 ",
|
|
107
|
+
tableCellResizer:
|
|
108
|
+
"EditorTheme__tableCellResizer absolute -right-1 h-full w-2 cursor-ew-resize z-10 top-0",
|
|
109
|
+
tableCellSelected: "EditorTheme__tableCellSelected bg-muted",
|
|
110
|
+
tableCellSortedIndicator:
|
|
111
|
+
"EditorTheme__tableCellSortedIndicator block opacity-50 bsolute bottom-0 left-0 w-full h-1 bg-muted",
|
|
112
|
+
tableResizeRuler:
|
|
113
|
+
"EditorTheme__tableCellResizeRuler block absolute w-[1px] h-full bg-primary top-0",
|
|
114
|
+
tableRowStriping:
|
|
115
|
+
"EditorTheme__tableRowStriping m-0 border-t p-0 even:bg-muted",
|
|
116
|
+
tableSelected: "EditorTheme__tableSelected ring-2 ring-primary ring-offset-2",
|
|
117
|
+
tableSelection: "EditorTheme__tableSelection bg-transparent",
|
|
118
|
+
layoutItem: "border border-dashed px-4 py-2",
|
|
119
|
+
layoutContainer: "grid gap-2.5 my-2.5 mx-0",
|
|
120
|
+
autocomplete: "text-muted-foreground",
|
|
121
|
+
blockCursor: "",
|
|
122
|
+
embedBlock: {
|
|
123
|
+
base: "user-select-none",
|
|
124
|
+
focus: "ring-2 ring-primary ring-offset-2",
|
|
125
|
+
},
|
|
126
|
+
hr: 'p-0.5 border-none my-1 mx-0 cursor-pointer after:content-[""] after:block after:h-0.5 after:bg-muted selected:ring-2 selected:ring-primary selected:ring-offset-2 selected:user-select-none',
|
|
127
|
+
indent: "[--lexical-indent-base-value:40px]",
|
|
128
|
+
mark: "",
|
|
129
|
+
markOverlap: "",
|
|
130
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
const button_variants = cva(
|
|
6
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
11
|
+
destructive:
|
|
12
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
13
|
+
outline:
|
|
14
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
15
|
+
secondary:
|
|
16
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
17
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
default: "h-10 px-4 py-2",
|
|
22
|
+
sm: "h-9 rounded-md px-3",
|
|
23
|
+
lg: "h-11 rounded-md px-8",
|
|
24
|
+
icon: "h-10 w-10",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
variant: "default",
|
|
29
|
+
size: "default",
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
export interface ButtonProps
|
|
35
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
36
|
+
VariantProps<typeof button_variants> {
|
|
37
|
+
asChild?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
41
|
+
({ className, variant, size, ...props }, ref) => {
|
|
42
|
+
return (
|
|
43
|
+
<button
|
|
44
|
+
className={cn(button_variants({ variant, size, className }))}
|
|
45
|
+
ref={ref}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
Button.displayName = "Button"
|
|
52
|
+
|
|
53
|
+
export { Button, button_variants }
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
const Card = React.forwardRef<
|
|
5
|
+
HTMLDivElement,
|
|
6
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
7
|
+
>(({ className, ...props }, ref) => (
|
|
8
|
+
<div
|
|
9
|
+
ref={ref}
|
|
10
|
+
className={cn(
|
|
11
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
))
|
|
17
|
+
Card.displayName = "Card"
|
|
18
|
+
|
|
19
|
+
const CardHeader = React.forwardRef<
|
|
20
|
+
HTMLDivElement,
|
|
21
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
22
|
+
>(({ className, ...props }, ref) => (
|
|
23
|
+
<div
|
|
24
|
+
ref={ref}
|
|
25
|
+
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
))
|
|
29
|
+
CardHeader.displayName = "CardHeader"
|
|
30
|
+
|
|
31
|
+
const CardTitle = React.forwardRef<
|
|
32
|
+
HTMLParagraphElement,
|
|
33
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
34
|
+
>(({ className, ...props }, ref) => (
|
|
35
|
+
<h3
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cn(
|
|
38
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
))
|
|
44
|
+
CardTitle.displayName = "CardTitle"
|
|
45
|
+
|
|
46
|
+
const CardDescription = React.forwardRef<
|
|
47
|
+
HTMLParagraphElement,
|
|
48
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
49
|
+
>(({ className, ...props }, ref) => (
|
|
50
|
+
<p
|
|
51
|
+
ref={ref}
|
|
52
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
))
|
|
56
|
+
CardDescription.displayName = "CardDescription"
|
|
57
|
+
|
|
58
|
+
const CardContent = React.forwardRef<
|
|
59
|
+
HTMLDivElement,
|
|
60
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
61
|
+
>(({ className, ...props }, ref) => (
|
|
62
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
63
|
+
))
|
|
64
|
+
CardContent.displayName = "CardContent"
|
|
65
|
+
|
|
66
|
+
const CardFooter = React.forwardRef<
|
|
67
|
+
HTMLDivElement,
|
|
68
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
69
|
+
>(({ className, ...props }, ref) => (
|
|
70
|
+
<div
|
|
71
|
+
ref={ref}
|
|
72
|
+
className={cn("flex items-center p-6 pt-0", className)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
))
|
|
76
|
+
CardFooter.displayName = "CardFooter"
|
|
77
|
+
|
|
78
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export interface InputProps
|
|
5
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
6
|
+
|
|
7
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
8
|
+
({ className, type, ...props }, ref) => {
|
|
9
|
+
return (
|
|
10
|
+
<input
|
|
11
|
+
type={type}
|
|
12
|
+
className={cn(
|
|
13
|
+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
ref={ref}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
Input.displayName = "Input"
|
|
23
|
+
|
|
24
|
+
export { Input }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export interface LabelProps
|
|
5
|
+
extends React.LabelHTMLAttributes<HTMLLabelElement> {}
|
|
6
|
+
|
|
7
|
+
const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
|
|
8
|
+
({ className, ...props }, ref) => (
|
|
9
|
+
<label
|
|
10
|
+
ref={ref}
|
|
11
|
+
className={cn(
|
|
12
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
)
|
|
19
|
+
Label.displayName = "Label"
|
|
20
|
+
|
|
21
|
+
export { Label }
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
children: React.ReactNode
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const Sidebar = React.forwardRef<HTMLDivElement, SidebarProps>(
|
|
9
|
+
({ className, children, ...props }, ref) => (
|
|
10
|
+
<div
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn(
|
|
13
|
+
"flex h-screen w-64 flex-col border-r bg-card text-card-foreground",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
>
|
|
18
|
+
{children}
|
|
19
|
+
</div>
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
Sidebar.displayName = "Sidebar"
|
|
23
|
+
|
|
24
|
+
const SidebarHeader = React.forwardRef<
|
|
25
|
+
HTMLDivElement,
|
|
26
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
27
|
+
>(({ className, ...props }, ref) => (
|
|
28
|
+
<div
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn("flex h-16 items-center border-b px-6", className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
))
|
|
34
|
+
SidebarHeader.displayName = "SidebarHeader"
|
|
35
|
+
|
|
36
|
+
const SidebarContent = React.forwardRef<
|
|
37
|
+
HTMLDivElement,
|
|
38
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
39
|
+
>(({ className, ...props }, ref) => (
|
|
40
|
+
<div
|
|
41
|
+
ref={ref}
|
|
42
|
+
className={cn("flex-1 overflow-y-auto px-4 py-4", className)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
))
|
|
46
|
+
SidebarContent.displayName = "SidebarContent"
|
|
47
|
+
|
|
48
|
+
const SidebarFooter = React.forwardRef<
|
|
49
|
+
HTMLDivElement,
|
|
50
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
51
|
+
>(({ className, ...props }, ref) => (
|
|
52
|
+
<div
|
|
53
|
+
ref={ref}
|
|
54
|
+
className={cn("border-t px-4 py-4", className)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
))
|
|
58
|
+
SidebarFooter.displayName = "SidebarFooter"
|
|
59
|
+
|
|
60
|
+
const SidebarNav = React.forwardRef<
|
|
61
|
+
HTMLElement,
|
|
62
|
+
React.HTMLAttributes<HTMLElement>
|
|
63
|
+
>(({ className, ...props }, ref) => (
|
|
64
|
+
<nav
|
|
65
|
+
ref={ref as React.Ref<HTMLElement>}
|
|
66
|
+
className={cn("space-y-1", className)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
))
|
|
70
|
+
SidebarNav.displayName = "SidebarNav"
|
|
71
|
+
|
|
72
|
+
const SidebarNavItem = React.forwardRef<
|
|
73
|
+
HTMLLIElement,
|
|
74
|
+
React.HTMLAttributes<HTMLLIElement>
|
|
75
|
+
>(({ className, ...props }, ref) => (
|
|
76
|
+
<li
|
|
77
|
+
ref={ref}
|
|
78
|
+
className={cn("", className)}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
))
|
|
82
|
+
SidebarNavItem.displayName = "SidebarNavItem"
|
|
83
|
+
|
|
84
|
+
interface SidebarNavLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
85
|
+
active?: boolean
|
|
86
|
+
asChild?: boolean
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const SidebarNavLink = React.forwardRef<HTMLAnchorElement, SidebarNavLinkProps>(
|
|
90
|
+
({ className, active, asChild, children, ...props }, ref) => {
|
|
91
|
+
if (asChild) {
|
|
92
|
+
return <>{children}</>;
|
|
93
|
+
}
|
|
94
|
+
return (
|
|
95
|
+
<a
|
|
96
|
+
ref={ref}
|
|
97
|
+
className={cn(
|
|
98
|
+
"flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
|
|
99
|
+
active
|
|
100
|
+
? "bg-accent text-accent-foreground"
|
|
101
|
+
: "text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
102
|
+
className
|
|
103
|
+
)}
|
|
104
|
+
{...props}
|
|
105
|
+
>
|
|
106
|
+
{children}
|
|
107
|
+
</a>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
SidebarNavLink.displayName = "SidebarNavLink"
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
Sidebar,
|
|
115
|
+
SidebarHeader,
|
|
116
|
+
SidebarContent,
|
|
117
|
+
SidebarFooter,
|
|
118
|
+
SidebarNav,
|
|
119
|
+
SidebarNavItem,
|
|
120
|
+
SidebarNavLink,
|
|
121
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export interface SpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
size?: "sm" | "md" | "lg"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const Spinner = React.forwardRef<HTMLDivElement, SpinnerProps>(
|
|
9
|
+
({ className, size = "md", ...props }, ref) => {
|
|
10
|
+
const size_classes = {
|
|
11
|
+
sm: "h-4 w-4",
|
|
12
|
+
md: "h-8 w-8",
|
|
13
|
+
lg: "h-12 w-12",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn("inline-block", className)}
|
|
20
|
+
{...props}
|
|
21
|
+
role="status"
|
|
22
|
+
aria-label="Loading"
|
|
23
|
+
>
|
|
24
|
+
<svg
|
|
25
|
+
className={cn(
|
|
26
|
+
"animate-spin text-primary",
|
|
27
|
+
size_classes[size]
|
|
28
|
+
)}
|
|
29
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
30
|
+
fill="none"
|
|
31
|
+
viewBox="0 0 24 24"
|
|
32
|
+
>
|
|
33
|
+
<circle
|
|
34
|
+
className="opacity-25"
|
|
35
|
+
cx="12"
|
|
36
|
+
cy="12"
|
|
37
|
+
r="10"
|
|
38
|
+
stroke="currentColor"
|
|
39
|
+
strokeWidth="4"
|
|
40
|
+
/>
|
|
41
|
+
<path
|
|
42
|
+
className="opacity-75"
|
|
43
|
+
fill="currentColor"
|
|
44
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
45
|
+
/>
|
|
46
|
+
</svg>
|
|
47
|
+
<span className="sr-only">Loading...</span>
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
Spinner.displayName = "Spinner"
|
|
53
|
+
|
|
54
|
+
export { Spinner }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export interface TextareaProps
|
|
5
|
+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
|
6
|
+
|
|
7
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
8
|
+
({ className, ...props }, ref) => {
|
|
9
|
+
return (
|
|
10
|
+
<textarea
|
|
11
|
+
className={cn(
|
|
12
|
+
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
ref={ref}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
Textarea.displayName = "Textarea"
|
|
22
|
+
|
|
23
|
+
export { Textarea }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
const TooltipProvider = TooltipPrimitive.Provider
|
|
9
|
+
|
|
10
|
+
const Tooltip = TooltipPrimitive.Root
|
|
11
|
+
|
|
12
|
+
const TooltipTrigger = TooltipPrimitive.Trigger
|
|
13
|
+
|
|
14
|
+
const TooltipContent = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
17
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
18
|
+
<TooltipPrimitive.Content
|
|
19
|
+
ref={ref}
|
|
20
|
+
sideOffset={sideOffset}
|
|
21
|
+
className={cn(
|
|
22
|
+
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
))
|
|
28
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
29
|
+
|
|
30
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|