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

T11/admin insert data view #17

Closed
wants to merge 6 commits into from
Closed
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
4 changes: 2 additions & 2 deletions amplify/auth/pre-sign-up/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export const dataClient = generateClient<Schema>();

export const handler: PreSignUpTriggerHandler = async (event) => {
return await dataClient.models.User.create({
firstName: "Default",
lastName: "Name",
firstName: "",
lastName: "",
role: "GuestUser",
id: event.userName,
email: event.request.userAttributes.email,
Expand Down
6 changes: 3 additions & 3 deletions amplify/data/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ const schema = a
})
.authorization((allow) => [
allow.group("GuestUser").to(["read"]),
allow.group("AdminUser").to(["read", "update", "delete"]),
allow.group("AdminUser").to(["read", "update", "delete", "create"]),
]),
SessionsAttended: a
.model({
id: a.id().required(),
sessionAttendedId: a.id().required(),
earningsThatSession: a.float(),
date: a.belongsTo("User", "sessionAttendedId"),
date: a.belongsTo("User", "sessionAttendedId"), //date belongs to a user? -matthew july 1
})
.authorization((allow) => [
allow.group("GuestUser").to(["read"]),
allow.group("AdminUser").to(["read", "update", "delete"]),
allow.group("AdminUser").to(["read", "update", "delete", "create"]),
]),
})
.authorization((allow) => [allow.resource(preSignUp).to(["mutate"])]);
Expand Down
9 changes: 9 additions & 0 deletions src/app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import AdminForm from "@/components/admin/AdminForm";

export default function Admin() {
return (
<div className="flex min-h-screen flex-col items-center justify-between p-24">
<AdminForm />
</div>
);
}
8 changes: 6 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Inter } from "next/font/google";
import Footer from "@/components/Footer";
import Header from "@/components/Header";
import AuthQueryProvider from "@/components/contexts/Auth";
import { UserContextProvider } from "@/components/contexts/UserContext";
import { isAuthenticated } from "@/components/contexts/amplifyUtils";

import "./globals.css";
Expand All @@ -24,8 +25,11 @@ export default async function RootLayout({
<html lang="en">
<body className={inter.className}>
<AuthQueryProvider>
<Header isSignedIn={await isAuthenticated()} />
{children} <Footer />
<UserContextProvider>
<Header isSignedIn={await isAuthenticated()} />
{children}
<Footer />
</UserContextProvider>
</AuthQueryProvider>
</body>
</html>
Expand Down
19 changes: 17 additions & 2 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";

import { useUser } from "./contexts/UserContext";

export default function Header({
isSignedIn,
}: {
isSignedIn: boolean | undefined;
}) {
const [authCheck, setAuthCheck] = useState(isSignedIn);

const user = useUser().currentUser;

const router = useRouter();
useEffect(() => {
const hubListenerCancel = Hub.listen("auth", (data) => {
Expand Down Expand Up @@ -49,15 +53,26 @@ export default function Header({
{ name: "Spreadsheet", href: "/spreadsheet", loggedIn: true },
{ name: "Home", href: "/home", loggedIn: true },
{ name: "Planner", href: "/planner", loggedIn: true },
{ name: "Admin Panel", href: "/admin", loggedIn: true },
];

const routes = defaultRoutes.filter(
const loggedInRoutes = defaultRoutes.filter(
(route) => route.loggedIn === authCheck || route.loggedIn === undefined,
);

let renderedRoutes = loggedInRoutes;

if (user.role !== "AdminUser") {
renderedRoutes = loggedInRoutes.filter(
(route) => route.name != "Admin Panel",
);
}

console.log(user);

return (
<div className="flex h-24 flex-row items-center justify-evenly bg-slate-500">
{routes.map((route) => (
{renderedRoutes.map((route) => (
<Link key={route.name} href={route.href}>
{route.name}
</Link>
Expand Down
199 changes: 199 additions & 0 deletions src/components/admin/AdminForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
"use client";

import { generateClient } from "aws-amplify/data";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";

import { type Schema } from "@/../amplify/data/resource";

const client = generateClient<Schema>();

//we need to assign a list of these User objects to the useState
type User = {
id: string;
email: string;
firstName: string;
lastName: string;
role: string;
totalEarnings?: number;
sessionsAttended: SessionAttended[];
};

//referenced by User objects
type SessionAttended = {
id: string;
sessionAttendedId: string;
earningsThatSession?: number;
date: string;
};

type SessionFormData = {
sessionAttendedId: string;
earningsThatSession: number;
date: string;
};

export default function AdminForm() {
const [users, setUsers] = useState<User[]>([]);
const [searchQuery, setSearchQuery] = useState<string>("");
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [formSubmitted, setFormSubmitted] = useState<boolean>(false);
const { register, handleSubmit, reset } = useForm<SessionFormData>();

//to get all the users in our database
useEffect(() => {
async function fetchUsers() {
try {
const response = await client.models.User.list();
const userList = response.data as unknown as User[];
setUsers(userList);
} catch (error) {
console.error("Error fetching users:", error);
}
}

fetchUsers();
}, []);

const handleUserClick = (user: User) => {
setSelectedUser(user);
reset(); // Reset the form when a new user is selected
setFormSubmitted(false); // Reset form submitted state
};

const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(event.target.value);
};

const onSubmit = async (data: SessionFormData) => {
try {
await client.models.SessionsAttended.create({
...data,
sessionAttendedId: selectedUser!.id,
});
// Optionally refetch users or update local state
console.log("Session added:", data);
setFormSubmitted(true);
setTimeout(() => setFormSubmitted(false), 5000); //hide message after 5 seconds
} catch (error) {
console.error("Error creating session:", error);
}
};

const filteredUsers = users.filter((user) =>
`${user.firstName} ${user.lastName}`
.toLowerCase()
.includes(searchQuery.toLowerCase()),
);

return (
<div style={{ display: "flex", gap: "20px", padding: "20px" }}>
<div style={{ flex: "1" }}>
<h1>Admin Page</h1>
<input
type="text"
placeholder="Search for a name"
value={searchQuery}
onChange={handleSearchChange}
style={{
padding: "8px",
marginBottom: "10px",
border: "1px solid #ccc",
borderRadius: "4px",
width: "100%",
}}
/>
<ul style={{ listStyleType: "none", padding: "0" }}>
{filteredUsers.map((user) => (
<li
key={user.id}
onClick={() => handleUserClick(user)}
style={{
cursor: "pointer",
padding: "10px",
marginBottom: "5px",
border: "1px solid #ccc",
borderRadius: "4px",
backgroundColor:
selectedUser && selectedUser.id === user.id
? "#f0f0f0"
: "#fff",
}}
>
<strong>
{user.firstName} {user.lastName}
</strong>
<div>{user.email}</div>
</li>
))}
</ul>
</div>

{selectedUser && (
<div style={{ flex: "1" }}>
<h3>
Add a Session for {selectedUser.firstName} {selectedUser.lastName}
</h3>
<form
onSubmit={handleSubmit(onSubmit)}
style={{
display: "flex",
flexDirection: "column",
gap: "10px",
padding: "20px",
border: "1px solid #ccc",
borderRadius: "4px",
backgroundColor: "#fafafa",
}}
>
<div style={{ marginBottom: "10px" }}>
<label>Date</label>
<input
type="date"
{...register("date", { required: true })}
style={{
padding: "8px",
border: "1px solid #ccc",
borderRadius: "4px",
width: "100%",
}}
/>
</div>
<div style={{ marginBottom: "10px" }}>
<label>Earnings</label>
<input
type="number"
{...register("earningsThatSession", { required: true })}
style={{
padding: "8px",
border: "1px solid #ccc",
borderRadius: "4px",
width: "100%",
}}
/>
</div>
<button
type="submit"
style={{
padding: "10px",
backgroundColor: formSubmitted ? "green" : "#007bff",
color: "#fff",
border: "none",
borderRadius: "4px",
cursor: "pointer",
transition: "background-color 0.3s ease",
}}
>
{formSubmitted ? "Session Added" : "Add Session"}
</button>
{formSubmitted && (
<div style={{ marginTop: "10px", color: "green" }}>
Session has been successfully added!
</div>
)}
</form>
</div>
)}
</div>
);
}
9 changes: 3 additions & 6 deletions src/components/contexts/UserContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface IUser {
email?: string;
populated: boolean;
signedIn?: boolean;
role?: string;
}

interface IUserReturn {
Expand Down Expand Up @@ -80,14 +81,10 @@ export function UserContextProvider({ children }: Props) {
email: response.data?.email ?? "",
firstName: response.data?.firstName ?? "",
lastName: response.data?.lastName ?? "",
role: response.data?.role ?? "",
});
} catch (err) {
if (String(err).includes("No user")) {
setCurrentUser({
username: "",
type: UserType.GuestUser,
populated: true,
});
console.info("Not Logged in");
} else {
console.error(err);
Expand All @@ -98,7 +95,7 @@ export function UserContextProvider({ children }: Props) {
}, []);
return (
<UserContext.Provider value={{ currentUser }}>
{currentUser.populated && children}
{children}
</UserContext.Provider>
);
}
Expand Down
Loading