Skip to content

Commit

Permalink
Merge pull request #1 from desci-labs/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
shadrach-tayo authored Sep 4, 2024
2 parents 7cdf0e7 + a3923d7 commit fa70c0e
Show file tree
Hide file tree
Showing 14 changed files with 249 additions and 80 deletions.
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@tanstack/react-query": "^5.54.1",
"autoprefixer": "^10.4.20",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand Down
7 changes: 7 additions & 0 deletions react.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module "react-dom" {
export function useFormStatus(): { pending: boolean };
export function useFormState<T>(
action: (state: T, formData: FormData) => T,
initialState: T,
): [T, (formData: FormData) => void];
}
21 changes: 21 additions & 0 deletions src/apis/queries.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NODES_API_URL } from "@/lib/config";
import { DoiRecord } from "./types";

export async function getDois() {
const response = await fetch(`${NODES_API_URL}/v1/admin/doi/list`, {
credentials: "include",
});

const data = (await response.json()) as
| {
data: DoiRecord[];
message: string;
}
| { message: string };

if (response.status === 200 && "data" in data) {
return data.data;
}

throw new Error(data?.message);
}
7 changes: 7 additions & 0 deletions src/apis/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type DoiRecord = {
id: number;
doi: string;
dpid: string;
uuid: string;
createdAt: string | Date;
};
51 changes: 51 additions & 0 deletions src/app/Provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import { ThemeProvider } from "@/components/theme-provider";
import {
isServer,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { PropsWithChildren } from "react";

function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
},
},
});
}

let browserQueryClient: QueryClient | undefined = undefined;

function getQueryClient() {
if (isServer) {
// Server: always make a new query client
return makeQueryClient();
} else {
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
}

export default function Provider({ children }: PropsWithChildren<{}>) {
const queryClient = getQueryClient();
return (
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</ThemeProvider>
);
}
17 changes: 7 additions & 10 deletions src/app/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ export type LoginUserData = {
};

export async function login(prevState: any, formData: FormData) {
const cookie = cookies().get(AUTH_COOKIE_FIELDNAME);
console.log("cookie", cookie);

const email = formData.get("email") ?? prevState?.email;
const code = formData.get("code");

if (!email?.endsWith("@desci.com"))
return {
ok: false,
error: "Unauthorised email domain (only desci.com emails are allowed)",
};

const res = await fetch(`${API_URL}/v1/auth/magic`, {
method: "POST",
body: JSON.stringify({ email, code }),
Expand All @@ -29,13 +32,7 @@ export async function login(prevState: any, formData: FormData) {

if (response.ok && response.user) {
// Set cookie
cookies().set(AUTH_COOKIE_FIELDNAME, response.user.token, {
path: "/",
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30 * 1), // 1 month
httpOnly: true,
secure: process.env.NEXT_ENV === "production",
domain: process.env.NODE_ENV === "production" ? ".desci.com" : undefined,
});
cookies().set(AUTH_COOKIE_FIELDNAME, response.user.token);
redirect(`/`);
}

Expand Down
27 changes: 13 additions & 14 deletions src/app/api/logout/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function DELETE(_request: Request) {
try {
const logoutRes = await fetch(`${NODES_API_URL}/v1/auth/logout"`, {
method: "delete",
credentials: "include",
headers: {
cookie: cookies().toString(),
},
});
console.log("LOGOUT", logoutRes);
cookies().delete(AUTH_COOKIE_FIELDNAME);
return NextResponse.json({ ok: true });
} catch (e) {
return NextResponse.json({ error: e }, { status: 500 });
}
try {
const logoutRes = await fetch(`${NODES_API_URL}/v1/auth/logout`, {
method: "delete",
credentials: "include",
headers: {
cookie: cookies().toString(),
},
});
if (logoutRes.ok && logoutRes.status === 200) cookies().delete(AUTH_COOKIE_FIELDNAME);
return NextResponse.json({ ok: logoutRes.ok });
} catch (e) {
return NextResponse.json({ error: e }, { status: 500 });
}
}
12 changes: 2 additions & 10 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { ThemeProvider } from "@/components/theme-provider";
import "./globals.scss";

import Provider from "./Provider";
const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
Expand All @@ -18,14 +17,7 @@ export default function RootLayout({
return (
<html lang="en" className="">
<body className={inter.className}>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
<Provider>{children}</Provider>
</body>
</html>
);
Expand Down
8 changes: 7 additions & 1 deletion src/app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import LoginForm from "@/components/molecules/LoginForm";
// @ts-ignore
import { useFormState, useFormStatus } from "react-dom";
import { login, LoginUserData } from "@/app/actions";

Expand All @@ -23,7 +24,12 @@ export default function Login() {
<h1 className="text-4xl font-bold my-4">
{!state?.email ? "Verify Email" : "Check your inbox"}
</h1>
<LoginForm login={formAction} pending={pending} email={state.email} />
<LoginForm
login={formAction}
pending={pending}
email={state.email}
message={state.error}
/>
</div>
</div>
);
Expand Down
17 changes: 14 additions & 3 deletions src/components/molecules/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { Switch } from "@/components/ui/switch";
import DoiRecords from "@/components/molecules/DoiRecords";
import { cn } from "@/lib/utils";
import { useRouter } from "next/navigation";

interface Node {
id: string;
Expand Down Expand Up @@ -51,15 +52,21 @@ function Sidebar({
activeTab: string;
setActiveTab: (tab: string) => void;
}) {
const router = useRouter();
return (
<div className="flex flex-col space-y-2 p-4 h-full">
<div className="flex flex-col space-y-2 flex-1">
{["Nodes", "Users", "DOIs", "Settings",].map((tab) => (
{["Nodes", "Users", "DOIs", "Settings"].map((tab) => (
<Button
key={tab}
variant={activeTab === tab ? "default" : "ghost"}
onClick={() => setActiveTab(tab)}
className={cn("justify-start hover:bg-btn-surface-primary-neutral text-txt-subdued", activeTab === tab ? "bg-btn-surface-primary-focus text-btn-surface-primary-foreground" : "bg-surface-primary-neutral")}
className={cn(
"justify-start text-txt-subdued",
activeTab === tab
? "bg-btn-surface-primary-focus text-btn-surface-primary-foreground hover:bg-btn-surface-primary-focus"
: "hover:bg-btn-surface-primary-neutral hover:text-txt-subdued"
)}
>
{tab}
</Button>
Expand All @@ -68,14 +75,18 @@ function Sidebar({
<Button
variant="default"
className="justify-center text-red-500 bg-red-500/20 hover:bg-red-500/30"
onClick={() => {
fetch("/api/logout", { method: "DELETE" }).then(() => {
router.push("/login");
});
}}
>
Logout
</Button>
</div>
);
}


function NodesTable() {
return (
<Table>
Expand Down
Loading

0 comments on commit fa70c0e

Please sign in to comment.