create-fluxstack 1.20.0 → 1.21.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.
@@ -1,252 +1,290 @@
1
- import { useState, useEffect } from 'react'
2
- import { Link, Outlet, useLocation } from 'react-router'
3
- import { FaBook, FaGithub, FaBars, FaTimes } from 'react-icons/fa'
4
- import FluxStackLogo from '@client/src/assets/fluxstack.svg'
5
- import faviconSvg from '@client/src/assets/fluxstack-static.svg?raw'
6
- import { useThemeClock } from '../hooks/useThemeClock'
7
- import { ThemePicker } from './ThemePicker'
8
- import type { ColorPalette } from '../lib/theme-clock'
9
- import { themeConfig } from '../config/theme.config'
10
-
11
- const navItems = [
12
- { to: '/', label: 'Home' },
13
- { to: '/counter', label: 'Counter' },
14
- { to: '/form', label: 'Form' },
15
- { to: '/upload', label: 'Upload' },
16
- { to: '/shared-counter', label: 'Shared Counter' },
17
- { to: '/room-chat', label: 'Room Chat' },
18
- { to: '/auth', label: 'Auth' },
19
- { to: '/ping-pong', label: 'Ping Pong' },
20
- { to: '/api-test', label: 'API Test' }
21
- ]
22
-
23
- const routeFlameHue: Record<string, string> = {
24
- '/': '0deg', // roxo original
25
- '/counter': '180deg', // ciano
26
- '/form': '300deg', // rosa
27
- '/upload': '60deg', // amarelo
28
- '/shared-counter': '120deg', // verde
29
- '/room-chat': '240deg', // azul
30
- '/auth': '330deg', // vermelho
31
- '/ping-pong': '200deg', // ciano-azul
32
- '/api-test': '90deg', // lima
33
- }
34
-
35
- // Cache favicon blob URLs by hue to avoid recreating blobs on every navigation.
36
- // Limited to MAX_FAVICON_CACHE entries; old blob URLs are revoked to prevent leaks.
37
- const MAX_FAVICON_CACHE = 20
38
- const faviconUrlCache = new Map<string, string>()
39
-
40
- export function AppLayout() {
41
- const location = useLocation()
42
- const [menuOpen, setMenuOpen] = useState(false)
43
- const autoTheme = useThemeClock()
44
- const [overrideTheme, setOverrideTheme] = useState<ColorPalette | null>(null)
45
- const theme = overrideTheme || autoTheme
46
-
47
- useEffect(() => {
48
- const current = navItems.find(item => item.to === location.pathname)
49
- document.title = current ? `${current.label} - FluxStack` : 'FluxStack'
50
-
51
- // Dynamic favicon with hue-rotate based on theme clock
52
- const hue = `${Math.round(theme.baseHue - 270)}deg`
53
- let url = faviconUrlCache.get(hue)
54
- if (!url) {
55
- // Evict oldest entry if cache is full, revoking blob URL to free memory
56
- if (faviconUrlCache.size >= MAX_FAVICON_CACHE) {
57
- const oldestKey = faviconUrlCache.keys().next().value!
58
- const oldestUrl = faviconUrlCache.get(oldestKey)!
59
- URL.revokeObjectURL(oldestUrl)
60
- faviconUrlCache.delete(oldestKey)
61
- }
62
- const colored = faviconSvg.replace(
63
- '<svg ',
64
- `<svg style="filter: hue-rotate(${hue})" `
65
- )
66
- const blob = new Blob([colored], { type: 'image/svg+xml' })
67
- url = URL.createObjectURL(blob)
68
- faviconUrlCache.set(hue, url)
69
- }
70
- let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]')
71
- if (!link) {
72
- link = document.createElement('link')
73
- link.rel = 'icon'
74
- document.head.appendChild(link)
75
- }
76
- link.type = 'image/svg+xml'
77
- link.href = url
78
- }, [location.pathname, theme.baseHue])
79
-
80
- return (
81
- <div className="min-h-screen text-white flex flex-col" style={{ backgroundColor: `oklch(8% 0.02 ${theme.baseHue})` }}>
82
- <header className="sticky top-0 z-50 backdrop-blur-xl bg-[#0a0a1a]/80 border-b border-white/[0.06]">
83
- <div className="container mx-auto px-4 sm:px-6 py-3 sm:py-4 flex items-center justify-between gap-4">
84
- <Link to="/" className="flex items-center gap-2 font-semibold tracking-wide">
85
- <img
86
- src={FluxStackLogo}
87
- alt="FluxStack"
88
- className="w-9 h-9 transition-[filter] duration-500"
89
- style={{
90
- filter: `hue-rotate(${theme.baseHue - 270}deg) drop-shadow(0 0 8px ${theme.primaryGlow})`,
91
- }}
92
- />
93
- <span
94
- className="bg-clip-text text-transparent"
95
- style={{ backgroundImage: theme.gradientPrimary }}
96
- >
97
- FluxStack
98
- </span>
99
- </Link>
100
-
101
- {/* Desktop nav */}
102
- <nav className="hidden md:flex items-center gap-2">
103
- {navItems.map((item) => {
104
- const active = location.pathname === item.to
105
- return (
106
- <Link
107
- key={item.to}
108
- to={item.to}
109
- className={`px-3 py-1.5 rounded-lg text-sm transition-all ${
110
- active
111
- ? 'font-medium'
112
- : 'text-gray-400 hover:text-white hover:bg-white/[0.05]'
113
- }`}
114
- style={active ? {
115
- backgroundColor: theme.primaryMuted,
116
- color: theme.textPrimary,
117
- } : undefined}
118
- >
119
- {item.label}
120
- </Link>
121
- )
122
- })}
123
- </nav>
124
-
125
- <div className="flex items-center gap-2">
126
- <a
127
- href="https://live-docs.marcosbrendon.com/"
128
- target="_blank"
129
- rel="noopener noreferrer"
130
- className="hidden sm:inline-flex items-center gap-2 px-3 py-1.5 bg-purple-500/20 border border-purple-500/20 text-purple-300 rounded-xl text-sm hover:bg-purple-500/30 transition-all"
131
- >
132
- <FaBook />
133
- Live Docs
134
- </a>
135
- <a
136
- href="/swagger"
137
- target="_blank"
138
- rel="noopener noreferrer"
139
- className="hidden sm:inline-flex items-center gap-2 px-3 py-1.5 bg-white/[0.03] border border-white/[0.06] text-gray-400 rounded-xl text-sm hover:bg-white/[0.06] hover:text-white transition-all"
140
- >
141
- <FaBook />
142
- API Docs
143
- </a>
144
- <a
145
- href="https://github.com/MarcosBrendonDePaula/FluxStack"
146
- target="_blank"
147
- rel="noopener noreferrer"
148
- className="hidden sm:inline-flex items-center gap-2 px-3 py-1.5 bg-white/[0.03] border border-white/[0.06] text-gray-400 rounded-xl text-sm hover:bg-white/[0.06] hover:text-white transition-all"
149
- >
150
- <FaGithub />
151
- GitHub
152
- </a>
153
-
154
- {/* Mobile menu toggle */}
155
- <button
156
- onClick={() => setMenuOpen(!menuOpen)}
157
- className="md:hidden p-2 text-gray-400 hover:text-white transition-colors"
158
- aria-label="Toggle menu"
159
- >
160
- {menuOpen ? <FaTimes size={20} /> : <FaBars size={20} />}
161
- </button>
162
- </div>
163
- </div>
164
-
165
- {/* Mobile nav */}
166
- {menuOpen && (
167
- <div className="md:hidden border-t border-white/[0.06] bg-[#0a0a1a]/90 backdrop-blur-xl">
168
- <nav className="container mx-auto px-4 py-3 flex gap-4 relative">
169
- <div className="flex flex-col gap-1 flex-1">
170
- {navItems.map((item) => {
171
- const active = location.pathname === item.to
172
- return (
173
- <Link
174
- key={item.to}
175
- to={item.to}
176
- onClick={() => setMenuOpen(false)}
177
- className={`px-3 py-2 rounded-lg text-sm transition-all ${
178
- active
179
- ? 'font-medium'
180
- : 'text-gray-400 hover:text-white hover:bg-white/[0.05]'
181
- }`}
182
- style={active ? {
183
- backgroundColor: theme.primaryMuted,
184
- color: theme.textPrimary,
185
- } : undefined}
186
- >
187
- {item.label}
188
- </Link>
189
- )
190
- })}
191
- <div className="flex flex-wrap gap-2 mt-2 pt-2 border-t border-white/[0.06]">
192
- <a
193
- href="https://live-docs.marcosbrendon.com/"
194
- target="_blank"
195
- rel="noopener noreferrer"
196
- className="inline-flex items-center gap-2 px-3 py-2 bg-purple-500/20 border border-purple-500/20 text-purple-300 rounded-xl text-sm hover:bg-purple-500/30 transition-all"
197
- >
198
- <FaBook />
199
- Live Docs
200
- </a>
201
- <a
202
- href="/swagger"
203
- target="_blank"
204
- rel="noopener noreferrer"
205
- className="inline-flex items-center gap-2 px-3 py-2 bg-white/[0.03] border border-white/[0.06] text-gray-400 rounded-xl text-sm hover:bg-white/[0.06] hover:text-white transition-all"
206
- >
207
- <FaBook />
208
- API Docs
209
- </a>
210
- <a
211
- href="https://github.com/MarcosBrendonDePaula/FluxStack"
212
- target="_blank"
213
- rel="noopener noreferrer"
214
- className="inline-flex items-center gap-2 px-3 py-2 bg-white/[0.03] border border-white/[0.06] text-gray-400 rounded-xl text-sm hover:bg-white/[0.06] hover:text-white transition-all"
215
- >
216
- <FaGithub />
217
- GitHub
218
- </a>
219
- </div>
220
- </div>
221
-
222
- {/* Logo floating right */}
223
- <img
224
- src={FluxStackLogo}
225
- alt=""
226
- className="absolute right-4 top-1/2 -translate-y-1/2 w-40 h-40 opacity-15 pointer-events-none transition-[filter] duration-500"
227
- style={{ filter: `hue-rotate(${routeFlameHue[location.pathname] || '0deg'})` }}
228
- />
229
- </nav>
230
- </div>
231
- )}
232
- </header>
233
-
234
- <main className="flex-1">
235
- <Outlet />
236
- </main>
237
-
238
- <footer className="border-t border-white/[0.06] py-6 mt-auto">
239
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
240
- <p className="text-gray-500 text-sm">
241
- Built with <span style={{ color: theme.primary }}>FluxStack</span> — Bun + Elysia + React
242
- </p>
243
- <p className="text-gray-600 text-xs mt-1">
244
- 🎨 <span style={{ color: theme.primary }}>{theme.period}</span> palette — colors shift with the time of day
245
- </p>
246
- </div>
247
- </footer>
248
-
249
- {themeConfig.showPicker && <ThemePicker palette={theme} onOverride={setOverrideTheme} />}
250
- </div>
251
- )
252
- }
1
+ import { useState, useEffect } from 'react'
2
+ import type { ReactNode } from 'react'
3
+ import { Link, Outlet, useLocation } from 'react-router'
4
+ import { FaBars, FaBook, FaChevronDown, FaExternalLinkAlt, FaGithub, FaTimes } from 'react-icons/fa'
5
+ import FluxStackLogo from '@client/src/assets/fluxstack.svg'
6
+ import faviconSvg from '@client/src/assets/fluxstack-static.svg?raw'
7
+ import { useThemeClock } from '../hooks/useThemeClock'
8
+ import { ThemePicker } from './ThemePicker'
9
+ import type { ColorPalette } from '../lib/theme-clock'
10
+ import { themeConfig } from '../config/theme.config'
11
+
12
+ const navItems = [
13
+ { to: '/', label: 'Home' },
14
+ ]
15
+
16
+ const demoItems = [
17
+ { to: '/counter', label: 'Counter' },
18
+ { to: '/form', label: 'Form' },
19
+
20
+ { to: '/room-chat', label: 'Room Chat' },
21
+ { to: '/auth', label: 'Auth' },
22
+ { to: '/ping-pong', label: 'Ping Pong' },
23
+ ]
24
+
25
+ const allRouteItems = [...navItems, ...demoItems]
26
+
27
+ const MAX_FAVICON_CACHE = 20
28
+ const faviconUrlCache = new Map<string, string>()
29
+
30
+ function HeaderLink({
31
+ href,
32
+ children,
33
+ subtle = false,
34
+ }: {
35
+ href: string
36
+ children: ReactNode
37
+ subtle?: boolean
38
+ }) {
39
+ return (
40
+ <a
41
+ href={href}
42
+ target="_blank"
43
+ rel="noopener noreferrer"
44
+ className={`hidden h-9 items-center gap-2 rounded-lg border px-3 text-sm transition sm:inline-flex ${
45
+ subtle
46
+ ? 'border-white/10 bg-white/[0.025] text-gray-400 hover:border-white/20 hover:bg-white/[0.05] hover:text-white'
47
+ : 'border-theme-active bg-theme-muted text-theme hover:shadow-theme'
48
+ }`}
49
+ >
50
+ {children}
51
+ </a>
52
+ )
53
+ }
54
+
55
+ export function AppLayout() {
56
+ const location = useLocation()
57
+ const [menuOpen, setMenuOpen] = useState(false)
58
+ const [demosOpen, setDemosOpen] = useState(false)
59
+ const autoTheme = useThemeClock()
60
+ const [overrideTheme, setOverrideTheme] = useState<ColorPalette | null>(null)
61
+ const theme = overrideTheme || autoTheme
62
+
63
+ useEffect(() => {
64
+ setDemosOpen(false)
65
+ setMenuOpen(false)
66
+ }, [location.pathname])
67
+
68
+ useEffect(() => {
69
+ const current = allRouteItems.find(item => item.to === location.pathname)
70
+ document.title = current ? `${current.label} - FluxStack` : 'FluxStack'
71
+
72
+ const hue = `${Math.round(theme.baseHue - 270)}deg`
73
+ let url = faviconUrlCache.get(hue)
74
+ if (!url) {
75
+ if (faviconUrlCache.size >= MAX_FAVICON_CACHE) {
76
+ const oldestKey = faviconUrlCache.keys().next().value!
77
+ const oldestUrl = faviconUrlCache.get(oldestKey)!
78
+ URL.revokeObjectURL(oldestUrl)
79
+ faviconUrlCache.delete(oldestKey)
80
+ }
81
+ const colored = faviconSvg.replace(
82
+ '<svg ',
83
+ `<svg style="filter: hue-rotate(${hue})" `
84
+ )
85
+ const blob = new Blob([colored], { type: 'image/svg+xml' })
86
+ url = URL.createObjectURL(blob)
87
+ faviconUrlCache.set(hue, url)
88
+ }
89
+ let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]')
90
+ if (!link) {
91
+ link = document.createElement('link')
92
+ link.rel = 'icon'
93
+ document.head.appendChild(link)
94
+ }
95
+ link.type = 'image/svg+xml'
96
+ link.href = url
97
+ }, [location.pathname, theme.baseHue])
98
+
99
+ const activeDemo = demoItems.some(item => item.to === location.pathname)
100
+
101
+ return (
102
+ <div
103
+ className="min-h-screen text-white"
104
+ style={{ backgroundColor: `oklch(7% 0.018 ${theme.baseHue})` }}
105
+ >
106
+ <header className="sticky top-0 z-50 border-b border-white/10 bg-black/45 backdrop-blur-2xl">
107
+ <div className="mx-auto flex h-16 max-w-7xl items-center justify-between gap-4 px-4 sm:px-6 lg:px-8">
108
+ <Link to="/" className="group flex min-w-0 items-center gap-3">
109
+ <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-white/10 bg-white/[0.04]">
110
+ <img
111
+ src={FluxStackLogo}
112
+ alt="FluxStack"
113
+ className="h-6 w-6 transition-[filter] duration-500"
114
+ style={{
115
+ filter: `hue-rotate(${theme.baseHue - 270}deg) drop-shadow(0 0 8px ${theme.primaryGlow})`,
116
+ }}
117
+ />
118
+ </span>
119
+ <span className="truncate text-sm font-semibold tracking-tight text-white">
120
+ FluxStack
121
+ </span>
122
+ </Link>
123
+
124
+ <nav className="hidden min-w-0 flex-1 items-center justify-center md:flex">
125
+ <div className="relative flex max-w-full items-center gap-1 rounded-lg border border-white/10 bg-white/[0.025] p-1">
126
+ {navItems.map((item) => {
127
+ const active = location.pathname === item.to
128
+ return (
129
+ <Link
130
+ key={item.to}
131
+ to={item.to}
132
+ className={`whitespace-nowrap rounded-md px-3 py-1.5 text-sm transition ${
133
+ active
134
+ ? 'bg-white text-black shadow-sm'
135
+ : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
136
+ }`}
137
+ >
138
+ {item.label}
139
+ </Link>
140
+ )
141
+ })}
142
+ <div className="relative">
143
+ <button
144
+ type="button"
145
+ onClick={() => setDemosOpen(open => !open)}
146
+ className={`inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-1.5 text-sm transition ${
147
+ activeDemo
148
+ ? 'bg-white text-black shadow-sm'
149
+ : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
150
+ }`}
151
+ aria-expanded={demosOpen}
152
+ >
153
+ Live Demos
154
+ <FaChevronDown className={`h-2.5 w-2.5 transition ${demosOpen ? 'rotate-180' : ''}`} />
155
+ </button>
156
+
157
+ {demosOpen && (
158
+ <div className="absolute left-1/2 top-11 z-50 w-72 -translate-x-1/2 rounded-lg border border-white/10 bg-[#07070b]/95 p-2 shadow-2xl shadow-black/40 backdrop-blur-2xl">
159
+ <div className="px-3 py-2">
160
+ <p className="text-xs font-semibold uppercase tracking-[0.18em] text-gray-500">Live Components</p>
161
+ <p className="mt-1 text-xs leading-5 text-gray-400">Demos de estado no servidor, salas e auth.</p>
162
+ </div>
163
+ <div className="mt-1 grid gap-1">
164
+ {demoItems.map((item) => {
165
+ const active = location.pathname === item.to
166
+ return (
167
+ <Link
168
+ key={item.to}
169
+ to={item.to}
170
+ className={`rounded-md px-3 py-2 text-sm transition ${
171
+ active
172
+ ? 'bg-white text-black'
173
+ : 'text-gray-300 hover:bg-white/[0.06] hover:text-white'
174
+ }`}
175
+ >
176
+ {item.label}
177
+ </Link>
178
+ )
179
+ })}
180
+ </div>
181
+ </div>
182
+ )}
183
+ </div>
184
+ </div>
185
+ </nav>
186
+
187
+ <div className="flex shrink-0 items-center gap-2">
188
+ <HeaderLink href="https://live-docs.marcosbrendon.com/">
189
+ <FaBook className="h-3.5 w-3.5" />
190
+ Live Docs
191
+ <FaExternalLinkAlt className="h-2.5 w-2.5 opacity-60" />
192
+ </HeaderLink>
193
+ <HeaderLink href="/swagger" subtle>
194
+ <FaBook className="h-3.5 w-3.5" />
195
+ API
196
+ </HeaderLink>
197
+ <HeaderLink href="https://github.com/MarcosBrendonDePaula/FluxStack" subtle>
198
+ <FaGithub className="h-3.5 w-3.5" />
199
+ GitHub
200
+ </HeaderLink>
201
+
202
+ <button
203
+ onClick={() => setMenuOpen(!menuOpen)}
204
+ className="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-white/10 bg-white/[0.03] text-gray-300 transition hover:bg-white/[0.06] hover:text-white md:hidden"
205
+ aria-label="Toggle menu"
206
+ >
207
+ {menuOpen ? <FaTimes size={16} /> : <FaBars size={16} />}
208
+ </button>
209
+ </div>
210
+ </div>
211
+
212
+ {menuOpen && (
213
+ <div className="border-t border-white/10 bg-black/70 backdrop-blur-2xl md:hidden">
214
+ <nav className="mx-auto grid max-w-7xl gap-1 px-4 py-3 sm:px-6">
215
+ {navItems.map((item) => {
216
+ const active = location.pathname === item.to
217
+ return (
218
+ <Link
219
+ key={item.to}
220
+ to={item.to}
221
+ onClick={() => setMenuOpen(false)}
222
+ className={`rounded-lg px-3 py-2 text-sm transition ${
223
+ active
224
+ ? 'bg-white text-black'
225
+ : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
226
+ }`}
227
+ >
228
+ {item.label}
229
+ </Link>
230
+ )
231
+ })}
232
+ <div className="mt-2 border-t border-white/10 pt-3">
233
+ <p className="px-3 pb-2 text-xs font-semibold uppercase tracking-[0.18em] text-gray-500">
234
+ Live Demos
235
+ </p>
236
+ <div className="grid gap-1">
237
+ {demoItems.map((item) => {
238
+ const active = location.pathname === item.to
239
+ return (
240
+ <Link
241
+ key={item.to}
242
+ to={item.to}
243
+ onClick={() => setMenuOpen(false)}
244
+ className={`rounded-lg px-3 py-2 text-sm transition ${
245
+ active
246
+ ? 'bg-white text-black'
247
+ : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
248
+ }`}
249
+ >
250
+ {item.label}
251
+ </Link>
252
+ )
253
+ })}
254
+ </div>
255
+ </div>
256
+ <div className="mt-2 grid gap-2 border-t border-white/10 pt-3 sm:grid-cols-3">
257
+ <a href="https://live-docs.marcosbrendon.com/" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-theme-active bg-theme-muted px-3 py-2 text-sm text-theme">
258
+ Live Docs
259
+ </a>
260
+ <a href="/swagger" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm text-gray-300">
261
+ API Docs
262
+ </a>
263
+ <a href="https://github.com/MarcosBrendonDePaula/FluxStack" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm text-gray-300">
264
+ GitHub
265
+ </a>
266
+ </div>
267
+ </nav>
268
+ </div>
269
+ )}
270
+ </header>
271
+
272
+ <main className="min-h-[calc(100vh-128px)]">
273
+ <Outlet />
274
+ </main>
275
+
276
+ <footer className="border-t border-white/10 bg-black/20 py-5">
277
+ <div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-2 px-4 text-center sm:flex-row sm:px-6 lg:px-8">
278
+ <p className="text-xs text-gray-500">
279
+ Built with <span style={{ color: theme.primary }}>FluxStack</span> - Bun + Elysia + React
280
+ </p>
281
+ <p className="text-xs text-gray-600">
282
+ <span style={{ color: theme.primary }}>{theme.period}</span> palette
283
+ </p>
284
+ </div>
285
+ </footer>
286
+
287
+ {themeConfig.showPicker && <ThemePicker palette={theme} onOverride={setOverrideTheme} />}
288
+ </div>
289
+ )
290
+ }
@@ -1,13 +1,16 @@
1
- import { useNavigate } from 'react-router'
2
-
3
- export function BackButton() {
4
- const navigate = useNavigate()
5
- return (
6
- <button
7
- onClick={() => navigate(-1)}
8
- className="px-4 py-2 bg-white/10 backdrop-blur-sm border border-white/20 text-white rounded-lg font-medium hover:bg-white/20 transition-all"
9
- >
10
- Voltar
11
- </button>
12
- )
13
- }
1
+ import { useNavigate } from 'react-router'
2
+ import { FaArrowLeft } from 'react-icons/fa6'
3
+
4
+ export function BackButton() {
5
+ const navigate = useNavigate()
6
+
7
+ return (
8
+ <button
9
+ onClick={() => navigate(-1)}
10
+ className="inline-flex items-center gap-2 rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm font-medium text-gray-300 transition hover:border-white/20 hover:bg-white/[0.06] hover:text-white"
11
+ >
12
+ <FaArrowLeft className="h-3 w-3" />
13
+ Back
14
+ </button>
15
+ )
16
+ }