Skip to content

Commit be3d7d8

Browse files
authored
feat(clerk): add Clerk for auth and protected route (#146)
* feat: implement clerk auth * refactor: extract authenticated route layout * fix: add signUpFallbackRedirectUrl in ClerkProvider * fix: add default email address in clerk sign in * feat: add user-management flow with Clerk auth * refactor: extract learn-more component * chore: add learn more button in clerk auth layout * fix: update nav title for Clerk * fix: add example env file * feat(clerk): add fallback UI when publishable key is missing * docs(clerk): add clerk in readme
1 parent f8fa601 commit be3d7d8

19 files changed

Lines changed: 868 additions & 36 deletions

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VITE_CLERK_PUBLISHABLE_KEY=

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ dist
1212
dist-ssr
1313
*.local
1414

15+
.env
16+
1517
# Editor directories and files
1618
.vscode/*
1719
!.vscode/extensions.json

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ I've been creating dashboard UIs at work and for my personal projects. I always
3232

3333
**Icons:** [Tabler Icons](https://tabler.io/icons)
3434

35+
**Auth (partial):** [Clerk](https://go.clerk.com/GttUAaK)
36+
3537
## Run Locally
3638

3739
Clone the project
@@ -58,6 +60,16 @@ Start the server
5860
pnpm run dev
5961
```
6062

63+
## Sponsoring this project ❤️
64+
65+
If you find this project helpful or use this in your own work, consider [sponsoring me](https://github.com/sponsors/satnaing) to support development and maintenance. You can [buy me a coffee](https://buymeacoffee.com/satnaing) as well. Don’t worry, every penny helps. Thank you! 🙏
66+
67+
For questions or sponsorship inquiries, feel free to reach out at [contact@satnaing.dev](mailto:contact@satnaing.dev).
68+
69+
### Current Sponsor
70+
71+
- [Clerk](https://go.clerk.com/GttUAaK) - for backing the implementation of Clerk in this project
72+
6173
## Author
6274

6375
Crafted with 🤍 by [@satnaing](https://github.com/satnaing)

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"knip": "knip"
1414
},
1515
"dependencies": {
16+
"@clerk/clerk-react": "^5.31.4",
1617
"@hookform/resolvers": "^5.0.1",
1718
"@radix-ui/react-alert-dialog": "^1.1.7",
1819
"@radix-ui/react-avatar": "^1.1.4",

pnpm-lock.yaml

Lines changed: 77 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/assets/clerk-full-logo.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { SVGProps } from 'react'
2+
3+
export function ClerkFullLogo(props: SVGProps<SVGSVGElement>) {
4+
return (
5+
<svg
6+
width={77}
7+
height={24}
8+
viewBox='0 0 77 24'
9+
fill='none'
10+
xmlns='http://www.w3.org/2000/svg'
11+
{...props}
12+
>
13+
<path
14+
d='M35.148 16.738a4.198 4.198 0 01-3.06 1.283 3.53 3.53 0 01-2.604-1.034c-.619-.645-.975-1.566-.975-2.665 0-2.199 1.432-3.703 3.58-3.703a3.914 3.914 0 013.034 1.377l1.859-1.644c-1.211-1.47-3.176-2.229-5.042-2.229-3.652 0-6.24 2.517-6.24 6.22 0 1.831.643 3.374 1.728 4.463s2.631 1.728 4.415 1.728c2.317 0 4.166-.94 5.203-2.122l-1.898-1.674zM38.727 3.428h2.766V20.34h-2.766V3.428zM54.818 15.283c.046-.368.07-.74.076-1.11 0-3.507-2.296-6.047-5.847-6.047a5.738 5.738 0 00-4.215 1.725c-1.038 1.089-1.66 2.631-1.66 4.47 0 3.749 2.642 6.216 6.146 6.216 2.35 0 4.043-.951 5.058-2.242l-1.812-1.605-.09-.076a3.749 3.749 0 01-3.008 1.406c-1.778 0-3.061-1.037-3.427-2.737h8.779zm-8.733-2.22a3.365 3.365 0 01.737-1.449 3.082 3.082 0 012.368-.996c1.58 0 2.57.988 2.911 2.445h-6.016zM63.445 8.09v3.084a13.36 13.36 0 00-.838-.05c-2.094 0-3.282 1.505-3.282 3.479v5.736h-2.763V8.261h2.763v1.83h.025c.938-1.283 2.284-1.997 3.75-1.997l.345-.004zM69.887 15.281l-1.998 2.222v2.837h-2.764V3.428h2.764v10.374L72.822 8.3h3.283l-4.341 4.86 4.417 7.18h-3.11l-3.133-5.059h-.051z'
15+
fill='#1F0256'
16+
/>
17+
<path
18+
d='M19.116 3.16l-2.88 2.881a.571.571 0 01-.701.084 6.854 6.854 0 00-10.39 5.647 6.867 6.867 0 00.979 3.764.571.571 0 01-.084.699l-2.88 2.88a.57.57 0 01-.865-.063A11.994 11.994 0 0119.051 2.295a.57.57 0 01.065.866z'
19+
fill='url(#paint0_linear_26568_214324)'
20+
/>
21+
<path
22+
d='M19.113 20.829l-2.88-2.88a.571.571 0 00-.7-.085 6.854 6.854 0 01-7.081 0 .571.571 0 00-.7.084l-2.881 2.88a.57.57 0 00.062.877 11.994 11.994 0 0014.114 0 .571.571 0 00.066-.876zM11.997 15.422a3.427 3.427 0 100-6.854 3.427 3.427 0 000 6.854z'
23+
fill='#1F0256'
24+
/>
25+
<defs>
26+
<linearGradient
27+
id='paint0_linear_26568_214324'
28+
x1={16.4087}
29+
y1={-1.75881}
30+
x2={-7.88473}
31+
y2={22.5365}
32+
gradientUnits='userSpaceOnUse'
33+
>
34+
<stop stopColor='#17CCFC' />
35+
<stop offset={0.5} stopColor='#5D31FF' />
36+
<stop offset={1} stopColor='#F35AFF' />
37+
</linearGradient>
38+
</defs>
39+
</svg>
40+
)
41+
}

src/assets/clerk-logo.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { SVGProps } from 'react'
2+
import { cn } from '@/lib/utils'
3+
4+
export function ClerkLogo({ className, ...props }: SVGProps<SVGSVGElement>) {
5+
return (
6+
<svg
7+
role='img'
8+
viewBox='0 0 24 24'
9+
xmlns='http://www.w3.org/2000/svg'
10+
id='clerk'
11+
height='24'
12+
width='24'
13+
className={cn('[&>path]:fill-foreground', className)}
14+
{...props}
15+
>
16+
<title>Clerk</title>
17+
<path
18+
d='m21.47 20.829 -2.881 -2.881a0.572 0.572 0 0 0 -0.7 -0.084 6.854 6.854 0 0 1 -7.081 0 0.576 0.576 0 0 0 -0.7 0.084l-2.881 2.881a0.576 0.576 0 0 0 -0.103 0.69 0.57 0.57 0 0 0 0.166 0.186 12 12 0 0 0 14.113 0 0.58 0.58 0 0 0 0.239 -0.423 0.576 0.576 0 0 0 -0.172 -0.453Zm0.002 -17.668 -2.88 2.88a0.569 0.569 0 0 1 -0.701 0.084A6.857 6.857 0 0 0 8.724 8.08a6.862 6.862 0 0 0 -1.222 3.692 6.86 6.86 0 0 0 0.978 3.764 0.573 0.573 0 0 1 -0.083 0.699l-2.881 2.88a0.567 0.567 0 0 1 -0.864 -0.063A11.993 11.993 0 0 1 6.771 2.7a11.99 11.99 0 0 1 14.637 -0.405 0.566 0.566 0 0 1 0.232 0.418 0.57 0.57 0 0 1 -0.168 0.448Zm-7.118 12.261a3.427 3.427 0 1 0 0 -6.854 3.427 3.427 0 0 0 0 6.854Z'
19+
strokeWidth='1'
20+
></path>
21+
</svg>
22+
)
23+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import Cookies from 'js-cookie'
2+
import { Outlet } from '@tanstack/react-router'
3+
import { cn } from '@/lib/utils'
4+
import { SearchProvider } from '@/context/search-context'
5+
import { SidebarProvider } from '@/components/ui/sidebar'
6+
import { AppSidebar } from '@/components/layout/app-sidebar'
7+
import SkipToMain from '@/components/skip-to-main'
8+
9+
interface Props {
10+
children?: React.ReactNode
11+
}
12+
13+
export function AuthenticatedLayout({ children }: Props) {
14+
const defaultOpen = Cookies.get('sidebar_state') !== 'false'
15+
return (
16+
<SearchProvider>
17+
<SidebarProvider defaultOpen={defaultOpen}>
18+
<SkipToMain />
19+
<AppSidebar />
20+
<div
21+
id='content'
22+
className={cn(
23+
'ml-auto w-full max-w-full',
24+
'peer-data-[state=collapsed]:w-[calc(100%-var(--sidebar-width-icon)-1rem)]',
25+
'peer-data-[state=expanded]:w-[calc(100%-var(--sidebar-width))]',
26+
'sm:transition-[width] sm:duration-200 sm:ease-linear',
27+
'flex h-svh flex-col',
28+
'group-data-[scroll-locked=1]/body:h-full',
29+
'has-[main.fixed-main]:group-data-[scroll-locked=1]/body:h-svh'
30+
)}
31+
>
32+
{children ? children : <Outlet />}
33+
</div>
34+
</SidebarProvider>
35+
</SearchProvider>
36+
)
37+
}

src/components/layout/data/sidebar-data.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
IconUsers,
2121
} from '@tabler/icons-react'
2222
import { AudioWaveform, Command, GalleryVerticalEnd } from 'lucide-react'
23+
import { ClerkLogo } from '@/assets/clerk-logo'
2324
import { type SidebarData } from '../types'
2425

2526
export const sidebarData: SidebarData = {
@@ -75,6 +76,24 @@ export const sidebarData: SidebarData = {
7576
url: '/users',
7677
icon: IconUsers,
7778
},
79+
{
80+
title: 'Secured by Clerk',
81+
icon: ClerkLogo,
82+
items: [
83+
{
84+
title: 'Sign In',
85+
url: '/clerk/sign-in',
86+
},
87+
{
88+
title: 'Sign Up',
89+
url: '/clerk/sign-up',
90+
},
91+
{
92+
title: 'User Management',
93+
url: '/clerk/user-management',
94+
},
95+
],
96+
},
7897
],
7998
},
8099
{

src/components/layout/main.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ interface MainProps extends React.HTMLAttributes<HTMLElement> {
66
ref?: React.Ref<HTMLElement>
77
}
88

9-
export const Main = ({ fixed, ...props }: MainProps) => {
9+
export const Main = ({ fixed, className, ...props }: MainProps) => {
1010
return (
1111
<main
1212
className={cn(
1313
'peer-[.header-fixed]/header:mt-16',
1414
'px-4 py-6',
15-
fixed && 'fixed-main flex grow flex-col overflow-hidden'
15+
fixed && 'fixed-main flex grow flex-col overflow-hidden',
16+
className
1617
)}
1718
{...props}
1819
/>

0 commit comments

Comments
 (0)