mirror of
https://github.com/cssgunc/compass.git
synced 2025-04-09 14:00:15 -04:00
Intialize resource, service, and training-manual routes
This commit is contained in:
parent
150dde1b0a
commit
dfed92816d
|
@ -28,7 +28,7 @@ export default function RootLayout({
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log("Accessed admin page but not logged in");
|
console.log("Accessed admin page but not logged in");
|
||||||
router.push("auth/login");
|
router.push("/auth/login");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ export default function RootLayout({
|
||||||
console.log(
|
console.log(
|
||||||
`Accessed admin page but incorrect permissions: ${user.username} ${user.role}`
|
`Accessed admin page but incorrect permissions: ${user.username} ${user.role}`
|
||||||
);
|
);
|
||||||
router.push("auth/login");
|
router.push("/auth/login");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ export async function login(email: string, password: string) {
|
||||||
const supabaseUser = await supabase.auth.getUser();
|
const supabaseUser = await supabase.auth.getUser();
|
||||||
|
|
||||||
if (!supabaseUser.data.user) {
|
if (!supabaseUser.data.user) {
|
||||||
revalidatePath("/resource", "layout");
|
revalidatePath("/home", "layout");
|
||||||
redirect("/resource");
|
redirect("/home");
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiData = await fetch(
|
const apiData = await fetch(
|
||||||
|
@ -37,8 +37,8 @@ export async function login(email: string, password: string) {
|
||||||
redirect("/admin");
|
redirect("/admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
revalidatePath("/resource", "layout");
|
revalidatePath("/home", "layout");
|
||||||
redirect("/resource");
|
redirect("/home");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function signOut() {
|
export async function signOut() {
|
||||||
|
@ -53,6 +53,6 @@ export async function signOut() {
|
||||||
|
|
||||||
await supabase.auth.signOut();
|
await supabase.auth.signOut();
|
||||||
|
|
||||||
revalidatePath("/resource", "layout");
|
revalidatePath("/auth/login", "layout");
|
||||||
redirect("/auth/login");
|
redirect("/auth/login");
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default function Page() {
|
||||||
async function checkUser() {
|
async function checkUser() {
|
||||||
const { data } = await supabase.auth.getUser();
|
const { data } = await supabase.auth.getUser();
|
||||||
if (data.user) {
|
if (data.user) {
|
||||||
router.push("/resource");
|
router.push("/home");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkUser();
|
checkUser();
|
||||||
|
|
89
compass/app/home/layout.tsx
Normal file
89
compass/app/home/layout.tsx
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
"use client";
|
||||||
|
import Sidebar from "@/components/Sidebar/Sidebar";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { createClient } from "@/utils/supabase/client";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import User, { Role } from "@/utils/models/User";
|
||||||
|
import Loading from "@/components/auth/Loading";
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||||
|
const [user, setUser] = useState<User>();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getUser() {
|
||||||
|
const supabase = createClient();
|
||||||
|
|
||||||
|
const { data, error } = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
console.log(data, error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log("Accessed home page but not logged in");
|
||||||
|
router.push("/auth/login");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userData = await fetch(
|
||||||
|
`${process.env.NEXT_PUBLIC_HOST}/api/user?uuid=${data.user.id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
setUser(await userData.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser();
|
||||||
|
}, [router]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-row">
|
||||||
|
{user ? (
|
||||||
|
<div>
|
||||||
|
{/* button to open sidebar */}
|
||||||
|
<button
|
||||||
|
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
|
||||||
|
className={`fixed z-20 p-2 text-gray-500 hover:text-gray-800 left-0`}
|
||||||
|
aria-label={"Open sidebar"}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
!isSidebarOpen && (
|
||||||
|
<ChevronDoubleRightIcon className="h-5 w-5" />
|
||||||
|
) // Icon for closing the sidebar
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
{/* sidebar */}
|
||||||
|
<div
|
||||||
|
className={`absolute inset-y-0 left-0 transform ${
|
||||||
|
isSidebarOpen
|
||||||
|
? "translate-x-0"
|
||||||
|
: "-translate-x-full"
|
||||||
|
} w-64 transition duration-300 ease-in-out`}
|
||||||
|
>
|
||||||
|
<Sidebar
|
||||||
|
name={user.username}
|
||||||
|
email={user.email}
|
||||||
|
setIsSidebarOpen={setIsSidebarOpen}
|
||||||
|
isAdmin={user.role === Role.ADMIN}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* page ui */}
|
||||||
|
<div
|
||||||
|
className={`flex-1 transition duration-300 ease-in-out ${
|
||||||
|
isSidebarOpen ? "ml-64" : "ml-0"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Loading />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
61
compass/app/home/page.tsx
Normal file
61
compass/app/home/page.tsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
"use client";
|
||||||
|
import Callout from "@/components/resource/Callout";
|
||||||
|
import Card from "@/components/resource/Card";
|
||||||
|
import { LandingSearchBar } from "@/components/resource/LandingSearchBar";
|
||||||
|
import {
|
||||||
|
BookOpenIcon,
|
||||||
|
BookmarkIcon,
|
||||||
|
ClipboardIcon,
|
||||||
|
} from "@heroicons/react/24/solid";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col">
|
||||||
|
{/* icon + title */}
|
||||||
|
<div className="pt-16 px-8 pb-4 flex-row">
|
||||||
|
<div className="mb-4 flex items-center space-x-4">
|
||||||
|
<Image
|
||||||
|
src="/logo.png"
|
||||||
|
alt="Compass Center logo."
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
/>
|
||||||
|
<h1 className="font-bold text-2xl text-purple-800">
|
||||||
|
Compass Center Advocate Landing Page
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<Callout>
|
||||||
|
Welcome! Below you will find a list of resources for the
|
||||||
|
Compass Center's trained advocates. These materials
|
||||||
|
serve to virtually provide a collection of advocacy,
|
||||||
|
resource, and hotline manuals and information.
|
||||||
|
<b>
|
||||||
|
{" "}
|
||||||
|
If you are an advocate looking for the contact
|
||||||
|
information of a particular Compass Center employee,
|
||||||
|
please directly contact your staff back-up or the person
|
||||||
|
in charge of your training.
|
||||||
|
</b>
|
||||||
|
</Callout>
|
||||||
|
</div>
|
||||||
|
<div className="p-8 flex-grow border-t border-gray-200 bg-gray-50">
|
||||||
|
{/* link to different pages */}
|
||||||
|
<div className="grid grid-cols-3 gap-6 pb-6">
|
||||||
|
<Link href="/resource">
|
||||||
|
<Card icon={<BookmarkIcon />} text="Resources" />
|
||||||
|
</Link>
|
||||||
|
<Link href="/service">
|
||||||
|
<Card icon={<ClipboardIcon />} text="Services" />
|
||||||
|
</Link>
|
||||||
|
<Link href="/training-manual">
|
||||||
|
<Card icon={<BookOpenIcon />} text="Training Manuals" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
{/* search bar */}
|
||||||
|
<LandingSearchBar />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Sidebar from "@/components/Sidebar/Sidebar";
|
import Sidebar from "@/components/Sidebar/Sidebar";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
|
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
|
||||||
import { createClient } from "@/utils/supabase/client";
|
import { createClient } from "@/utils/supabase/client";
|
||||||
import { useEffect } from "react";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useEffect } from "react";
|
||||||
import User, { Role } from "@/utils/models/User";
|
import User, { Role } from "@/utils/models/User";
|
||||||
import Loading from "@/components/auth/Loading";
|
import Loading from "@/components/auth/Loading";
|
||||||
|
|
||||||
|
@ -14,8 +15,8 @@ export default function RootLayout({
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||||
const [user, setUser] = useState<User>();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [user, setUser] = useState<User>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getUser() {
|
async function getUser() {
|
||||||
|
@ -27,7 +28,7 @@ export default function RootLayout({
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log("Accessed resource page but not logged in");
|
console.log("Accessed resource page but not logged in");
|
||||||
router.push("auth/login");
|
router.push("/auth/login");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +36,9 @@ export default function RootLayout({
|
||||||
`${process.env.NEXT_PUBLIC_HOST}/api/user?uuid=${data.user.id}`
|
`${process.env.NEXT_PUBLIC_HOST}/api/user?uuid=${data.user.id}`
|
||||||
);
|
);
|
||||||
|
|
||||||
setUser(await userData.json());
|
const user: User = await userData.json();
|
||||||
|
|
||||||
|
setUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUser();
|
getUser();
|
||||||
|
@ -66,9 +69,9 @@ export default function RootLayout({
|
||||||
} w-64 transition duration-300 ease-in-out`}
|
} w-64 transition duration-300 ease-in-out`}
|
||||||
>
|
>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
|
setIsSidebarOpen={setIsSidebarOpen}
|
||||||
name={user.username}
|
name={user.username}
|
||||||
email={user.email}
|
email={user.email}
|
||||||
setIsSidebarOpen={setIsSidebarOpen}
|
|
||||||
isAdmin={user.role === Role.ADMIN}
|
isAdmin={user.role === Role.ADMIN}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,54 +1,45 @@
|
||||||
"use client";
|
"use client";
|
||||||
import Callout from "@/components/resource/Callout";
|
|
||||||
import Card from "@/components/resource/Card";
|
import { PageLayout } from "@/components/PageLayout";
|
||||||
import { LandingSearchBar } from "@/components/resource/LandingSearchBar";
|
import { Table } from "@/components/Table/Index";
|
||||||
import {
|
import User from "@/utils/models/User";
|
||||||
BookOpenIcon,
|
import { createClient } from "@/utils/supabase/client";
|
||||||
BookmarkIcon,
|
|
||||||
ClipboardIcon,
|
import { UsersIcon } from "@heroicons/react/24/solid";
|
||||||
} from "@heroicons/react/24/solid";
|
import { useEffect, useState } from "react";
|
||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getUser() {
|
||||||
|
const supabase = createClient();
|
||||||
|
|
||||||
|
const { data, error } = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log("Accessed admin page but not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userListData = await fetch(
|
||||||
|
`${process.env.NEXT_PUBLIC_HOST}/api/user/all?uuid=${data.user.id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const users: User[] = await userListData.json();
|
||||||
|
|
||||||
|
setUsers(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col">
|
<div className="min-h-screen flex flex-col">
|
||||||
{/* icon + title */}
|
{/* icon + title */}
|
||||||
<div className="pt-16 px-8 pb-4 flex-row">
|
<PageLayout title="Users" icon={<UsersIcon />}>
|
||||||
<div className="mb-4 flex items-center space-x-4">
|
<Table users={users} />
|
||||||
<Image
|
</PageLayout>
|
||||||
src="/logo.png"
|
|
||||||
alt="Compass Center logo."
|
|
||||||
width={25}
|
|
||||||
height={25}
|
|
||||||
/>
|
|
||||||
<h1 className="font-bold text-2xl text-purple-800">
|
|
||||||
Compass Center Advocate Landing Page
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<Callout>
|
|
||||||
Welcome! Below you will find a list of resources for the
|
|
||||||
Compass Center's trained advocates. These materials
|
|
||||||
serve to virtually provide a collection of advocacy,
|
|
||||||
resource, and hotline manuals and information.
|
|
||||||
<b>
|
|
||||||
{" "}
|
|
||||||
If you are an advocate looking for the contact
|
|
||||||
information of a particular Compass Center employee,
|
|
||||||
please directly contact your staff back-up or the person
|
|
||||||
in charge of your training.
|
|
||||||
</b>
|
|
||||||
</Callout>
|
|
||||||
</div>
|
|
||||||
<div className="p-8 flex-grow border-t border-gray-200 bg-gray-50">
|
|
||||||
{/* link to different pages */}
|
|
||||||
<div className="grid grid-cols-3 gap-6 pb-6">
|
|
||||||
<Card icon={<BookmarkIcon />} text="Resources" />
|
|
||||||
<Card icon={<ClipboardIcon />} text="Services" />
|
|
||||||
<Card icon={<BookOpenIcon />} text="Training Manuals" />
|
|
||||||
</div>
|
|
||||||
{/* search bar */}
|
|
||||||
<LandingSearchBar />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
100
compass/app/service/layout.tsx
Normal file
100
compass/app/service/layout.tsx
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import Sidebar from "@/components/Sidebar/Sidebar";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { createClient } from "@/utils/supabase/client";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import User, { Role } from "@/utils/models/User";
|
||||||
|
import Loading from "@/components/auth/Loading";
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||||
|
const router = useRouter();
|
||||||
|
const [user, setUser] = useState<User>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getUser() {
|
||||||
|
const supabase = createClient();
|
||||||
|
|
||||||
|
const { data, error } = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
console.log(data, error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log("Accessed admin page but not logged in");
|
||||||
|
router.push("/auth/login");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userData = await fetch(
|
||||||
|
`${process.env.NEXT_PUBLIC_HOST}/api/user?uuid=${data.user.id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const user: User = await userData.json();
|
||||||
|
|
||||||
|
if (user.role !== Role.ADMIN) {
|
||||||
|
console.log(
|
||||||
|
`Accessed admin page but incorrect permissions: ${user.username} ${user.role}`
|
||||||
|
);
|
||||||
|
router.push("/auth/login");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser();
|
||||||
|
}, [router]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-row">
|
||||||
|
{user ? (
|
||||||
|
<div>
|
||||||
|
{/* button to open sidebar */}
|
||||||
|
<button
|
||||||
|
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
|
||||||
|
className={`fixed z-20 p-2 text-gray-500 hover:text-gray-800 left-0`}
|
||||||
|
aria-label={"Open sidebar"}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
!isSidebarOpen && (
|
||||||
|
<ChevronDoubleRightIcon className="h-5 w-5" />
|
||||||
|
) // Icon for closing the sidebar
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
{/* sidebar */}
|
||||||
|
<div
|
||||||
|
className={`absolute inset-y-0 left-0 transform ${
|
||||||
|
isSidebarOpen
|
||||||
|
? "translate-x-0"
|
||||||
|
: "-translate-x-full"
|
||||||
|
} w-64 transition duration-300 ease-in-out`}
|
||||||
|
>
|
||||||
|
<Sidebar
|
||||||
|
setIsSidebarOpen={setIsSidebarOpen}
|
||||||
|
name={user.username}
|
||||||
|
email={user.email}
|
||||||
|
isAdmin={user.role === Role.ADMIN}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* page ui */}
|
||||||
|
<div
|
||||||
|
className={`flex-1 transition duration-300 ease-in-out ${
|
||||||
|
isSidebarOpen ? "ml-64" : "ml-0"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Loading />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
45
compass/app/service/page.tsx
Normal file
45
compass/app/service/page.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { PageLayout } from "@/components/PageLayout";
|
||||||
|
import { Table } from "@/components/Table/Index";
|
||||||
|
import User from "@/utils/models/User";
|
||||||
|
import { createClient } from "@/utils/supabase/client";
|
||||||
|
|
||||||
|
import { UsersIcon } from "@heroicons/react/24/solid";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getUser() {
|
||||||
|
const supabase = createClient();
|
||||||
|
|
||||||
|
const { data, error } = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log("Accessed admin page but not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userListData = await fetch(
|
||||||
|
`${process.env.NEXT_PUBLIC_HOST}/api/user/all?uuid=${data.user.id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const users: User[] = await userListData.json();
|
||||||
|
|
||||||
|
setUsers(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col">
|
||||||
|
{/* icon + title */}
|
||||||
|
<PageLayout title="Users" icon={<UsersIcon />}>
|
||||||
|
<Table users={users} />
|
||||||
|
</PageLayout>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
40
compass/app/training-manual/page.tsx
Normal file
40
compass/app/training-manual/page.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// page.tsx
|
||||||
|
import React from "react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
const ComingSoonPage: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Training Manuals - Coming Soon</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Our training manuals page is coming soon. Stay tuned for updates!"
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
<div className="min-h-screen bg-gradient-to-r from-purple-600 to-blue-500 flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-4xl font-bold text-white mb-4">
|
||||||
|
Training Manuals
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-white mb-8">
|
||||||
|
Our training manuals page is under construction.
|
||||||
|
</p>
|
||||||
|
<p className="text-lg text-white">
|
||||||
|
Stay tuned for updates!
|
||||||
|
</p>
|
||||||
|
<div className="mt-8">
|
||||||
|
<Link href="/home">
|
||||||
|
<button className="bg-white text-purple-600 font-semibold py-2 px-4 rounded-full shadow-md hover:bg-purple-100 transition duration-300">
|
||||||
|
Notify Me
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComingSoonPage;
|
|
@ -5,6 +5,7 @@ import {
|
||||||
BookmarkIcon,
|
BookmarkIcon,
|
||||||
ClipboardIcon,
|
ClipboardIcon,
|
||||||
BookOpenIcon,
|
BookOpenIcon,
|
||||||
|
LockClosedIcon,
|
||||||
} from "@heroicons/react/24/solid";
|
} from "@heroicons/react/24/solid";
|
||||||
import { SidebarItem } from "./SidebarItem";
|
import { SidebarItem } from "./SidebarItem";
|
||||||
import { UserProfile } from "../resource/UserProfile";
|
import { UserProfile } from "../resource/UserProfile";
|
||||||
|
@ -47,7 +48,7 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
<nav className="flex flex-col">
|
<nav className="flex flex-col">
|
||||||
{admin && (
|
{admin && (
|
||||||
<SidebarItem
|
<SidebarItem
|
||||||
icon={<HomeIcon />}
|
icon={<LockClosedIcon />}
|
||||||
text="Admin"
|
text="Admin"
|
||||||
active={true}
|
active={true}
|
||||||
redirect="/admin"
|
redirect="/admin"
|
||||||
|
@ -58,7 +59,7 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
icon={<HomeIcon />}
|
icon={<HomeIcon />}
|
||||||
text="Home"
|
text="Home"
|
||||||
active={true}
|
active={true}
|
||||||
redirect="/resource"
|
redirect="/home"
|
||||||
/>
|
/>
|
||||||
<SidebarItem
|
<SidebarItem
|
||||||
icon={<BookmarkIcon />}
|
icon={<BookmarkIcon />}
|
||||||
|
|
306
compass/components/Table/ResourceIndex.tsx
Normal file
306
compass/components/Table/ResourceIndex.tsx
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
// for showcasing to compass
|
||||||
|
|
||||||
|
import users from "./users.json";
|
||||||
|
import {
|
||||||
|
Cell,
|
||||||
|
ColumnDef,
|
||||||
|
Row,
|
||||||
|
createColumnHelper,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
sortingFns,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import {
|
||||||
|
ChangeEvent,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
FunctionComponent,
|
||||||
|
useRef,
|
||||||
|
ChangeEventHandler,
|
||||||
|
Key,
|
||||||
|
} from "react";
|
||||||
|
import { RowOptionMenu } from "./RowOptionMenu";
|
||||||
|
import { RowOpenAction } from "./RowOpenAction";
|
||||||
|
import { TableAction } from "./TableAction";
|
||||||
|
import {
|
||||||
|
AtSymbolIcon,
|
||||||
|
Bars2Icon,
|
||||||
|
ArrowDownCircleIcon,
|
||||||
|
PlusIcon,
|
||||||
|
} from "@heroicons/react/24/solid";
|
||||||
|
import TagsInput from "../TagsInput/Index";
|
||||||
|
import { rankItem } from "@tanstack/match-sorter-utils";
|
||||||
|
import User from "@/utils/models/User";
|
||||||
|
|
||||||
|
// For search
|
||||||
|
const fuzzyFilter = (
|
||||||
|
row: Row<any>,
|
||||||
|
columnId: string,
|
||||||
|
value: any,
|
||||||
|
addMeta: (meta: any) => void
|
||||||
|
) => {
|
||||||
|
// Rank the item
|
||||||
|
const itemRank = rankItem(row.getValue(columnId), value);
|
||||||
|
|
||||||
|
// Store the ranking info
|
||||||
|
addMeta(itemRank);
|
||||||
|
|
||||||
|
// Return if the item should be filtered in/out
|
||||||
|
return itemRank.passed;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Table = ({ users }: { users: User[] }) => {
|
||||||
|
const columnHelper = createColumnHelper<User>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const sortedUsers = [...users].sort((a, b) =>
|
||||||
|
a.visible === b.visible ? 0 : a.visible ? -1 : 1
|
||||||
|
);
|
||||||
|
setData(sortedUsers);
|
||||||
|
}, [users]);
|
||||||
|
|
||||||
|
const deleteUser = (userId: number) => {
|
||||||
|
console.log(data);
|
||||||
|
setData((currentData) =>
|
||||||
|
currentData.filter((user) => user.id !== userId)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideUser = (userId: number) => {
|
||||||
|
console.log(`Toggling visibility for user with ID: ${userId}`);
|
||||||
|
setData((currentData) => {
|
||||||
|
const newData = currentData
|
||||||
|
.map((user) => {
|
||||||
|
if (user.id === userId) {
|
||||||
|
return { ...user, visible: !user.visible };
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
})
|
||||||
|
.sort((a, b) =>
|
||||||
|
a.visible === b.visible ? 0 : a.visible ? -1 : 1
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(newData);
|
||||||
|
return newData;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const [presetOptions, setPresetOptions] = useState([
|
||||||
|
"administrator",
|
||||||
|
"volunteer",
|
||||||
|
"employee",
|
||||||
|
]);
|
||||||
|
const [tagColors, setTagColors] = useState(new Map());
|
||||||
|
|
||||||
|
const getTagColor = (tag: string) => {
|
||||||
|
if (!tagColors.has(tag)) {
|
||||||
|
const colors = [
|
||||||
|
"bg-cyan-100",
|
||||||
|
"bg-blue-100",
|
||||||
|
"bg-green-100",
|
||||||
|
"bg-yellow-100",
|
||||||
|
"bg-purple-100",
|
||||||
|
];
|
||||||
|
const randomColor =
|
||||||
|
colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
setTagColors(new Map(tagColors).set(tag, randomColor));
|
||||||
|
}
|
||||||
|
return tagColors.get(tag);
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
columnHelper.display({
|
||||||
|
id: "options",
|
||||||
|
cell: (props) => (
|
||||||
|
<RowOptionMenu
|
||||||
|
onDelete={() => deleteUser(props.row.original.id)}
|
||||||
|
onHide={() => hideUser(props.row.original.id)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("username", {
|
||||||
|
header: () => (
|
||||||
|
<>
|
||||||
|
<Bars2Icon className="inline align-top h-4" /> Username
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
cell: (info) => (
|
||||||
|
<RowOpenAction
|
||||||
|
title={info.getValue()}
|
||||||
|
rowData={info.row.original}
|
||||||
|
onRowUpdate={handleRowUpdate}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("role", {
|
||||||
|
header: () => (
|
||||||
|
<>
|
||||||
|
<ArrowDownCircleIcon className="inline align-top h-4" />{" "}
|
||||||
|
Role
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
cell: (info) => (
|
||||||
|
<TagsInput
|
||||||
|
presetValue={info.getValue()}
|
||||||
|
presetOptions={presetOptions}
|
||||||
|
setPresetOptions={setPresetOptions}
|
||||||
|
getTagColor={getTagColor}
|
||||||
|
setTagColors={setTagColors}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("email", {
|
||||||
|
header: () => (
|
||||||
|
<>
|
||||||
|
<AtSymbolIcon className="inline align-top h-4" /> Email
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
cell: (info) => (
|
||||||
|
<span className="ml-2 text-gray-500 underline hover:text-gray-400">
|
||||||
|
{info.getValue()}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("program", {
|
||||||
|
header: () => (
|
||||||
|
<>
|
||||||
|
<ArrowDownCircleIcon className="inline align-top h-4" />{" "}
|
||||||
|
Program
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
cell: (info) => <TagsInput presetValue={info.getValue()} />,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const [data, setData] = useState<User[]>([...users]);
|
||||||
|
|
||||||
|
const addUser = () => {
|
||||||
|
setData([...data]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Searching
|
||||||
|
const [query, setQuery] = useState("");
|
||||||
|
const handleSearchChange = (e: ChangeEvent) => {
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
setQuery(String(target.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCellChange = (e: ChangeEvent, key: Key) => {
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
console.log(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Filtering
|
||||||
|
|
||||||
|
// TODO: Sorting
|
||||||
|
|
||||||
|
// added this fn for editing rows
|
||||||
|
const handleRowUpdate = (updatedRow: User) => {
|
||||||
|
const dataIndex = data.findIndex((row) => row.id === updatedRow.id);
|
||||||
|
if (dataIndex !== -1) {
|
||||||
|
const updatedData = [...data];
|
||||||
|
updatedData[dataIndex] = updatedRow;
|
||||||
|
setData(updatedData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
filterFns: {
|
||||||
|
fuzzy: fuzzyFilter,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
globalFilter: query,
|
||||||
|
},
|
||||||
|
onGlobalFilterChange: setQuery,
|
||||||
|
globalFilterFn: fuzzyFilter,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleRowData = (row: any) => {
|
||||||
|
const rowData: any = {};
|
||||||
|
row.cells.forEach((cell: any) => {
|
||||||
|
rowData[cell.column.id] = cell.value;
|
||||||
|
});
|
||||||
|
// Use rowData object containing data from all columns for the current row
|
||||||
|
console.log(rowData);
|
||||||
|
return rowData;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="flex flex-row justify-end">
|
||||||
|
<TableAction query={query} handleChange={handleSearchChange} />
|
||||||
|
</div>
|
||||||
|
<table className="w-full text-xs text-left rtl:text-right">
|
||||||
|
<thead className="text-xs text-gray-500 capitalize">
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header, i) => (
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className={
|
||||||
|
"p-2 border-gray-200 border-y font-medium " +
|
||||||
|
(1 < i && i < columns.length - 1
|
||||||
|
? "border-x"
|
||||||
|
: "")
|
||||||
|
}
|
||||||
|
key={header.id}
|
||||||
|
>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{table.getRowModel().rows.map((row) => {
|
||||||
|
// Individual row
|
||||||
|
const isUserVisible = row.original.visible;
|
||||||
|
const rowClassNames = `text-gray-800 border-y lowercase hover:bg-gray-50 ${
|
||||||
|
!isUserVisible ? "bg-gray-200 text-gray-500" : ""
|
||||||
|
}`;
|
||||||
|
return (
|
||||||
|
<tr className={rowClassNames} key={row.id}>
|
||||||
|
{row.getVisibleCells().map((cell, i) => (
|
||||||
|
<td
|
||||||
|
key={cell.id}
|
||||||
|
className={
|
||||||
|
"[&:nth-child(n+3)]:border-x relative first:text-left first:px-0 last:border-none"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
className="p-3 border-y border-gray-200 text-gray-600 hover:bg-gray-50"
|
||||||
|
colSpan={100}
|
||||||
|
onClick={addUser}
|
||||||
|
>
|
||||||
|
<span className="flex ml-1 text-gray-500">
|
||||||
|
<PlusIcon className="inline h-4 mr-1" />
|
||||||
|
New
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -17,7 +17,9 @@ const TagsInput: React.FC<TagsInputProps> = ({
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
const [cellSelected, setCellSelected] = useState(false);
|
const [cellSelected, setCellSelected] = useState(false);
|
||||||
const [tags, setTags] = useState<Set<string>>(
|
const [tags, setTags] = useState<Set<string>>(
|
||||||
new Set(presetValue ? [presetValue] : [])
|
typeof presetValue === "string"
|
||||||
|
? new Set([presetValue])
|
||||||
|
: new Set(presetValue)
|
||||||
);
|
);
|
||||||
const [options, setOptions] = useState<Set<string>>(new Set(presetOptions));
|
const [options, setOptions] = useState<Set<string>>(new Set(presetOptions));
|
||||||
const dropdown = useRef<HTMLDivElement>(null);
|
const dropdown = useRef<HTMLDivElement>(null);
|
||||||
|
|
|
@ -7,6 +7,8 @@ export interface Tags {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TagsArray = ({ tags, handleDelete, active = false }: Tags) => {
|
export const TagsArray = ({ tags, handleDelete, active = false }: Tags) => {
|
||||||
|
console.log(tags);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex ml-2 flex-wrap gap-2 items-center">
|
<div className="flex ml-2 flex-wrap gap-2 items-center">
|
||||||
{Array.from(tags).map((tag, index) => {
|
{Array.from(tags).map((tag, index) => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user