This commit is contained in:
Gustavo Maronato 2023-08-19 14:24:58 -03:00
parent e0e1a775bc
commit 8a1a511a9b
Signed by: maronato
SSH Key Fingerprint: SHA256:2Gw7kwMz/As+2UkR1qQ/qYYhn+WNh3FGv6ozhoRrLcs
11 changed files with 223 additions and 21 deletions

View File

@ -8,6 +8,7 @@
"name": "goshort",
"version": "0.0.0",
"dependencies": {
"@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18",
"classnames": "^2.3.2",
"react": "^18.2.0",
@ -469,6 +470,21 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@headlessui/react": {
"version": "1.7.17",
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.17.tgz",
"integrity": "sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==",
"dependencies": {
"client-only": "^0.0.1"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"react": "^16 || ^17 || ^18",
"react-dom": "^16 || ^17 || ^18"
}
},
"node_modules/@heroicons/react": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz",
@ -1614,6 +1630,11 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
},
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",

View File

@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18",
"classnames": "^2.3.2",
"react": "^18.2.0",

View File

@ -1,17 +1,20 @@
import { FunctionComponent } from "react"
import { Outlet } from "react-router-dom"
import { Outlet, ScrollRestoration } from "react-router-dom"
import Button from "./components/Button"
// import Button from "./components/Button"
import Container from "./components/Container"
import { useIsAuthenticated } from "./hooks/useAuth"
import Navbar from "./components/Navbar"
// import { useIsAuthenticated } from "./hooks/useAuth"
const App: FunctionComponent = () => {
const isAuthenticated = useIsAuthenticated()
// const isAuthenticated = useIsAuthenticated()
return (
<Container>
<div className="flex flex-col">
<div className="grid grid-cols-3 justify-between text-slate-500 font-medium">
<>
<Navbar />
<Container>
<div className="flex flex-col">
{/* <div className="grid grid-cols-3 justify-between text-slate-500 font-medium">
<div className="flex-flex-row mr-auto text-lg">
<Button text="GoShort" link="/" />
</div>
@ -29,12 +32,14 @@ const App: FunctionComponent = () => {
{isAuthenticated || <Button text="Login" link="lgn" />}
{isAuthenticated && <Button text="Logout" link="lgo" />}
</div>
</div> */}
<div className="flex flex-col">
<Outlet />
</div>
</div>
<div className="flex flex-col">
<Outlet />
</div>
</div>
</Container>
</Container>
<ScrollRestoration />
</>
)
}

View File

@ -0,0 +1,27 @@
import { FunctionComponent, useCallback } from "react"
import { ChevronLeftIcon } from "@heroicons/react/24/outline"
import classNames from "classnames"
import { useNavigate } from "react-router-dom"
const BackButton: FunctionComponent<{ className?: string }> = ({
className,
}) => {
const navigate = useNavigate()
const goBack = useCallback(() => navigate(-1), [navigate])
return (
<div
className={classNames(
className,
"text-blue-500 flex flex-row align-middle justify-items-center hover:text-blue-600 duration-200 transition-colors"
)}>
<ChevronLeftIcon className="w-5 5-6" />
<button className="" onClick={goBack}>
Back
</button>
</div>
)
}
export default BackButton

View File

@ -2,7 +2,7 @@ import { FunctionComponent, PropsWithChildren } from "react"
const Container: FunctionComponent<PropsWithChildren> = ({ children }) => {
return (
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">
<div className="mx-auto max-w-7xl px-4 pb-20 sm:px-6 lg:px-8 w-full">
{children}
</div>
)

View File

@ -2,7 +2,7 @@ import { FunctionComponent } from "react"
const Header: FunctionComponent<{ title: string }> = ({ title }) => {
return (
<header className="pt-20 pb-10 px-3">
<header className="pt-14 pb-10">
<h1 className="text-5xl font-bold">{title}</h1>
</header>
)

View File

@ -3,7 +3,7 @@ import { FunctionComponent } from "react"
import classNames from "classnames"
import { NavLink, useSearchParams } from "react-router-dom"
const Button: FunctionComponent<{
const NavButton: FunctionComponent<{
text: string
onClick?: () => void
link?: string
@ -11,7 +11,7 @@ const Button: FunctionComponent<{
const Content: FunctionComponent<{ active?: boolean }> = ({ active }) => (
<div
className={classNames(
"px-0 mx-3 py-2 group border-b hover:border-blue-600 transition-colors duration-200",
"text-xl px-0 mx-3 py-2 group border-b hover:border-blue-600 transition-colors duration-200",
{ "border-b-2 border-blue-600": active, "border-transparent": !active }
)}>
<button
@ -41,4 +41,4 @@ const Button: FunctionComponent<{
return <Content />
}
export default Button
export default NavButton

View File

@ -0,0 +1,146 @@
import { Fragment, useMemo } from "react"
import { Disclosure, Menu, Transition } from "@headlessui/react"
import {
Bars3Icon,
ChevronDownIcon,
XMarkIcon,
} from "@heroicons/react/24/outline"
import classNames from "classnames"
import { Link, NavLink } from "react-router-dom"
import { useIsAuthenticated, useUser } from "../hooks/useAuth"
import NavButton from "./NavButton"
export default function Navbar() {
const isAuthenticated = useIsAuthenticated()
const user = useUser()
const navigation = useMemo(() => {
const authed = [
{ name: "Shorts", href: "sht" },
{ name: "Tokens", href: "tkn" },
{ name: "Sessions", href: "ses" },
]
const unauthed = [
{ name: "Login", href: "lgn" },
{ name: "Signup", href: "sgn" },
]
return isAuthenticated ? authed : unauthed
}, [isAuthenticated])
return (
<Disclosure as="nav">
{({ open }) => (
<>
<div
className={classNames("mx-auto max-w-7xl px-2 sm:px-6 lg:px-8", {
"bg-white": open,
})}>
<div className="relative flex h-16 items-center justify-between">
<div className="absolute inset-y-0 left-0 flex items-center sm:hidden">
{/* Mobile menu button*/}
<Disclosure.Button className="relative inline-flex items-center justify-center rounded-md p-2 text-slate-500 hover:bg-slate-200 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500">
<span className="absolute -inset-0.5" />
<span className="sr-only">Open main menu</span>
{open ? (
<XMarkIcon className="block h-6 w-6" aria-hidden="true" />
) : (
<Bars3Icon className="block h-6 w-6" aria-hidden="true" />
)}
</Disclosure.Button>
</div>
<div className="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
<div className="flex flex-shrink-0 items-center">
<NavButton text="GoShort" link="/" />
</div>
<div className="hidden sm:ml-6 sm:flex w-full flex-row justify-center">
<div className="flex space-x-4">
{navigation.map((item) => (
<NavButton
key={item.name}
text={item.name}
link={item.href}
/>
))}
</div>
</div>
</div>
<div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
{/* Profile dropdown */}
{isAuthenticated && (
<Menu as="div" className="relative ml-3">
<div>
<Menu.Button className="relative px-3 py-2 justify-items-center flex text-lg rounded-lg focus:outline-none focus:ring-none">
<span className="absolute -inset-1.5" />
<span className="sr-only">Open user menu</span>
<span className="my-auto">{user?.username}</span>
<span className="my-auto">
<ChevronDownIcon
className="h-4 w-4 ml-2"
aria-hidden="true"
/>
</span>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95">
<Menu.Items className="absolute right-0 z-10 mt-2 w-32 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<Menu.Item>
{({ active }) => (
<Link
to="/lgo"
className={classNames(
active ? "bg-gray-100" : "",
"block px-4 py-2 text-sm text-gray-700"
)}>
Logout
</Link>
)}
</Menu.Item>
</Menu.Items>
</Transition>
</Menu>
)}
{isAuthenticated || <NavButton text="Login" link="lgn" />}
</div>
</div>
</div>
<Disclosure.Panel
className={classNames("sm:hidden pb-2 flex", {
"bg-white shadow": open,
})}>
{({ close }) => (
<div className="space-y-1">
{navigation.map((item) => (
<NavLink
onClick={() => close()}
key={item.name}
to={item.href}
className={({ isActive }) =>
classNames(
isActive
? "bg-blue-100 text-blue-600 border-blue-500"
: "text-slate-500 hover:bg-slate-50 hover:text-slate-600 hover:border-slate-500",
"block border-l-4 border-transparent px-3 py-2 text-base font-medium"
)
}>
{item.name}
</NavLink>
))}
</div>
)}
</Disclosure.Panel>
</>
)}
</Disclosure>
)
}

View File

@ -68,7 +68,7 @@ const ShortItem: FunctionComponent<Short & { doDelete: () => void }> = ({
<div className="col-span-10 md:col-span-4 lg:col-span-5 my-auto order-4 md:order-3">
<a
href={url}
target="_blank"
title={url}
rel="noreferrer"
className="flex flex-row font-normal leading-6 text-blue-600 hover:text-blue-500 transition-all duration-200">
<span className="break-all">
@ -87,10 +87,10 @@ const ShortItem: FunctionComponent<Short & { doDelete: () => void }> = ({
Last viewed <time dateTime="2023-01-23T13:23Z">3h ago</time>
</p>
<Link to={`/sht/${name}`}>
<div className="py-1 my-1 text-xs flex flex-row gap-1 text-slate-500 hover:text-blue-500 border-b border-transparent hover:border-blue-500 transition-colors duration-200">
<div className="py-1 my-1 text-xs flex flex-row align-middle justify-items-center text-blue-500 hover:text-blue-600 transition-colors duration-200">
<span className="my-auto">details</span>
<ChevronRightIcon className="my-auto w-3 h-3 -mr-1" />
<ChevronRightIcon className="my-auto w-4 h-4 " />
</div>
</Link>
</div>

View File

@ -3,6 +3,6 @@
@tailwind utilities;
:root {
@apply text-slate-700;
@apply text-slate-700 bg-slate-50;
font-family: -apple-system, SF Pro Text, SF UI Text, system-ui, Helvetica Neue, Helvetica, Arial, sans-serif;
}

View File

@ -1,5 +1,6 @@
import { LoaderFunction, redirect, useLoaderData } from "react-router-dom"
import BackButton from "../components/BackButton"
import Header from "../components/Header"
import { protectedLoader } from "../hooks/useAuth"
import { Short } from "../types"
@ -10,6 +11,7 @@ export function Component() {
return (
<>
<BackButton className="absolute mt-10" />
<Header title={short.name} />
{short.url}
</>