This repository has been archived by the owner on Dec 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #78 from brown-ccv/feat-admin-page
Feat: admin page (history)
- Loading branch information
Showing
11 changed files
with
804 additions
and
377 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { useState } from "react" | ||
import type { UserInfo } from "firebase/auth" | ||
import Login from "../components/Login" | ||
import { useActivityData } from "../hooks/activity.ts" | ||
import ActivityTable from "./ActivityTable.tsx" | ||
|
||
const ActivityPage = () => { | ||
const [user, setUser] = useState<UserInfo | null | undefined>(null) | ||
const setUserFunction = (loggedUser: UserInfo | null | undefined) => { | ||
setUser(loggedUser) | ||
} | ||
const activityData = useActivityData(user) | ||
|
||
return ( | ||
<div className="space-y-8"> | ||
<section className="space-y-6"> | ||
<Login currentUser={user} setUserFunction={setUserFunction} /> | ||
{!user && ( | ||
<p> | ||
This section of the website is reserved for administrators to view download statistics. | ||
</p> | ||
)} | ||
</section> | ||
{user && activityData && ( | ||
<section className="space-y-2"> | ||
<h3> | ||
<span className="font-bold px-2">{activityData.length}</span> download(s) | ||
</h3> | ||
<ActivityTable data={activityData} /> | ||
</section> | ||
)} | ||
</div> | ||
) | ||
} | ||
|
||
export default ActivityPage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import React from "react" | ||
import type { activityType } from "../hooks/activity.ts" | ||
|
||
export interface ActivityTableProps { | ||
data: activityType[] | ||
} | ||
|
||
const ActivityTable: React.FC<ActivityTableProps> = ({ data }) => { | ||
return ( | ||
<div className="w-full overflow-x-scroll no-scrollbar"> | ||
<table className="table-fixed border-spacing-2 w-full"> | ||
<thead> | ||
<tr className="text-xl bg-neutral-100 text-left text-neutral-900"> | ||
<th>Description</th> | ||
<th className="w-1/4">User</th> | ||
<th className="w-1/5">Download Date</th> | ||
</tr> | ||
</thead> | ||
|
||
<tbody> | ||
{data && | ||
data.map(({ name, institution, email, description, date }, i) => { | ||
const stringDate = date.toDate().toDateString() | ||
return ( | ||
<tr key={i} className="align-top"> | ||
<td> | ||
<p>{description}</p> | ||
</td> | ||
<td> | ||
<div> | ||
<p className="text-lg font-semibold text-neutral-900">{name}</p> | ||
<p className="text-neutral-700 italic overflow-hidden overflow-ellipsis "> | ||
{email} | ||
</p> | ||
<p className="small">{institution}</p> | ||
</div> | ||
</td> | ||
<td>{stringDate}</td> | ||
</tr> | ||
) | ||
})} | ||
</tbody> | ||
</table> | ||
</div> | ||
) | ||
} | ||
export default ActivityTable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React, { useState } from "react" | ||
import { type UserInfo } from "firebase/auth" | ||
import { handleLogin, handleLogout } from "../firebase" | ||
import Button from "./Button.tsx" | ||
|
||
interface LoginProps { | ||
currentUser: UserInfo | null | undefined | ||
setUserFunction: (loggedUser: UserInfo | null | undefined) => void | ||
} | ||
|
||
const Login: React.FC<LoginProps> = ({ currentUser, setUserFunction }) => { | ||
const [message, setMessage] = useState("") | ||
|
||
const login = async () => { | ||
await handleLogin().then((loggedUser) => { | ||
if (!loggedUser) | ||
setMessage( | ||
"You must be an admin on this project in order to see this data. If you believe this to be an error, please reach out to David Lindstrom ([email protected])." | ||
) | ||
setUserFunction(loggedUser) | ||
}) | ||
} | ||
const logout = async () => { | ||
await handleLogout() | ||
setUserFunction(null) | ||
setMessage("") | ||
} | ||
return ( | ||
<section className="flex flex-col gap-6"> | ||
<div> | ||
{currentUser ? ( | ||
<Button onClick={() => logout()}>Log Out</Button> | ||
) : ( | ||
<Button onClick={() => login()}>Log In</Button> | ||
)} | ||
</div> | ||
{message && <p className="font-semibold text-primary-300">{message}</p>} | ||
</section> | ||
) | ||
} | ||
export default Login |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,12 @@ | ||
export const SITE_TITLE = "MMP" | ||
export const SITE_DESCRIPTION = "Mesoamerican Migration Project" | ||
|
||
export const LINKS = ["people", "news", "publications", "study-design", "data", "documentation"] | ||
export const LINKS = [ | ||
"people", | ||
"news", | ||
"publications", | ||
"study-design", | ||
"data", | ||
"documentation", | ||
"activity", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,71 @@ | ||
import { initializeApp } from "firebase/app" | ||
import { getFirestore, addDoc, collection } from "firebase/firestore" | ||
import { addDoc, collection, getDocs, getFirestore, Timestamp } from "firebase/firestore" | ||
import { getAuth, GoogleAuthProvider, signInWithPopup, signOut } from "firebase/auth" | ||
import firebaseConfig from "./firebase-config.json" | ||
|
||
const provider = new GoogleAuthProvider() | ||
provider.setCustomParameters({ | ||
hd: "brown.edu", | ||
}) | ||
|
||
const app = initializeApp(firebaseConfig) | ||
const db = getFirestore(app) | ||
export const auth = getAuth(app) | ||
|
||
export const addHistoryData = async (inputs) => { | ||
export const addActivityData = async (inputs) => { | ||
try { | ||
const docRef = await addDoc(collection(db, "history"), { | ||
name: inputs.name, | ||
institution: inputs.institution, | ||
email: inputs.email, | ||
description: inputs.description, | ||
date: new Date(), | ||
date: Timestamp.now(), | ||
}) | ||
console.log("Document written with ID: ", docRef.id) | ||
} catch (e) { | ||
console.error("Error adding document: ", e) | ||
} | ||
} | ||
|
||
export const getAdminUsers = async () => { | ||
const querySnapshot = await getDocs(collection(db, "admin")) | ||
const data = [] | ||
querySnapshot.forEach((doc) => { | ||
data.push(doc.data()) | ||
}) | ||
return data.map((user) => { | ||
return user.email | ||
}) | ||
} | ||
|
||
export const getActivityData = async () => { | ||
const querySnapshot = await getDocs(collection(db, "history")) | ||
const data = [] | ||
querySnapshot.forEach((doc) => { | ||
data.push(doc.data()) | ||
}) | ||
return data | ||
} | ||
|
||
export const handleLogin = async () => { | ||
try { | ||
const result = await signInWithPopup(auth, provider) | ||
const { email } = result.user | ||
const adminUsers = await getAdminUsers() | ||
if (!adminUsers.includes(email.toLowerCase())) { | ||
return null | ||
} else { | ||
return result.user | ||
} | ||
} catch (error) { | ||
console.log(error) | ||
} | ||
} | ||
|
||
export const handleLogout = async () => { | ||
try { | ||
await signOut(auth) | ||
} catch (error) { | ||
console.log(error) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import type { Timestamp } from "firebase/firestore" | ||
import type { UserInfo } from "firebase/auth" | ||
import { useEffect, useState } from "react" | ||
import { getActivityData } from "../firebase" | ||
|
||
export interface activityType { | ||
name: string | ||
institution: string | ||
email: string | ||
description: string | ||
date: Timestamp | ||
} | ||
|
||
const getData = async () => { | ||
return await getActivityData() | ||
} | ||
|
||
export function useActivityData(user: UserInfo | null | undefined) { | ||
const [activityData, setActivityData] = useState<activityType[] | null>(null) | ||
useEffect(() => { | ||
if (user) { | ||
getData().then((data) => { | ||
setActivityData(data) | ||
}) | ||
} | ||
}, [user]) | ||
return activityData | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
import Layout from "../layouts/Layout.astro" | ||
import ActivityPage from "../components/ActivityPage" | ||
--- | ||
|
||
<Layout title="Admin" description="History Table of Downloads"> | ||
<ActivityPage client:only="react" /> | ||
</Layout> |