-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
34357b2
commit 9840ab9
Showing
23 changed files
with
455 additions
and
14 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import {useState} from "react"; | ||
import styles from "@/styles/components/dropdown_section.module.css"; | ||
|
||
interface DropdownSectionProps { | ||
title: string; | ||
children: React.ReactNode; | ||
open?: boolean; | ||
} | ||
|
||
|
||
export function DropdownSection({title, children, open = false}: DropdownSectionProps){ | ||
|
||
const [opened, setOpened] = useState(open) | ||
|
||
|
||
return( | ||
|
||
<> | ||
<div className={styles.titleBar}> | ||
<h1 className={styles.title}>{title}</h1> | ||
<button className={styles.button} onClick={() => setOpened(!opened)}> {opened ? "Close" : "Open"}</button> | ||
</div> | ||
<div className={styles.content + " " + (opened ? styles.open : styles.closed)}> | ||
{opened && children} | ||
</div> | ||
</> | ||
|
||
) | ||
|
||
} |
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 |
---|---|---|
@@ -1,5 +1,25 @@ | ||
import {User} from "next-auth"; | ||
|
||
export const ADMIN_USER_TYPE = 0; | ||
export const EDITOR_USER_TYPE = 1; | ||
export const MEMBER_USER_TYPE = 2; | ||
|
||
export const UNDEFINED_USER_TYPE = -1; | ||
export const UNDEFINED_USER_TYPE = -1; | ||
|
||
export interface RongoaUser extends User{ | ||
|
||
database: UserDatabaseDetails | ||
|
||
} | ||
|
||
export interface UserDatabaseDetails { | ||
|
||
id: number, | ||
user_name: string, | ||
user_email: string, | ||
user_type: number, | ||
user_last_login: string, | ||
user_api_keys: object, | ||
|
||
|
||
} |
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,218 @@ | ||
import HtmlHeader from "@/components/html_header"; | ||
import Navbar from "@/components/navbar"; | ||
import React, {useEffect, useRef} from "react"; | ||
import Section from "@/components/section"; | ||
import Footer from "@/components/footer"; | ||
import PageHeader from "@/components/page_header"; | ||
import styles from "@/styles/pages/account/index.module.css" | ||
import statsStyles from "@/styles/components/stats.module.css" | ||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; | ||
import {faCamera, faPerson, faSeedling} from "@fortawesome/free-solid-svg-icons"; | ||
import {signIn, signOut, useSession} from "next-auth/react"; | ||
import {ADMIN_USER_TYPE, EDITOR_USER_TYPE, MEMBER_USER_TYPE, RongoaUser} from "@/lib/users"; | ||
import axios from "axios"; | ||
import Image from "next/image"; | ||
import {globalStyles} from "@/lib/global_css"; | ||
import {DropdownSection} from "@/components/dropdown_section"; | ||
import {useRouter} from "next/router"; | ||
|
||
export default function Account() { | ||
|
||
const pageName = "Account"; | ||
|
||
const { data: session } = useSession() | ||
|
||
const router = useRouter() | ||
|
||
const [userName, setUserName] = React.useState<string | null | undefined>("") | ||
const [userEmail, setUserEmail] = React.useState<string | null | undefined>("") | ||
const [userRole, setUserRole] = React.useState<string | null | undefined>("") | ||
const [userImage, setUserImage] = React.useState<string | null | undefined>("") | ||
const [userLastLogin, setUserLastLogin] = React.useState<string | null | undefined>("") | ||
const [userPlants, setUserPlants] = React.useState<string | null | undefined>("") | ||
const [userPosts, setUserPosts] = React.useState<string | null | undefined>("") | ||
const [userPlantsData, setUserPlantsData] = React.useState(null) | ||
|
||
|
||
|
||
// Don't fetch the data again if it has already been fetched | ||
const dataFetch = useRef(false) | ||
|
||
|
||
useEffect(() => { | ||
|
||
// If there is session data | ||
if(session?.user) { | ||
|
||
let user = session.user as RongoaUser | ||
if(!user) return | ||
|
||
setUserName(user.name) | ||
setUserEmail(user.email) | ||
switch (user.database.user_type){ | ||
case MEMBER_USER_TYPE: | ||
setUserRole("Member") | ||
break | ||
|
||
case ADMIN_USER_TYPE: | ||
setUserRole("Admin") | ||
break | ||
|
||
case EDITOR_USER_TYPE: | ||
setUserRole("Editor") | ||
break | ||
} | ||
setUserImage(user.image) | ||
setUserLastLogin(user.database.user_last_login.split("T")[0]) | ||
setUserPosts("0") | ||
|
||
// Prevent the data from being fetched again | ||
if (dataFetch.current) | ||
return | ||
dataFetch.current = true | ||
|
||
// Get the users data | ||
fetchData() | ||
|
||
} | ||
|
||
}, [session]) | ||
|
||
|
||
const fetchData = async () => { | ||
|
||
|
||
// Get the users plants | ||
try { | ||
const plants = await axios.get("/api/user/plants/") | ||
if(!plants.data.error){ | ||
setUserPlants(plants.data.data.length.toString()) | ||
} | ||
setUserPlantsData(plants.data.data) | ||
} catch (e) { | ||
console.log(e) | ||
setUserPlants("0") | ||
} | ||
} | ||
|
||
const signOutUser = async () => { | ||
await signOut() | ||
await router.push("/") | ||
} | ||
|
||
const deleteAccount = async () => { | ||
await axios.delete("/api/user/delete/") | ||
await signOutUser() | ||
} | ||
|
||
|
||
return( | ||
|
||
<> | ||
|
||
{/* Set up the page header and navbar */} | ||
<HtmlHeader currentPage={pageName}/> | ||
<Navbar currentPage={pageName}/> | ||
|
||
|
||
{/* Header for the page */} | ||
<Section> | ||
<PageHeader size={"small"}> | ||
<div className={styles.welcomeContainer}> | ||
<h1>Your Account</h1> | ||
</div> | ||
</PageHeader> | ||
</Section> | ||
|
||
{ !session ? | ||
<> | ||
<div className={globalStyles.gridCentre}> | ||
<button className={styles.signInButton} onClick={() => signIn()}><FontAwesomeIcon icon={faPerson}/> Sign in</button> | ||
</div> | ||
</> | ||
: | ||
<> | ||
{/* Users Information */} | ||
<Section autoPadding> | ||
<div className={globalStyles.gridCentre}> | ||
<div className={styles.accountContainer}> | ||
|
||
<div className={styles.lastLogin}> | ||
<p> Last Login: {userLastLogin} </p> | ||
</div> | ||
|
||
<div className={styles.mainInfo}> | ||
<div className={styles.accountImage}> | ||
<Image src={userImage ? userImage.replaceAll("s96-c", "s192-c") : "/media/images/logo.svg"} alt={userName ? userName : ""} fill={true}/> | ||
</div> | ||
<h1> {userName}</h1> | ||
<h2> {userEmail}</h2> | ||
<h2> {userRole}</h2> | ||
</div> | ||
|
||
<div className={statsStyles.statsRowContainer + " " + statsStyles.twoCols}> | ||
|
||
{/* Each stat is a div with the number central and the icon and value inline */} | ||
<div className={statsStyles.stat}> | ||
<h2> {userPlants}</h2> | ||
<FontAwesomeIcon icon={faSeedling} className={statsStyles.inline}/> | ||
<p className={statsStyles.inline}> Plants </p> | ||
</div> | ||
|
||
{/* Each stat is a div with the number central and the icon and value inline */} | ||
<div className={statsStyles.stat}> | ||
<h2> N/A </h2> | ||
<FontAwesomeIcon icon={faCamera} className={statsStyles.inline}/> | ||
<p className={statsStyles.inline}> Posts </p> | ||
</div> | ||
|
||
</div> | ||
</div> | ||
</div> | ||
|
||
</Section> | ||
|
||
{/* Users Plants */} | ||
<Section autoPadding> | ||
<DropdownSection title={"Plants Created"} open> | ||
<div><p>asd</p></div> | ||
</DropdownSection> | ||
</Section> | ||
|
||
{/* Users Posts */} | ||
<Section autoPadding> | ||
<DropdownSection title={"Posts"} open> | ||
<div><p>asd</p></div> | ||
</DropdownSection> | ||
</Section> | ||
|
||
{/* Users Api Keys */} | ||
<Section autoPadding> | ||
<DropdownSection title={"Api Keys"} open> | ||
<div><p>asd</p></div> | ||
</DropdownSection> | ||
</Section> | ||
|
||
{/* Actions */} | ||
<Section autoPadding> | ||
<div className={globalStyles.gridCentre}> | ||
|
||
<div className={styles.actionItem}> | ||
<button onClick={signOutUser} className={styles.signOutButton}>Sign Out</button> | ||
</div> | ||
|
||
<div className={styles.actionItem}> | ||
<button onClick={deleteAccount} className={styles.deleteAccountButton}>Delete Account</button> | ||
</div> | ||
</div> | ||
</Section> | ||
</> | ||
} | ||
{/* Footer */} | ||
<Section> | ||
<Footer/> | ||
</Section> | ||
</> | ||
|
||
) | ||
} |
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,51 @@ | ||
import {NextApiRequest, NextApiResponse} from 'next'; | ||
import {getClient, getTables} from "@/lib/databse"; | ||
import {GetOrigin} from "@/lib/api_tools"; | ||
import {getServerSession} from "next-auth"; | ||
import {authOptions} from "@/pages/api/auth/[...nextauth]"; | ||
|
||
export default async function handler( | ||
request: NextApiRequest, | ||
response: NextApiResponse, | ||
) { | ||
|
||
// Get the origin of the request | ||
const origin = GetOrigin(request); | ||
|
||
// Get the client | ||
const client = await getClient() | ||
|
||
// Get the tables | ||
const tables = getTables(); | ||
|
||
try { | ||
|
||
// Get the session | ||
const session = await getServerSession(request, response, authOptions) | ||
|
||
// If there is no session then return an error | ||
if(!session || !session.user) { | ||
return response.status(401).json({ error: 'User not logged in'}); | ||
} | ||
|
||
// Get the user details | ||
const user_email = session.user.email; | ||
const user_name = session.user.name; | ||
|
||
// Remove the user | ||
let query = `DELETE FROM users WHERE ${tables.user_email} = '${user_email}' AND ${tables.user_name} = '${user_name}'`; | ||
console.log(query); | ||
const removed = await client.query(query); | ||
|
||
// Return the user | ||
return response.status(200).json({removed: removed.affectedRows}); | ||
|
||
|
||
} catch (error) { | ||
console.log("Error"); | ||
console.log(error); | ||
|
||
// If there is an error, return the error | ||
return response.status(500).json({ error: 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
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
Oops, something went wrong.
9840ab9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
13-dgt-website – ./
13-dgt-website-maxtyson123.vercel.app
www.rongoa.stream
rongoa.stream
13-dgt-website-git-main-maxtyson123.vercel.app
13-dgt-website.vercel.app