Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a simple content management system #1

Draft
wants to merge 19 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 34 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
# Clark Dashboard
# Yearbook

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
This is an interactive, personalized yearbook featuring a Spotify Wrapped-like
recap experience and an interface that allows students to show off the cool
things they've done.

This is a dashboard that lets the Clark Summer Research Program track points for
houses, or groups of program participants who receive points for engaging with
the program through events, activites, and good behavior or get deducted points
for doing not-so-good things.

A live deployment can be found at [clark.williecubed.dev](clark.williecubed.dev).
A live deployment can be found at [yearbook.williecubed.dev](yearbook.williecubed.dev).

## Getting Started

Expand All @@ -24,16 +21,39 @@ Make sure you install the Git hooks for Prettier:

```shell
npx husky install
npm set-script prepare "husky install"
npx husky add .husky/pre-commit "npx lint-staged"
```

TODO: Include information on Supabase

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
### Setting up Supabase

```
npx supabase login
npx supabase link --project-ref <SUPABASE_PROJECT_ID>
```

#### Creating Environment Variables

Create a .env.local at the root of the project:

```shell
# Not required, but can be used to customize editor used when debugging
REACT_EDITOR=code
# All required
NEXT_PUBLIC_SUPABASE_URL=
SUPABASE_SERVICE_KEY=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
```

### Generate Database Types

From the project root, run:

```shell
npm run update-types
```

[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
### Deployment

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
TODO
7 changes: 5 additions & 2 deletions app/(user)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SiteFooter from "../../components/SiteFooter";
import styles from "../styles.module.css";

/**
* A landing page where one can see a brief overview to Yearbook.
Expand All @@ -8,7 +9,9 @@ import SiteFooter from "../../components/SiteFooter";
export default function LandingPage() {
return (
<div className="h-screen">
<main className="h-full p-4 md:p-8 bg-[#3BCEAC] dark:bg-[#540D6E]">
<main
className={`h-full p-4 md:p-8 bg-[#3BCEAC] dark:bg-[#540D6E] ${styles["cool-background"]} `}
>
<section className="mx-auto max-w-4xl">
<div className="mt-16 md:mt-32 p-8 md:p-16 border-4 border-black text-[#333333] bg-[#FFD23F] dark:text-white dark:bg-[#2767CE] drop-shadow-pop">
<div className="font-display font-bold text-5xl md:text-7xl">
Expand All @@ -21,7 +24,7 @@ export default function LandingPage() {
<a
className="mt-[72px] md:mt-[144px] inline-block p-4 md:p-6 border-4 border-black text-[#333333] bg-[#EE4266] dark:text-white dark:bg-[#3BCEAC] drop-shadow-pop hover:-translate-x-[8px] hover:-translate-y-[8px] hover:drop-shadow-pop-1 focus:-translate-x-[8px] focus:-translate-y-[8px] focus:drop-shadow-pop-1 font-display font-semibold text-2xl md:text-3xl hover:cursor-pointer hover:text-blue-400 underline transition ease-in-out duration-200"
title="Ha, did you really think I was that far along in this project?"
href={process.env.WAITLIST_FORM || '#'}
href={process.env.WAITLIST_FORM || "#"}
>
Sign up for waitlist
</a>
Expand Down
21 changes: 21 additions & 0 deletions app/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";
import DashboardHeader from "../../components/common/DashboardHeader";
import SideNavigationBar from "../../components/common/SideNavigationBar";

export const metadata = {
title: "Dashboard - Yearbook Admin",
};

export default function AdminDashboardLayout({
children,
}: React.PropsWithChildren) {
return (
<div className="h-screen bg-neutral-100 dark:bg-gray-800">
<DashboardHeader />
<div className="h-full flex">
<SideNavigationBar />
<main className="flex-1">{children}</main>
</div>
</div>
);
}
50 changes: 50 additions & 0 deletions app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import YearbookVolumeSelector from "../../components/common/YearbookVolumeSelector";
import { createClient } from "../../lib/supabase-server";
import { Database } from "../../lib/supabase.types";
import { YearbookInfo } from "../../lib/yearbook";

export default async function AdminPage() {
// TODO: If Yearbook volume stored in local storage, simply redirect
const supabase = createClient();

// TODO: Only select yearbooks from current owner
const { data, error } = await supabase.from("yearbooks").select("*");
if (error) {
throw new Error();
}

const yearbooks = data.map(mapTableToYearbook);

return (
<div>
<section className="p-8 max-w-5xl">
<div className="text-3xl font-bold font-display">
Select a volume to continue.
</div>
<div className="my-4 flex p-4 bg-neutral-200 dark:bg-neutral-800 dark:border-neutral-700 border-2 rounded-md justify-between">
<div>Filter by</div>
<div>(icon)</div>
</div>
<YearbookVolumeSelector mode={"list"} yearbooks={yearbooks} />
</section>
</div>
);
}

type DBYearbook = Database["public"]["Tables"]["yearbooks"]["Row"];

function mapTableToYearbook({
id,
title,
thumbnail_url,
created_at,
owner,
}: DBYearbook): YearbookInfo {
return {
id: id,
title: title,
thumbnailUrl: thumbnail_url,
createdAt: created_at,
owner: owner,
};
}
16 changes: 16 additions & 0 deletions app/admin/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import SignInSection from "../../../components/settings/SignInSection";

export const metadata = {
title: "Account Settings - Yearbook Admin",
};

export default function YearbookAdminSettingsPage() {
return (
<div>
<section className="p-8 max-w-5xl">
<div className="text-3xl font-bold font-display">User Settings</div>
</section>
<SignInSection />
</div>
);
}
14 changes: 14 additions & 0 deletions app/admin/yearbook/[yearbookId]/content/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const metadata = {
title: "Edit Content - Yearbook Admin",
};

export default function YearbookContentDashboardLayout({
children,
}: React.PropsWithChildren) {
return (
<div className="h-full bg-neutral-100">
<div className="py-2">Content editor</div>
<main>{children}</main>
</div>
);
}
3 changes: 3 additions & 0 deletions app/admin/yearbook/[yearbookId]/content/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function AdminContentPage() {
return <div>Admin credits!</div>;
}
7 changes: 7 additions & 0 deletions app/admin/yearbook/[yearbookId]/credits/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const metadata = {
title: "Manage Credits - Yearbook Admin",
};

export default function AdminCreditsPage() {
return <div>Admin credits!</div>;
}
11 changes: 11 additions & 0 deletions app/admin/yearbook/[yearbookId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* A page that allows an admin to manage all yearbook instances.
*/
export default function YearbookAdminLandingPage() {
return (
<div>
<div>Yearbook landing page</div>
<div>TODO: Show useful shortcuts to other pages</div>
</div>
);
}
7 changes: 7 additions & 0 deletions app/admin/yearbook/[yearbookId]/preferences/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const metadata = {
title: "Yearbook Settings - Yearbook Admin",
};

export default function YearbookAdminPreferencesPage() {
return <div>Settings page coming soon!</div>;
}
7 changes: 7 additions & 0 deletions app/admin/yearbook/[yearbookId]/users/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const metadata = {
title: "Manage Users - Yearbook Admin",
};

export default function AdminCreditsPage() {
return <div>User management</div>;
}
10 changes: 10 additions & 0 deletions app/admin/yearbook/[yearbookId]/wrapped/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const metadata = {
title: "Manage Wrapped Experience - Yearbook Admin",
};

export default function YearbookWrappedAdminPage() {
// TODO: Have place to accept terms and conditions for Wrapped
// TODO: Create onboarding for Wrapped
// TODO: Create layout for organizing Wrapped experience
return <div>Coming soon!</div>;
}
6 changes: 6 additions & 0 deletions app/admin/yearbook/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* A page that allows an admin to manage all yearbook instances
*/
export default function YearbookAdminPage() {
return <div>The admin dashboard!</div>;
}
26 changes: 23 additions & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import "server-only";

import React from "react";
import { Be_Vietnam_Pro } from "@next/font/google";
import { Be_Vietnam_Pro } from "next/font/google";
import SupabaseProvider from "../components/common/SupabaseProvider";
import SupabaseListener from "../components/common/SuapbaseListener";
import { createClient } from "../lib/supabase-server";
import "./globals.css";

const displayFont = Be_Vietnam_Pro({
Expand All @@ -9,10 +14,25 @@ const displayFont = Be_Vietnam_Pro({
variable: "--font-display",
});

export default function RootLayout({ children }: React.PropsWithChildren) {
// Caching should be stopped for Supabase Auth to function
export const revalidate = 0;

export default async function RootLayout({
children,
}: React.PropsWithChildren) {
const supabase = createClient();
const {
data: { session },
} = await supabase.auth.getSession();

return (
<html lang="en" className={displayFont.className}>
<body className="dark:bg-slate-900 dark:text-slate-100">{children}</body>
<body className="dark:bg-slate-900 dark:text-slate-100">
<SupabaseProvider>
<SupabaseListener serverAccessToken={session?.access_token} />
{children}
</SupabaseProvider>
</body>
</html>
);
}
16 changes: 16 additions & 0 deletions app/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createMiddlewareSupabaseClient } from "@supabase/auth-helpers-nextjs";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import type { Database } from "../lib/supabase.types";

export async function middleware(req: NextRequest) {
const res = NextResponse.next();

const supabase = createMiddlewareSupabaseClient<Database>({ req, res });

const {
data: { session },
} = await supabase.auth.getSession();

return res;
}
6 changes: 6 additions & 0 deletions app/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.cool-background {
@apply -m-4;
/* @apply animate-[spin_300s_linear_infinite]; */
z-index: -1;
background-image: url("../public/assets/backgrounds/landing.svg");
}
7 changes: 7 additions & 0 deletions app/y/[yearbookId]/contents/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function YearbookTableOfContentsPage() {
return (
<div>
<div className="text-3xl font-bold font-display">Table of Contents</div>
</div>
);
}
30 changes: 30 additions & 0 deletions app/y/[yearbookId]/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import { useEffect } from "react";

export default function YearbookPageError({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error);
}, [error]);

return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
Try again
</button>
</div>
);
}
13 changes: 13 additions & 0 deletions app/y/[yearbookId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";

export const metadata = {
title: "Dashboard - Yearbook Admin",
};

export default function YearbookWrapperLayout({
children,
}: React.PropsWithChildren) {
return (
<div className="h-screen bg-neutral-100 dark:bg-gray-800">{children}</div>
);
}
7 changes: 7 additions & 0 deletions app/y/[yearbookId]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function YearbookDetailLoading() {
return (
<div className="absolute h-screen w-screen p-4 place-content-center">
Loading yearbook&hellip;
</div>
);
}
Loading