update
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useKeyPress } from '@/hooks/use-keypress'
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupInput
|
||||
} from '@repo/ui/components/ui/input-group'
|
||||
import { useKeyPress } from '@/hooks/use-keypress'
|
||||
import clsx from 'clsx'
|
||||
import { debounce } from 'lodash'
|
||||
import { SearchIcon } from 'lucide-react'
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { throttle } from 'lodash'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export function useKeyPress(targetKey, callback) {
|
||||
useEffect(() => {
|
||||
const onKeyDown = throttle((event) => {
|
||||
if (event.key === targetKey) {
|
||||
event.preventDefault()
|
||||
callback(event)
|
||||
}
|
||||
}, 300)
|
||||
|
||||
window.addEventListener('keydown', onKeyDown)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown)
|
||||
onKeyDown.cancel?.()
|
||||
}
|
||||
}, [targetKey, callback])
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import type { OAuth2Tokens } from 'arctic'
|
||||
import { decodeJwt } from 'jose'
|
||||
import { Authenticator } from 'remix-auth'
|
||||
import { CodeChallengeMethod, OAuth2Strategy } from 'remix-auth-oauth2'
|
||||
|
||||
export type User = {
|
||||
sub: string
|
||||
email: string
|
||||
name: string
|
||||
scope: string
|
||||
email_verified: boolean
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
}
|
||||
|
||||
export function createAuth(env: Env) {
|
||||
const authenticator = new Authenticator()
|
||||
const strategy = new OAuth2Strategy(
|
||||
{
|
||||
clientId: env.CLIENT_ID,
|
||||
clientSecret: env.CLIENT_SECRET,
|
||||
redirectURI: env.REDIRECT_URI,
|
||||
authorizationEndpoint: `${env.ISSUER_URL}/authorize`,
|
||||
tokenEndpoint: `${env.ISSUER_URL}/token`,
|
||||
tokenRevocationEndpoint: `${env.ISSUER_URL}/revoke`,
|
||||
scopes: env.SCOPE.split(' '),
|
||||
codeChallengeMethod: CodeChallengeMethod.S256
|
||||
},
|
||||
async ({ tokens }: { tokens: OAuth2Tokens }) => {
|
||||
const user = decodeJwt(tokens.idToken())
|
||||
|
||||
return {
|
||||
...user,
|
||||
accessToken: tokens.accessToken(),
|
||||
refreshToken: tokens.hasRefreshToken() ? tokens.refreshToken() : null
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
authenticator.use(strategy, 'oidc')
|
||||
|
||||
return authenticator
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { userContext } from '@/context'
|
||||
import { userContext } from '@repo/auth/context'
|
||||
import type { LoaderFunctionArgs } from 'react-router'
|
||||
|
||||
enum Method {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { createCookieSessionStorage } from 'react-router'
|
||||
|
||||
export function createSessionStorage(env: Env) {
|
||||
const sessionStorage = createCookieSessionStorage({
|
||||
cookie: {
|
||||
name: '__session',
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
secrets: [env.SESSION_SECRET],
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
maxAge: 86400 * 7 // 7 days
|
||||
}
|
||||
})
|
||||
return sessionStorage
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
export default [
|
||||
layout('routes/layout.tsx', [
|
||||
index('routes/index.tsx'),
|
||||
route('certs', 'routes/certs.tsx'),
|
||||
route('payments', 'routes/payments.tsx'),
|
||||
route('settings', 'routes/settings.tsx'),
|
||||
route('player/:course', 'routes/player.tsx'),
|
||||
|
||||
@@ -10,10 +10,10 @@ import {
|
||||
} from '@repo/ui/components/ui/empty'
|
||||
import {
|
||||
BanIcon,
|
||||
BookCopyIcon,
|
||||
CircleCheckIcon,
|
||||
CircleIcon,
|
||||
CircleOffIcon,
|
||||
CirclePlusIcon,
|
||||
CircleXIcon,
|
||||
TimerIcon,
|
||||
type LucideIcon
|
||||
@@ -131,7 +131,7 @@ export default function Component({
|
||||
/>
|
||||
</div>
|
||||
<FacetedFilter
|
||||
icon={BookCopyIcon}
|
||||
icon={CirclePlusIcon}
|
||||
value={searchParams.getAll('status')}
|
||||
onChange={(statuses) => {
|
||||
setSearchParams((searchParams) => {
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import type { Route } from './+types'
|
||||
|
||||
import { Outlet } from 'react-router'
|
||||
import { Link, NavLink, Outlet } from 'react-router'
|
||||
|
||||
import { userContext } from '@repo/auth/context'
|
||||
import { authMiddleware } from '@repo/auth/middleware/auth'
|
||||
|
||||
import { ModeToggle, ThemedImage } from '@repo/ui/components/dark-mode'
|
||||
import { NavUser } from '@repo/ui/components/nav-user'
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList
|
||||
} from '@repo/ui/components/ui/navigation-menu'
|
||||
import { useIsMobile } from '@repo/ui/hooks/use-mobile'
|
||||
|
||||
export const middleware: Route.MiddlewareFunction[] = [authMiddleware]
|
||||
|
||||
@@ -16,6 +22,7 @@ export async function loader({ context }: Route.ActionArgs) {
|
||||
}
|
||||
|
||||
export default function Component({ loaderData }: Route.ComponentProps) {
|
||||
const isMobile = useIsMobile()
|
||||
const { user } = loaderData
|
||||
|
||||
return (
|
||||
@@ -25,7 +32,25 @@ export default function Component({ loaderData }: Route.ComponentProps) {
|
||||
px-4 py-2 lg:py-4 sticky top-0 z-5"
|
||||
>
|
||||
<div className="container mx-auto flex items-center">
|
||||
<ThemedImage />
|
||||
<div className="flex gap-5">
|
||||
<Link to="/">
|
||||
<ThemedImage />
|
||||
</Link>
|
||||
|
||||
<NavigationMenu viewport={isMobile}>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<NavMenuLink to="/">Meus cursos</NavMenuLink>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavMenuLink to="/certs">Certificados</NavMenuLink>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavMenuLink to="/payments">Histórico de compras</NavMenuLink>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
</div>
|
||||
|
||||
<div className="ml-auto flex gap-2.5 items-center">
|
||||
<ModeToggle />
|
||||
@@ -42,3 +67,14 @@ export default function Component({ loaderData }: Route.ComponentProps) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NavMenuLink({ children, ...props }) {
|
||||
return (
|
||||
<NavigationMenuLink
|
||||
className="font-medium aria-[current=page]:bg-muted"
|
||||
asChild
|
||||
>
|
||||
<NavLink {...props}>{children}</NavLink>
|
||||
</NavigationMenuLink>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ import type { Route } from './+types'
|
||||
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import { userContext } from '@/context'
|
||||
import { request as req } from '@/lib/request'
|
||||
import type { User } from '@/middleware/auth'
|
||||
|
||||
import type { User } from '@repo/auth/auth'
|
||||
import { userContext } from '@repo/auth/context'
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
|
||||
Reference in New Issue
Block a user