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.
Files changed (61) hide show
  1. package/.cursor/rules/db_schema.mdc +0 -0
  2. package/.cursor/rules/design.mdc +16 -0
  3. package/.cursor/rules/general.mdc +49 -0
  4. package/README.md +765 -0
  5. package/components/emailer-html-editor.tsx +94 -0
  6. package/components/ui/button.tsx +53 -0
  7. package/components/ui/card.tsx +78 -0
  8. package/components/ui/input.tsx +24 -0
  9. package/components/ui/label.tsx +21 -0
  10. package/components/ui/sidebar.tsx +121 -0
  11. package/components/ui/spinner.tsx +54 -0
  12. package/components/ui/textarea.tsx +23 -0
  13. package/components/ui/tooltip.tsx +30 -0
  14. package/components.json +20 -0
  15. package/hazo_notify_config.ini +153 -0
  16. package/jest.config.js +27 -0
  17. package/jest.setup.js +1 -0
  18. package/next.config.js +22 -0
  19. package/package.json +72 -0
  20. package/postcss.config.js +6 -0
  21. package/src/app/api/hazo_notify/emailer/send/__tests__/route.test.ts +227 -0
  22. package/src/app/api/hazo_notify/emailer/send/route.ts +537 -0
  23. package/src/app/editor-00/page.tsx +47 -0
  24. package/src/app/globals.css +69 -0
  25. package/src/app/hazo_notify/emailer_test/layout.tsx +53 -0
  26. package/src/app/hazo_notify/emailer_test/page.tsx +369 -0
  27. package/src/app/hazo_notify/layout.tsx +77 -0
  28. package/src/app/hazo_notify/page.tsx +12 -0
  29. package/src/app/layout.tsx +26 -0
  30. package/src/app/page.tsx +14 -0
  31. package/src/components/blocks/editor-00/editor.tsx +61 -0
  32. package/src/components/blocks/editor-00/nodes.ts +11 -0
  33. package/src/components/blocks/editor-00/plugins.tsx +36 -0
  34. package/src/components/editor/editor-ui/content-editable.tsx +34 -0
  35. package/src/components/editor/themes/editor-theme.css +91 -0
  36. package/src/components/editor/themes/editor-theme.ts +130 -0
  37. package/src/components/ui/button.tsx +53 -0
  38. package/src/components/ui/card.tsx +78 -0
  39. package/src/components/ui/input.tsx +24 -0
  40. package/src/components/ui/label.tsx +21 -0
  41. package/src/components/ui/sidebar.tsx +121 -0
  42. package/src/components/ui/spinner.tsx +54 -0
  43. package/src/components/ui/textarea.tsx +23 -0
  44. package/src/components/ui/tooltip.tsx +30 -0
  45. package/src/lib/emailer/__tests__/emailer.test.ts +200 -0
  46. package/src/lib/emailer/emailer.ts +263 -0
  47. package/src/lib/emailer/index.ts +11 -0
  48. package/src/lib/emailer/providers/__tests__/zeptomail_provider.test.ts +196 -0
  49. package/src/lib/emailer/providers/index.ts +33 -0
  50. package/src/lib/emailer/providers/pop3_provider.ts +30 -0
  51. package/src/lib/emailer/providers/smtp_provider.ts +30 -0
  52. package/src/lib/emailer/providers/zeptomail_provider.ts +299 -0
  53. package/src/lib/emailer/types.ts +119 -0
  54. package/src/lib/emailer/utils/constants.ts +24 -0
  55. package/src/lib/emailer/utils/index.ts +9 -0
  56. package/src/lib/emailer/utils/logger.ts +71 -0
  57. package/src/lib/emailer/utils/validation.ts +84 -0
  58. package/src/lib/index.ts +6 -0
  59. package/src/lib/utils.ts +6 -0
  60. package/tailwind.config.ts +65 -0
  61. 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 }