Skip to content

Commit

Permalink
feat(dashboard): add reusable header
Browse files Browse the repository at this point in the history
  • Loading branch information
klevente committed Aug 11, 2024
1 parent 31cb383 commit e15aa36
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 118 deletions.
2 changes: 1 addition & 1 deletion dashboard/app/db/seed.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export async function seedAdminUser() {
console.log("Route already exist, not adding admin user again.");
return;
}
console.log("No users in DB, creating admin user...");
console.log("No _auth.users in DB, creating admin user...");
await createUser(env.ADMIN_EMAIL, env.ADMIN_PASSWORD, "admin");
console.log("Admin user successfully created!");
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import {
type MetaFunction,
redirect,
} from "@remix-run/node";
import { Link, useFetcher, useLoaderData } from "@remix-run/react";
import { useFetcher, useLoaderData } from "@remix-run/react";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { SOUNDS_PATH } from "~/config/constants.server";
import { Button } from "~/components/ui/button";
import { Loader2, LogOut, MenuIcon, Users, X } from "lucide-react";
import { Loader2, X } from "lucide-react";
import { Input } from "~/components/ui/input";
import {
type ChangeEvent,
Expand All @@ -20,7 +20,6 @@ import {
useRef,
useState,
} from "react";
import { Separator } from "~/components/ui/separator";
import { authenticator } from "~/services/auth.server";
import { action as uploadAction } from "./upload";
import { useToast } from "~/components/ui/use-toast";
Expand All @@ -33,14 +32,6 @@ import {
TooltipProvider,
TooltipTrigger,
} from "~/components/ui/tooltip";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";

const PAGE_SIZE = 50;

Expand Down Expand Up @@ -77,7 +68,6 @@ export async function loader({ request }: LoaderFunctionArgs) {
const onLastPage = page === pageCount;

return json({
user,
isAdmin,
files,
page,
Expand Down Expand Up @@ -212,7 +202,6 @@ const tableColumns: ColumnDef<Sound>[] = [

export default function Index() {
const {
user,
isAdmin,
files,
page,
Expand Down Expand Up @@ -260,56 +249,7 @@ export default function Index() {
const isUploading = uploadFetcher.state !== "idle";

return (
<>
<header className="flex justify-between">
<Link to="/">
<h1 className="text-2xl font-bold tracking-tight mb-4 flex items-center gap-2">
<img src="/favicon.ico" alt="icon" className="w-8 img-pixelated" />{" "}
Mememachine
</h1>
</Link>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon" className="overflow-hidden">
<MenuIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>
<span>{user.email}</span>
</DropdownMenuLabel>
{isAdmin && (
<>
<DropdownMenuSeparator />
<Link to="/users">
<DropdownMenuItem>
<Users className="mr-2 h-4 w-4" />
<span>Users</span>
</DropdownMenuItem>
</Link>
</>
)}
<DropdownMenuSeparator />
<Link to="/logout">
<DropdownMenuItem>
<LogOut className="mr-2 h-4 w-4" />
<span>Logout</span>
</DropdownMenuItem>
</Link>
</DropdownMenuContent>
</DropdownMenu>
{/*<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item One</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink>Link</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>*/}
</header>
<Separator className="my-4" />
<article>
<uploadFetcher.Form
ref={uploadFormRef}
action="upload"
Expand Down Expand Up @@ -354,6 +294,6 @@ export default function Index() {
url="/"
/>
</div>
</>
</article>
);
}
70 changes: 70 additions & 0 deletions dashboard/app/routes/_auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Link, Outlet, useLoaderData } from "@remix-run/react";
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { authenticator } from "~/services/auth.server";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import { Button } from "~/components/ui/button";
import { LogOut, MenuIcon, Users } from "lucide-react";

export async function loader({ request }: LoaderFunctionArgs) {
const user = await authenticator.isAuthenticated(request, {
failureRedirect: "/login",
});

const isAdmin = user.role === "admin";

return json({ user, isAdmin });
}

export default function Layout() {
const { user, isAdmin } = useLoaderData<typeof loader>();
return (
<div>
<header className="flex justify-between">
<Link to="/">
<h1 className="text-2xl font-bold tracking-tight mb-4 flex items-center gap-2">
<img src="/favicon.ico" alt="icon" className="w-8 img-pixelated" />{" "}
Mememachine
</h1>
</Link>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon" className="overflow-hidden">
<MenuIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>
<span>{user.email}</span>
</DropdownMenuLabel>
{isAdmin && (
<>
<DropdownMenuSeparator />
<Link to="/users">
<DropdownMenuItem>
<Users className="mr-2 h-4 w-4" />
<span>Users</span>
</DropdownMenuItem>
</Link>
</>
)}
<DropdownMenuSeparator />
<Link to="/logout">
<DropdownMenuItem>
<LogOut className="mr-2 h-4 w-4" />
<span>Logout</span>
</DropdownMenuItem>
</Link>
</DropdownMenuContent>
</DropdownMenu>
</header>
<Outlet />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,7 @@
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import { Button } from "~/components/ui/button";
import {
ClipboardCheck,
ClipboardCopy,
Loader2,
MenuIcon,
X,
} from "lucide-react";
import { ClipboardCheck, ClipboardCopy, Loader2, X } from "lucide-react";
import {
Form,
Link,
useFetcher,
useLoaderData,
useNavigation,
Expand All @@ -40,8 +25,11 @@ import { type ColumnDef } from "@tanstack/react-table";
import { DataTable } from "~/components/ui/data-table";
import { type FunctionComponent, useEffect, useState } from "react";
import { useToast } from "~/components/ui/use-toast";
import { type InviteSchema, inviteSchemaResolver } from "~/routes/users/types";
import { createInvitation } from "~/routes/users/service.server";
import {
type InviteSchema,
inviteSchemaResolver,
} from "~/routes/_auth.users/types";
import { createInvitation } from "~/routes/_auth.users/service.server";
import {
Tooltip,
TooltipContent,
Expand Down Expand Up @@ -242,7 +230,7 @@ const userTableColumns: ColumnDef<UserRow>[] = [
];

export default function Route() {
const { currentUser, users, invitations } = useLoaderData<typeof loader>();
const { users, invitations } = useLoaderData<typeof loader>();

const {
handleSubmit,
Expand All @@ -265,36 +253,7 @@ export default function Route() {
}, [toast, reset, isSubmitSuccessful]);

return (
<>
<header className="flex justify-between">
<Link to="/">
<h1 className="text-2xl font-bold tracking-tight mb-4 flex items-center gap-2">
<img src="/favicon.ico" alt="icon" className="w-8 img-pixelated" />{" "}
Mememachine
</h1>
</Link>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon" className="overflow-hidden">
<MenuIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>{currentUser.email}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Users</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Form action="/logout" method="post">
<Button variant="link" type="submit">
Logout
</Button>
</Form>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
<Separator className="my-4" />
<article>
<h3 className="font-bold">Invitations</h3>
<Form
method="post"
Expand Down Expand Up @@ -328,6 +287,6 @@ export default function Route() {
<Separator className="my-4" />
<h3 className="font-bold">Users</h3>
<DataTable columns={userTableColumns} data={users} />
</>
</article>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { invitations, lower, users } from "~/db/schema.server";
import { eq } from "drizzle-orm";
import type { FieldErrors } from "react-hook-form";
import { v7 as uuidv7 } from "uuid";
import type { DeleteSchema, InviteSchema } from "~/routes/users/types";
import type { DeleteSchema, InviteSchema } from "~/routes/_auth.users/types";
import type { ReceivedValues } from "~/lib/utils";

export async function createInvitation(
Expand Down
File renamed without changes.
7 changes: 5 additions & 2 deletions dashboard/app/routes/users.delete.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { type ActionFunctionArgs, json, redirect } from "@remix-run/node";
import { authenticator } from "~/services/auth.server";
import { deleteInvitation } from "~/routes/users/service.server";
import { deleteInvitation } from "~/routes/_auth.users/service.server";
import { getValidatedFormData } from "remix-hook-form";
import { type DeleteSchema, deleteSchemaResolver } from "~/routes/users/types";
import {
type DeleteSchema,
deleteSchemaResolver,
} from "~/routes/_auth.users/types";

export async function action({ request }: ActionFunctionArgs) {
const currentUser = await authenticator.isAuthenticated(request, {
Expand Down

0 comments on commit e15aa36

Please sign in to comment.