@sundaysf/cli-v2 1.0.1 → 1.0.5

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 (191) hide show
  1. package/README.md +178 -178
  2. package/dist/README.md +178 -178
  3. package/dist/bin/generators/class.js.map +1 -1
  4. package/dist/bin/generators/postman.js.map +1 -1
  5. package/dist/bin/index.js +1 -1
  6. package/dist/bin/index.js.map +1 -1
  7. package/dist/templates/backend/.claude/agents/knex-table-implementer.md +113 -113
  8. package/dist/templates/backend/.claude/agents/sundays-backend-builder.md +70 -70
  9. package/dist/templates/backend/.claude/settings.local.json +13 -13
  10. package/dist/templates/backend/.env.example +13 -13
  11. package/dist/templates/backend/.prettierignore +2 -2
  12. package/dist/templates/backend/.prettierrc +9 -9
  13. package/dist/templates/backend/.sundaysrc +7 -0
  14. package/dist/templates/backend/CLAUDE.md +348 -348
  15. package/dist/templates/backend/Dockerfile +14 -14
  16. package/dist/templates/backend/README.md +18 -18
  17. package/dist/templates/backend/eslint.config.js +20 -20
  18. package/dist/templates/backend/src/app.ts +34 -34
  19. package/dist/templates/backend/src/common/config/origins/origins.config.ts +11 -11
  20. package/dist/templates/backend/src/common/utils/environment.resolver.ts +3 -3
  21. package/dist/templates/backend/src/common/utils/version.resolver.ts +4 -4
  22. package/dist/templates/backend/src/controllers/health/health.controller.ts +23 -23
  23. package/dist/templates/backend/src/middlewares/error/error.middleware.ts +21 -21
  24. package/dist/templates/backend/src/routes/health/health.router.ts +16 -16
  25. package/dist/templates/backend/src/routes/index.ts +57 -57
  26. package/dist/templates/backend/src/server.ts +16 -16
  27. package/dist/templates/backend/src/types.d.ts +10 -10
  28. package/dist/templates/backend/tsconfig.json +16 -16
  29. package/dist/templates/backend-db-sql/.claude/agents/knex-table-implementer.md +114 -114
  30. package/dist/templates/backend-db-sql/.claude/agents/sundays-backend-builder.md +70 -70
  31. package/dist/templates/backend-db-sql/.claude/settings.local.json +19 -19
  32. package/dist/templates/backend-db-sql/.env.example +13 -13
  33. package/dist/templates/backend-db-sql/.prettierignore +2 -2
  34. package/dist/templates/backend-db-sql/.prettierrc +9 -9
  35. package/dist/templates/backend-db-sql/.sundaysrc +7 -0
  36. package/dist/templates/backend-db-sql/CLAUDE.md +374 -374
  37. package/dist/templates/backend-db-sql/Dockerfile +17 -17
  38. package/dist/templates/backend-db-sql/README.md +34 -34
  39. package/dist/templates/backend-db-sql/db/knexfile.ts +33 -33
  40. package/dist/templates/backend-db-sql/db/migrations/001_create_sundays_package_version.ts +12 -12
  41. package/dist/templates/backend-db-sql/db/seeds/001_sundays_package_version_seed.ts +10 -10
  42. package/dist/templates/backend-db-sql/db/src/KnexConnection.ts +74 -74
  43. package/dist/templates/backend-db-sql/db/src/d.types.ts +18 -18
  44. package/dist/templates/backend-db-sql/db/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -71
  45. package/dist/templates/backend-db-sql/db/src/index.ts +9 -9
  46. package/dist/templates/backend-db-sql/db/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -6
  47. package/dist/templates/backend-db-sql/db/tsconfig.json +16 -16
  48. package/dist/templates/backend-db-sql/eslint.config.js +20 -20
  49. package/dist/templates/backend-db-sql/src/app.ts +34 -34
  50. package/dist/templates/backend-db-sql/src/common/config/origins/origins.config.ts +11 -11
  51. package/dist/templates/backend-db-sql/src/common/utils/environment.resolver.ts +3 -3
  52. package/dist/templates/backend-db-sql/src/common/utils/version.resolver.ts +4 -4
  53. package/dist/templates/backend-db-sql/src/controllers/health/health.controller.ts +23 -23
  54. package/dist/templates/backend-db-sql/src/middlewares/error/error.middleware.ts +21 -21
  55. package/dist/templates/backend-db-sql/src/routes/health/health.router.ts +16 -16
  56. package/dist/templates/backend-db-sql/src/routes/index.ts +57 -57
  57. package/dist/templates/backend-db-sql/src/server.ts +18 -18
  58. package/dist/templates/backend-db-sql/src/types.d.ts +10 -10
  59. package/dist/templates/backend-db-sql/tsconfig.json +16 -16
  60. package/dist/templates/backend-embedded-db-sql/.claude/agents/knex-table-implementer.md +116 -0
  61. package/dist/templates/backend-embedded-db-sql/.claude/agents/sundays-backend-builder.md +70 -0
  62. package/dist/templates/backend-embedded-db-sql/.claude/settings.local.json +18 -0
  63. package/dist/templates/backend-embedded-db-sql/.env.example +14 -0
  64. package/dist/templates/backend-embedded-db-sql/.prettierignore +3 -0
  65. package/dist/templates/backend-embedded-db-sql/.prettierrc +9 -0
  66. package/dist/templates/backend-embedded-db-sql/.sundaysrc +7 -0
  67. package/dist/templates/backend-embedded-db-sql/CLAUDE.md +371 -0
  68. package/dist/templates/backend-embedded-db-sql/Dockerfile +14 -0
  69. package/dist/templates/backend-embedded-db-sql/README.md +32 -0
  70. package/dist/templates/backend-embedded-db-sql/eslint.config.js +20 -0
  71. package/dist/templates/backend-embedded-db-sql/knexfile.ts +37 -0
  72. package/dist/templates/backend-embedded-db-sql/migrations/.gitkeep +0 -0
  73. package/dist/templates/backend-embedded-db-sql/migrations/001_create_sundays_package_version.ts +13 -0
  74. package/dist/templates/backend-embedded-db-sql/seeds/001_sundays_package_version_seed.ts +11 -0
  75. package/dist/templates/backend-embedded-db-sql/src/app.ts +35 -0
  76. package/dist/templates/backend-embedded-db-sql/src/common/config/origins/origins.config.ts +11 -0
  77. package/dist/templates/backend-embedded-db-sql/src/common/utils/environment.resolver.ts +4 -0
  78. package/dist/templates/backend-embedded-db-sql/src/common/utils/version.resolver.ts +5 -0
  79. package/dist/templates/backend-embedded-db-sql/src/controllers/health/health.controller.ts +24 -0
  80. package/dist/templates/backend-embedded-db-sql/src/db/KnexConnection.ts +74 -0
  81. package/dist/templates/backend-embedded-db-sql/src/db/d.types.ts +18 -0
  82. package/dist/templates/backend-embedded-db-sql/src/db/dao/sundays-package-version/sundays-package-version.dao.ts +71 -0
  83. package/dist/templates/backend-embedded-db-sql/src/db/index.ts +9 -0
  84. package/dist/templates/backend-embedded-db-sql/src/db/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -0
  85. package/dist/templates/backend-embedded-db-sql/src/middlewares/error/error.middleware.ts +21 -0
  86. package/dist/templates/backend-embedded-db-sql/src/routes/health/health.router.ts +17 -0
  87. package/dist/templates/backend-embedded-db-sql/src/routes/index.ts +57 -0
  88. package/dist/templates/backend-embedded-db-sql/src/server.ts +18 -0
  89. package/dist/templates/backend-embedded-db-sql/src/types.d.ts +10 -0
  90. package/dist/templates/backend-embedded-db-sql/tsconfig.json +16 -0
  91. package/dist/templates/db-sql/.claude/agents/knex-table-implementer.md +113 -113
  92. package/dist/templates/db-sql/.claude/agents/sundays-backend-builder.md +70 -70
  93. package/dist/templates/db-sql/.claude/settings.local.json +10 -10
  94. package/dist/templates/db-sql/.env.example +8 -8
  95. package/dist/templates/db-sql/CLAUDE.md +105 -105
  96. package/dist/templates/db-sql/knexfile.ts +33 -33
  97. package/dist/templates/db-sql/migrations/001_create_sundays_package_version.ts +12 -12
  98. package/dist/templates/db-sql/seeds/001_sundays_package_version_seed.ts +10 -10
  99. package/dist/templates/db-sql/src/KnexConnection.ts +74 -74
  100. package/dist/templates/db-sql/src/d.types.ts +18 -18
  101. package/dist/templates/db-sql/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -71
  102. package/dist/templates/db-sql/src/index.ts +9 -9
  103. package/dist/templates/db-sql/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -6
  104. package/dist/templates/db-sql/tsconfig.json +16 -16
  105. package/dist/templates/frontend-nextjs/.sundaysrc +7 -0
  106. package/dist/templates/frontend-nextjs/app/globals.css +125 -0
  107. package/dist/templates/frontend-nextjs/app/layout.tsx +45 -0
  108. package/dist/templates/frontend-nextjs/app/page.tsx +5 -0
  109. package/dist/templates/frontend-nextjs/components/project-generator.tsx +558 -0
  110. package/dist/templates/frontend-nextjs/components/theme-provider.tsx +11 -0
  111. package/dist/templates/frontend-nextjs/components/ui/accordion.tsx +66 -0
  112. package/dist/templates/frontend-nextjs/components/ui/alert-dialog.tsx +157 -0
  113. package/dist/templates/frontend-nextjs/components/ui/alert.tsx +66 -0
  114. package/dist/templates/frontend-nextjs/components/ui/aspect-ratio.tsx +11 -0
  115. package/dist/templates/frontend-nextjs/components/ui/avatar.tsx +53 -0
  116. package/dist/templates/frontend-nextjs/components/ui/badge.tsx +46 -0
  117. package/dist/templates/frontend-nextjs/components/ui/breadcrumb.tsx +109 -0
  118. package/dist/templates/frontend-nextjs/components/ui/button-group.tsx +83 -0
  119. package/dist/templates/frontend-nextjs/components/ui/button.tsx +60 -0
  120. package/dist/templates/frontend-nextjs/components/ui/calendar.tsx +213 -0
  121. package/dist/templates/frontend-nextjs/components/ui/card.tsx +92 -0
  122. package/dist/templates/frontend-nextjs/components/ui/carousel.tsx +241 -0
  123. package/dist/templates/frontend-nextjs/components/ui/chart.tsx +353 -0
  124. package/dist/templates/frontend-nextjs/components/ui/checkbox.tsx +32 -0
  125. package/dist/templates/frontend-nextjs/components/ui/collapsible.tsx +33 -0
  126. package/dist/templates/frontend-nextjs/components/ui/command.tsx +184 -0
  127. package/dist/templates/frontend-nextjs/components/ui/context-menu.tsx +252 -0
  128. package/dist/templates/frontend-nextjs/components/ui/dialog.tsx +143 -0
  129. package/dist/templates/frontend-nextjs/components/ui/drawer.tsx +135 -0
  130. package/dist/templates/frontend-nextjs/components/ui/dropdown-menu.tsx +257 -0
  131. package/dist/templates/frontend-nextjs/components/ui/empty.tsx +104 -0
  132. package/dist/templates/frontend-nextjs/components/ui/field.tsx +244 -0
  133. package/dist/templates/frontend-nextjs/components/ui/form.tsx +167 -0
  134. package/dist/templates/frontend-nextjs/components/ui/hover-card.tsx +44 -0
  135. package/dist/templates/frontend-nextjs/components/ui/input-group.tsx +169 -0
  136. package/dist/templates/frontend-nextjs/components/ui/input-otp.tsx +77 -0
  137. package/dist/templates/frontend-nextjs/components/ui/input.tsx +21 -0
  138. package/dist/templates/frontend-nextjs/components/ui/item.tsx +193 -0
  139. package/dist/templates/frontend-nextjs/components/ui/kbd.tsx +28 -0
  140. package/dist/templates/frontend-nextjs/components/ui/label.tsx +24 -0
  141. package/dist/templates/frontend-nextjs/components/ui/menubar.tsx +276 -0
  142. package/dist/templates/frontend-nextjs/components/ui/navigation-menu.tsx +166 -0
  143. package/dist/templates/frontend-nextjs/components/ui/pagination.tsx +127 -0
  144. package/dist/templates/frontend-nextjs/components/ui/popover.tsx +48 -0
  145. package/dist/templates/frontend-nextjs/components/ui/progress.tsx +31 -0
  146. package/dist/templates/frontend-nextjs/components/ui/radio-group.tsx +45 -0
  147. package/dist/templates/frontend-nextjs/components/ui/resizable.tsx +56 -0
  148. package/dist/templates/frontend-nextjs/components/ui/scroll-area.tsx +58 -0
  149. package/dist/templates/frontend-nextjs/components/ui/select.tsx +185 -0
  150. package/dist/templates/frontend-nextjs/components/ui/separator.tsx +28 -0
  151. package/dist/templates/frontend-nextjs/components/ui/sheet.tsx +139 -0
  152. package/dist/templates/frontend-nextjs/components/ui/sidebar.tsx +726 -0
  153. package/dist/templates/frontend-nextjs/components/ui/skeleton.tsx +13 -0
  154. package/dist/templates/frontend-nextjs/components/ui/slider.tsx +63 -0
  155. package/dist/templates/frontend-nextjs/components/ui/sonner.tsx +25 -0
  156. package/dist/templates/frontend-nextjs/components/ui/spinner.tsx +16 -0
  157. package/dist/templates/frontend-nextjs/components/ui/switch.tsx +31 -0
  158. package/dist/templates/frontend-nextjs/components/ui/table.tsx +116 -0
  159. package/dist/templates/frontend-nextjs/components/ui/tabs.tsx +66 -0
  160. package/dist/templates/frontend-nextjs/components/ui/textarea.tsx +18 -0
  161. package/dist/templates/frontend-nextjs/components/ui/toast.tsx +129 -0
  162. package/dist/templates/frontend-nextjs/components/ui/toaster.tsx +35 -0
  163. package/dist/templates/frontend-nextjs/components/ui/toggle-group.tsx +73 -0
  164. package/dist/templates/frontend-nextjs/components/ui/toggle.tsx +47 -0
  165. package/dist/templates/frontend-nextjs/components/ui/tooltip.tsx +61 -0
  166. package/dist/templates/frontend-nextjs/components/ui/use-mobile.tsx +19 -0
  167. package/dist/templates/frontend-nextjs/components/ui/use-toast.ts +191 -0
  168. package/dist/templates/frontend-nextjs/components.json +21 -0
  169. package/dist/templates/frontend-nextjs/hooks/use-mobile.ts +19 -0
  170. package/dist/templates/frontend-nextjs/hooks/use-toast.ts +191 -0
  171. package/dist/templates/frontend-nextjs/lib/utils.ts +6 -0
  172. package/dist/templates/frontend-nextjs/next.config.mjs +11 -0
  173. package/dist/templates/frontend-nextjs/postcss.config.mjs +8 -0
  174. package/dist/templates/frontend-nextjs/public/apple-icon.png +0 -0
  175. package/dist/templates/frontend-nextjs/public/icon-dark-32x32.png +0 -0
  176. package/dist/templates/frontend-nextjs/public/icon-light-32x32.png +0 -0
  177. package/dist/templates/frontend-nextjs/public/icon.svg +26 -0
  178. package/dist/templates/frontend-nextjs/public/placeholder-logo.png +0 -0
  179. package/dist/templates/frontend-nextjs/public/placeholder-logo.svg +1 -0
  180. package/dist/templates/frontend-nextjs/public/placeholder-user.jpg +0 -0
  181. package/dist/templates/frontend-nextjs/public/placeholder.jpg +0 -0
  182. package/dist/templates/frontend-nextjs/public/placeholder.svg +1 -0
  183. package/dist/templates/frontend-nextjs/styles/globals.css +125 -0
  184. package/dist/templates/frontend-nextjs/tsconfig.json +27 -0
  185. package/dist/templates/module/.claude/agents/knex-table-implementer.md +113 -113
  186. package/dist/templates/module/.claude/agents/sundays-backend-builder.md +70 -70
  187. package/dist/templates/module/.claude/settings.local.json +10 -10
  188. package/dist/templates/module/CLAUDE.md +158 -158
  189. package/dist/templates/module/src/index.ts +9 -9
  190. package/dist/templates/module/tsconfig.json +19 -19
  191. package/package.json +40 -40
@@ -0,0 +1,61 @@
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
+ function TooltipProvider({
9
+ delayDuration = 0,
10
+ ...props
11
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12
+ return (
13
+ <TooltipPrimitive.Provider
14
+ data-slot="tooltip-provider"
15
+ delayDuration={delayDuration}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ function Tooltip({
22
+ ...props
23
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
24
+ return (
25
+ <TooltipProvider>
26
+ <TooltipPrimitive.Root data-slot="tooltip" {...props} />
27
+ </TooltipProvider>
28
+ )
29
+ }
30
+
31
+ function TooltipTrigger({
32
+ ...props
33
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
34
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
35
+ }
36
+
37
+ function TooltipContent({
38
+ className,
39
+ sideOffset = 0,
40
+ children,
41
+ ...props
42
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
43
+ return (
44
+ <TooltipPrimitive.Portal>
45
+ <TooltipPrimitive.Content
46
+ data-slot="tooltip-content"
47
+ sideOffset={sideOffset}
48
+ className={cn(
49
+ 'bg-foreground text-background 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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
50
+ className,
51
+ )}
52
+ {...props}
53
+ >
54
+ {children}
55
+ <TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
56
+ </TooltipPrimitive.Content>
57
+ </TooltipPrimitive.Portal>
58
+ )
59
+ }
60
+
61
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -0,0 +1,19 @@
1
+ import * as React from 'react'
2
+
3
+ const MOBILE_BREAKPOINT = 768
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
+ }
13
+ mql.addEventListener('change', onChange)
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
+ return () => mql.removeEventListener('change', onChange)
16
+ }, [])
17
+
18
+ return !!isMobile
19
+ }
@@ -0,0 +1,191 @@
1
+ 'use client'
2
+
3
+ // Inspired by react-hot-toast library
4
+ import * as React from 'react'
5
+
6
+ import type { ToastActionElement, ToastProps } from '@/components/ui/toast'
7
+
8
+ const TOAST_LIMIT = 1
9
+ const TOAST_REMOVE_DELAY = 1000000
10
+
11
+ type ToasterToast = ToastProps & {
12
+ id: string
13
+ title?: React.ReactNode
14
+ description?: React.ReactNode
15
+ action?: ToastActionElement
16
+ }
17
+
18
+ const actionTypes = {
19
+ ADD_TOAST: 'ADD_TOAST',
20
+ UPDATE_TOAST: 'UPDATE_TOAST',
21
+ DISMISS_TOAST: 'DISMISS_TOAST',
22
+ REMOVE_TOAST: 'REMOVE_TOAST',
23
+ } as const
24
+
25
+ let count = 0
26
+
27
+ function genId() {
28
+ count = (count + 1) % Number.MAX_SAFE_INTEGER
29
+ return count.toString()
30
+ }
31
+
32
+ type ActionType = typeof actionTypes
33
+
34
+ type Action =
35
+ | {
36
+ type: ActionType['ADD_TOAST']
37
+ toast: ToasterToast
38
+ }
39
+ | {
40
+ type: ActionType['UPDATE_TOAST']
41
+ toast: Partial<ToasterToast>
42
+ }
43
+ | {
44
+ type: ActionType['DISMISS_TOAST']
45
+ toastId?: ToasterToast['id']
46
+ }
47
+ | {
48
+ type: ActionType['REMOVE_TOAST']
49
+ toastId?: ToasterToast['id']
50
+ }
51
+
52
+ interface State {
53
+ toasts: ToasterToast[]
54
+ }
55
+
56
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
57
+
58
+ const addToRemoveQueue = (toastId: string) => {
59
+ if (toastTimeouts.has(toastId)) {
60
+ return
61
+ }
62
+
63
+ const timeout = setTimeout(() => {
64
+ toastTimeouts.delete(toastId)
65
+ dispatch({
66
+ type: 'REMOVE_TOAST',
67
+ toastId: toastId,
68
+ })
69
+ }, TOAST_REMOVE_DELAY)
70
+
71
+ toastTimeouts.set(toastId, timeout)
72
+ }
73
+
74
+ export const reducer = (state: State, action: Action): State => {
75
+ switch (action.type) {
76
+ case 'ADD_TOAST':
77
+ return {
78
+ ...state,
79
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
80
+ }
81
+
82
+ case 'UPDATE_TOAST':
83
+ return {
84
+ ...state,
85
+ toasts: state.toasts.map((t) =>
86
+ t.id === action.toast.id ? { ...t, ...action.toast } : t,
87
+ ),
88
+ }
89
+
90
+ case 'DISMISS_TOAST': {
91
+ const { toastId } = action
92
+
93
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
94
+ // but I'll keep it here for simplicity
95
+ if (toastId) {
96
+ addToRemoveQueue(toastId)
97
+ } else {
98
+ state.toasts.forEach((toast) => {
99
+ addToRemoveQueue(toast.id)
100
+ })
101
+ }
102
+
103
+ return {
104
+ ...state,
105
+ toasts: state.toasts.map((t) =>
106
+ t.id === toastId || toastId === undefined
107
+ ? {
108
+ ...t,
109
+ open: false,
110
+ }
111
+ : t,
112
+ ),
113
+ }
114
+ }
115
+ case 'REMOVE_TOAST':
116
+ if (action.toastId === undefined) {
117
+ return {
118
+ ...state,
119
+ toasts: [],
120
+ }
121
+ }
122
+ return {
123
+ ...state,
124
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
125
+ }
126
+ }
127
+ }
128
+
129
+ const listeners: Array<(state: State) => void> = []
130
+
131
+ let memoryState: State = { toasts: [] }
132
+
133
+ function dispatch(action: Action) {
134
+ memoryState = reducer(memoryState, action)
135
+ listeners.forEach((listener) => {
136
+ listener(memoryState)
137
+ })
138
+ }
139
+
140
+ type Toast = Omit<ToasterToast, 'id'>
141
+
142
+ function toast({ ...props }: Toast) {
143
+ const id = genId()
144
+
145
+ const update = (props: ToasterToast) =>
146
+ dispatch({
147
+ type: 'UPDATE_TOAST',
148
+ toast: { ...props, id },
149
+ })
150
+ const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id })
151
+
152
+ dispatch({
153
+ type: 'ADD_TOAST',
154
+ toast: {
155
+ ...props,
156
+ id,
157
+ open: true,
158
+ onOpenChange: (open) => {
159
+ if (!open) dismiss()
160
+ },
161
+ },
162
+ })
163
+
164
+ return {
165
+ id: id,
166
+ dismiss,
167
+ update,
168
+ }
169
+ }
170
+
171
+ function useToast() {
172
+ const [state, setState] = React.useState<State>(memoryState)
173
+
174
+ React.useEffect(() => {
175
+ listeners.push(setState)
176
+ return () => {
177
+ const index = listeners.indexOf(setState)
178
+ if (index > -1) {
179
+ listeners.splice(index, 1)
180
+ }
181
+ }
182
+ }, [state])
183
+
184
+ return {
185
+ ...state,
186
+ toast,
187
+ dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
188
+ }
189
+ }
190
+
191
+ export { useToast, toast }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ },
20
+ "iconLibrary": "lucide"
21
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from 'react'
2
+
3
+ const MOBILE_BREAKPOINT = 768
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
+ }
13
+ mql.addEventListener('change', onChange)
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
+ return () => mql.removeEventListener('change', onChange)
16
+ }, [])
17
+
18
+ return !!isMobile
19
+ }
@@ -0,0 +1,191 @@
1
+ 'use client'
2
+
3
+ // Inspired by react-hot-toast library
4
+ import * as React from 'react'
5
+
6
+ import type { ToastActionElement, ToastProps } from '@/components/ui/toast'
7
+
8
+ const TOAST_LIMIT = 1
9
+ const TOAST_REMOVE_DELAY = 1000000
10
+
11
+ type ToasterToast = ToastProps & {
12
+ id: string
13
+ title?: React.ReactNode
14
+ description?: React.ReactNode
15
+ action?: ToastActionElement
16
+ }
17
+
18
+ const actionTypes = {
19
+ ADD_TOAST: 'ADD_TOAST',
20
+ UPDATE_TOAST: 'UPDATE_TOAST',
21
+ DISMISS_TOAST: 'DISMISS_TOAST',
22
+ REMOVE_TOAST: 'REMOVE_TOAST',
23
+ } as const
24
+
25
+ let count = 0
26
+
27
+ function genId() {
28
+ count = (count + 1) % Number.MAX_SAFE_INTEGER
29
+ return count.toString()
30
+ }
31
+
32
+ type ActionType = typeof actionTypes
33
+
34
+ type Action =
35
+ | {
36
+ type: ActionType['ADD_TOAST']
37
+ toast: ToasterToast
38
+ }
39
+ | {
40
+ type: ActionType['UPDATE_TOAST']
41
+ toast: Partial<ToasterToast>
42
+ }
43
+ | {
44
+ type: ActionType['DISMISS_TOAST']
45
+ toastId?: ToasterToast['id']
46
+ }
47
+ | {
48
+ type: ActionType['REMOVE_TOAST']
49
+ toastId?: ToasterToast['id']
50
+ }
51
+
52
+ interface State {
53
+ toasts: ToasterToast[]
54
+ }
55
+
56
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
57
+
58
+ const addToRemoveQueue = (toastId: string) => {
59
+ if (toastTimeouts.has(toastId)) {
60
+ return
61
+ }
62
+
63
+ const timeout = setTimeout(() => {
64
+ toastTimeouts.delete(toastId)
65
+ dispatch({
66
+ type: 'REMOVE_TOAST',
67
+ toastId: toastId,
68
+ })
69
+ }, TOAST_REMOVE_DELAY)
70
+
71
+ toastTimeouts.set(toastId, timeout)
72
+ }
73
+
74
+ export const reducer = (state: State, action: Action): State => {
75
+ switch (action.type) {
76
+ case 'ADD_TOAST':
77
+ return {
78
+ ...state,
79
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
80
+ }
81
+
82
+ case 'UPDATE_TOAST':
83
+ return {
84
+ ...state,
85
+ toasts: state.toasts.map((t) =>
86
+ t.id === action.toast.id ? { ...t, ...action.toast } : t,
87
+ ),
88
+ }
89
+
90
+ case 'DISMISS_TOAST': {
91
+ const { toastId } = action
92
+
93
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
94
+ // but I'll keep it here for simplicity
95
+ if (toastId) {
96
+ addToRemoveQueue(toastId)
97
+ } else {
98
+ state.toasts.forEach((toast) => {
99
+ addToRemoveQueue(toast.id)
100
+ })
101
+ }
102
+
103
+ return {
104
+ ...state,
105
+ toasts: state.toasts.map((t) =>
106
+ t.id === toastId || toastId === undefined
107
+ ? {
108
+ ...t,
109
+ open: false,
110
+ }
111
+ : t,
112
+ ),
113
+ }
114
+ }
115
+ case 'REMOVE_TOAST':
116
+ if (action.toastId === undefined) {
117
+ return {
118
+ ...state,
119
+ toasts: [],
120
+ }
121
+ }
122
+ return {
123
+ ...state,
124
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
125
+ }
126
+ }
127
+ }
128
+
129
+ const listeners: Array<(state: State) => void> = []
130
+
131
+ let memoryState: State = { toasts: [] }
132
+
133
+ function dispatch(action: Action) {
134
+ memoryState = reducer(memoryState, action)
135
+ listeners.forEach((listener) => {
136
+ listener(memoryState)
137
+ })
138
+ }
139
+
140
+ type Toast = Omit<ToasterToast, 'id'>
141
+
142
+ function toast({ ...props }: Toast) {
143
+ const id = genId()
144
+
145
+ const update = (props: ToasterToast) =>
146
+ dispatch({
147
+ type: 'UPDATE_TOAST',
148
+ toast: { ...props, id },
149
+ })
150
+ const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id })
151
+
152
+ dispatch({
153
+ type: 'ADD_TOAST',
154
+ toast: {
155
+ ...props,
156
+ id,
157
+ open: true,
158
+ onOpenChange: (open) => {
159
+ if (!open) dismiss()
160
+ },
161
+ },
162
+ })
163
+
164
+ return {
165
+ id: id,
166
+ dismiss,
167
+ update,
168
+ }
169
+ }
170
+
171
+ function useToast() {
172
+ const [state, setState] = React.useState<State>(memoryState)
173
+
174
+ React.useEffect(() => {
175
+ listeners.push(setState)
176
+ return () => {
177
+ const index = listeners.indexOf(setState)
178
+ if (index > -1) {
179
+ listeners.splice(index, 1)
180
+ }
181
+ }
182
+ }, [state])
183
+
184
+ return {
185
+ ...state,
186
+ toast,
187
+ dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
188
+ }
189
+ }
190
+
191
+ export { useToast, toast }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,11 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ typescript: {
4
+ ignoreBuildErrors: true,
5
+ },
6
+ images: {
7
+ unoptimized: true,
8
+ },
9
+ }
10
+
11
+ export default nextConfig
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ '@tailwindcss/postcss': {},
5
+ },
6
+ }
7
+
8
+ export default config
@@ -0,0 +1,26 @@
1
+ <svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <style>
3
+ @media (prefers-color-scheme: light) {
4
+ .background { fill: black; }
5
+ .foreground { fill: white; }
6
+ }
7
+ @media (prefers-color-scheme: dark) {
8
+ .background { fill: white; }
9
+ .foreground { fill: black; }
10
+ }
11
+ </style>
12
+ <g clip-path="url(#clip0_7960_43945)">
13
+ <rect class="background" width="180" height="180" rx="37" />
14
+ <g style="transform: scale(95%); transform-origin: center">
15
+ <path class="foreground"
16
+ d="M101.141 53H136.632C151.023 53 162.689 64.6662 162.689 79.0573V112.904H148.112V79.0573C148.112 78.7105 148.098 78.3662 148.072 78.0251L112.581 112.898C112.701 112.902 112.821 112.904 112.941 112.904H148.112V126.672H112.941C98.5504 126.672 86.5638 114.891 86.5638 100.5V66.7434H101.141V100.5C101.141 101.15 101.191 101.792 101.289 102.422L137.56 66.7816C137.255 66.7563 136.945 66.7434 136.632 66.7434H101.141V53Z" />
17
+ <path class="foreground"
18
+ d="M65.2926 124.136L14 66.7372H34.6355L64.7495 100.436V66.7372H80.1365V118.47C80.1365 126.278 70.4953 129.958 65.2926 124.136Z" />
19
+ </g>
20
+ </g>
21
+ <defs>
22
+ <clipPath id="clip0_7960_43945">
23
+ <rect width="180" height="180" fill="white" />
24
+ </clipPath>
25
+ </defs>
26
+ </svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="215" height="48" fill="none"><path fill="#000" d="M57.588 9.6h6L73.828 38h-5.2l-2.36-6.88h-11.36L52.548 38h-5.2l10.24-28.4Zm7.16 17.16-4.16-12.16-4.16 12.16h8.32Zm23.694-2.24c-.186-1.307-.706-2.32-1.56-3.04-.853-.72-1.866-1.08-3.04-1.08-1.68 0-2.986.613-3.92 1.84-.906 1.227-1.36 2.947-1.36 5.16s.454 3.933 1.36 5.16c.934 1.227 2.24 1.84 3.92 1.84 1.254 0 2.307-.373 3.16-1.12.854-.773 1.387-1.867 1.6-3.28l5.12.24c-.186 1.68-.733 3.147-1.64 4.4-.906 1.227-2.08 2.173-3.52 2.84-1.413.667-2.986 1-4.72 1-2.08 0-3.906-.453-5.48-1.36-1.546-.907-2.76-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84 0-2.24.427-4.187 1.28-5.84.88-1.68 2.094-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.68 0 3.227.32 4.64.96 1.414.64 2.56 1.56 3.44 2.76.907 1.2 1.454 2.6 1.64 4.2l-5.12.28Zm11.486-7.72.12 3.4c.534-1.227 1.307-2.173 2.32-2.84 1.04-.693 2.267-1.04 3.68-1.04 1.494 0 2.76.387 3.8 1.16 1.067.747 1.827 1.813 2.28 3.2.507-1.44 1.294-2.52 2.36-3.24 1.094-.747 2.414-1.12 3.96-1.12 1.414 0 2.64.307 3.68.92s1.84 1.52 2.4 2.72c.56 1.2.84 2.667.84 4.4V38h-4.96V25.92c0-1.813-.293-3.187-.88-4.12-.56-.96-1.413-1.44-2.56-1.44-.906 0-1.68.213-2.32.64-.64.427-1.133 1.053-1.48 1.88-.32.827-.48 1.84-.48 3.04V38h-4.56V25.92c0-1.2-.133-2.213-.4-3.04-.24-.827-.626-1.453-1.16-1.88-.506-.427-1.133-.64-1.88-.64-.906 0-1.68.227-2.32.68-.64.427-1.133 1.053-1.48 1.88-.32.827-.48 1.827-.48 3V38h-4.96V16.8h4.48Zm26.723 10.6c0-2.24.427-4.187 1.28-5.84.854-1.68 2.067-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.84 0 3.494.413 4.96 1.24 1.467.827 2.64 2.08 3.52 3.76.88 1.653 1.347 3.693 1.4 6.12v1.32h-15.08c.107 1.813.614 3.227 1.52 4.24.907.987 2.134 1.48 3.68 1.48.987 0 1.88-.253 2.68-.76a4.803 4.803 0 0 0 1.84-2.2l5.08.36c-.64 2.027-1.84 3.64-3.6 4.84-1.733 1.173-3.733 1.76-6 1.76-2.08 0-3.906-.453-5.48-1.36-1.573-.907-2.786-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84Zm15.16-2.04c-.213-1.733-.76-3.013-1.64-3.84-.853-.827-1.893-1.24-3.12-1.24-1.44 0-2.6.453-3.48 1.36-.88.88-1.44 2.12-1.68 3.72h9.92ZM163.139 9.6V38h-5.04V9.6h5.04Zm8.322 7.2.24 5.88-.64-.36c.32-2.053 1.094-3.56 2.32-4.52 1.254-.987 2.787-1.48 4.6-1.48 2.32 0 4.107.733 5.36 2.2 1.254 1.44 1.88 3.387 1.88 5.84V38h-4.96V25.92c0-1.253-.12-2.28-.36-3.08-.24-.8-.64-1.413-1.2-1.84-.533-.427-1.253-.64-2.16-.64-1.44 0-2.573.48-3.4 1.44-.8.933-1.2 2.307-1.2 4.12V38h-4.96V16.8h4.48Zm30.003 7.72c-.186-1.307-.706-2.32-1.56-3.04-.853-.72-1.866-1.08-3.04-1.08-1.68 0-2.986.613-3.92 1.84-.906 1.227-1.36 2.947-1.36 5.16s.454 3.933 1.36 5.16c.934 1.227 2.24 1.84 3.92 1.84 1.254 0 2.307-.373 3.16-1.12.854-.773 1.387-1.867 1.6-3.28l5.12.24c-.186 1.68-.733 3.147-1.64 4.4-.906 1.227-2.08 2.173-3.52 2.84-1.413.667-2.986 1-4.72 1-2.08 0-3.906-.453-5.48-1.36-1.546-.907-2.76-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84 0-2.24.427-4.187 1.28-5.84.88-1.68 2.094-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.68 0 3.227.32 4.64.96 1.414.64 2.56 1.56 3.44 2.76.907 1.2 1.454 2.6 1.64 4.2l-5.12.28Zm11.443 8.16V38h-5.6v-5.32h5.6Z"/><path fill="#171717" fill-rule="evenodd" d="m7.839 40.783 16.03-28.054L20 6 0 40.783h7.839Zm8.214 0H40L27.99 19.894l-4.02 7.032 3.976 6.914H20.02l-3.967 6.943Z" clip-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" fill="none"><rect width="1200" height="1200" fill="#EAEAEA" rx="3"/><g opacity=".5"><g opacity=".5"><path fill="#FAFAFA" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/></g><path stroke="url(#a)" stroke-width="2.418" d="M0-1.209h553.581" transform="scale(1 -1) rotate(45 1163.11 91.165)"/><path stroke="url(#b)" stroke-width="2.418" d="M404.846 598.671h391.726"/><path stroke="url(#c)" stroke-width="2.418" d="M599.5 795.742V404.017"/><path stroke="url(#d)" stroke-width="2.418" d="m795.717 796.597-391.441-391.44"/><path fill="#fff" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/><g clip-path="url(#e)"><path fill="#666" fill-rule="evenodd" d="M616.426 586.58h-31.434v16.176l3.553-3.554.531-.531h9.068l.074-.074 8.463-8.463h2.565l7.18 7.181V586.58Zm-15.715 14.654 3.698 3.699 1.283 1.282-2.565 2.565-1.282-1.283-5.2-5.199h-6.066l-5.514 5.514-.073.073v2.876a2.418 2.418 0 0 0 2.418 2.418h26.598a2.418 2.418 0 0 0 2.418-2.418v-8.317l-8.463-8.463-7.181 7.181-.071.072Zm-19.347 5.442v4.085a6.045 6.045 0 0 0 6.046 6.045h26.598a6.044 6.044 0 0 0 6.045-6.045v-7.108l1.356-1.355-1.282-1.283-.074-.073v-17.989h-38.689v23.43l-.146.146.146.147Z" clip-rule="evenodd"/></g><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/></g><defs><linearGradient id="a" x1="554.061" x2="-.48" y1=".083" y2=".087" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="b" x1="796.912" x2="404.507" y1="599.963" y2="599.965" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="600.792" x2="600.794" y1="403.677" y2="796.082" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="d" x1="404.85" x2="796.972" y1="403.903" y2="796.02" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><clipPath id="e"><path fill="#fff" d="M581.364 580.535h38.689v38.689h-38.689z"/></clipPath></defs></svg>