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

Firebase Authentication -- Login + Sign Up Pages #38

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
43a7af2
firebase auth config
meganleongg Apr 5, 2024
289e6a5
login frontend
meganleongg Apr 8, 2024
3f929e0
login button styling + search icon
meganleongg Apr 8, 2024
117b5d9
sign up page + auth log in works now
meganleongg Apr 16, 2024
8d2b455
sign up requires email invite + formatted error messages
meganleongg Apr 21, 2024
8dc59d1
can add admins from website
meganleongg Apr 30, 2024
c66638f
adjustable for smaller screen sizes
meganleongg May 7, 2024
7bc712d
merge
meganleongg May 7, 2024
11df264
merge
meganleongg May 7, 2024
bb7ca49
added persisting login logic to remember user
meganleongg May 7, 2024
9876b35
package
meganleongg May 7, 2024
e256843
Merge branch 'main' of https://github.com/TritonSE/DFM-Sideline-Sidek…
meganleongg May 7, 2024
96f6d81
added nav bars to layout
meganleongg May 7, 2024
3139938
added nav bars to layout
meganleongg May 7, 2024
b328044
added nav bars to layout
meganleongg May 7, 2024
9f68712
added nav bars to layout
meganleongg May 7, 2024
69ca44b
commented out nav bars
meganleongg May 15, 2024
c0de51d
add a new admin pop up form
meganleongg May 15, 2024
ded7323
Merge branch 'main' into login
Anthonyp0329 May 17, 2024
3ef926d
linting fixes
Anthonyp0329 May 17, 2024
49ca384
admin cards view, connected to firebase
meganleongg May 20, 2024
49dbc36
Merge branch 'login' of https://github.com/TritonSE/DFM-Sideline-Side…
meganleongg May 20, 2024
c3483e7
forgot password page
meganleongg May 28, 2024
f2a4087
merging main
Anthonyp0329 Jun 4, 2024
c50601b
merging main pt2
Anthonyp0329 Jun 4, 2024
ba74db1
finished designs for forgot password frontend
meganleongg Jun 4, 2024
3c8a03c
Merge branch 'main' into login
Anthonyp0329 Jun 4, 2024
081caab
package.json fixes
Anthonyp0329 Jun 4, 2024
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
1,541 changes: 1,516 additions & 25 deletions admin-portal-frontend/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions admin-portal-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"lint-check": "npm run check-git-hooks && eslint --cache --report-unused-disable-directives . && prettier --check ."
},
"dependencies": {
"firebase": "^10.10.0",
"next": "14.1.4",
"react": "^18",
"react-dom": "^18"
Expand Down
Binary file removed admin-portal-frontend/src/app/favicon.ico
Binary file not shown.
18 changes: 18 additions & 0 deletions admin-portal-frontend/src/app/firebase-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";

const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

export { auth };


13 changes: 2 additions & 11 deletions admin-portal-frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,13 @@

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-start-rgb: 229, 239, 245;
--background-end-rgb: 255, 255, 255;
}

@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}

body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb)))
rgb(var(--background-start-rgb));
background-color: rgb(var(--background-start-rgb));
}

@layer utilities {
Expand Down
14 changes: 14 additions & 0 deletions admin-portal-frontend/src/app/home/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import InviteAdmin from '../invite-admin';


export default function Home() {


return (
<><div className="flex items-center justify-center mb-2">
Success! Logged in!
</div><InviteAdmin /></>

);
}
58 changes: 58 additions & 0 deletions admin-portal-frontend/src/app/invite-admin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client";

import React, { useState } from 'react';
import { doc, setDoc, getFirestore } from 'firebase/firestore';

const InviteAdmin = () => {
const [email, setEmail] = useState('');
const [status, setStatus] = useState('');
const db = getFirestore();

const handleInvite = async (e: any) => {
e.preventDefault();
const inviteRef = doc(db, 'invitations', email);

try {
await setDoc(inviteRef, { invited: true });
setStatus('Invitation sent successfully.');
setEmail('');
} catch (error: any) {
setStatus(`Failed to send invitation: ${error.message}`);
}
};



return (
<div className="flex items-center justify-center">
<form onSubmit={handleInvite} className="flex flex-col items-center">
<input
className="mb-2 p-2 border w-full rounded border-gray-400"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email to invite"
required
/>
<button
type="submit"
style={{
backgroundColor: '#00629B',
color: 'white',
border: 'none',
borderRadius: '4px',
padding: '7px 14px',
cursor: 'pointer',
}}
className="hover:bg-blue-700 transition-colors"
>
Add Admin
</button>
</form>
{status && <p>{status}</p>}
</div>
);

};

export default InviteAdmin;
38 changes: 23 additions & 15 deletions admin-portal-frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
import { Inter } from "next/font/google";

import type { Metadata } from "next";
import "./globals.css";
"use client";

const inter = Inter({ subsets: ["latin"] });
import './globals.css'
import { Roboto } from 'next/font/google'
// import Navbar from './components/Navbar.js';
// import Footer from './components/Footer.js';
import React, { ReactNode } from 'react';

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
const roboto = Roboto({
subsets: ['latin'],
weight: ['400', '700']
})

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {



export default function RootLayout({ children }: { children: ReactNode }) {
return (

<html lang="en">
<body className={inter.className}>{children}</body>
<body className={roboto.className} >
{/* <Navbar /> */}
<main className="mt-16 flex min-h-screen flex-col antialiased">
{children}
</main>
{/* <Footer /> */}
</body>
</html>
);
)
}
199 changes: 96 additions & 103 deletions admin-portal-frontend/src/app/page.tsx
meganleongg marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,113 +1,106 @@
import Image from "next/image";
"use client";

import React, { useState, ChangeEvent, FormEvent, useEffect } from 'react';
import Image from 'next/image'
import { auth } from './firebase-config';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { useRouter } from 'next/navigation';
import search from './symbols/search.png'

interface LoginForm {
email: string;
password: string;
}

export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
Get started by editing&nbsp;
<code className="font-mono font-bold">src/app/page.tsx</code>
</p>
<div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
<a
className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
By{" "}
<Image
src="/vercel.svg"
alt="Vercel Logo"
className="dark:invert"
width={100}
height={24}
priority
/>
</a>
</div>
</div>
const [loginForm, setLoginForm] = useState<LoginForm>({ email: '', password: '' });
const [isFormFilled, setIsFormFilled] = useState(false);
const [error, setError] = useState<string>('');
const router = useRouter();

<div className="relative flex place-items-center before:absolute before:h-[300px] before:w-full sm:before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-full sm:after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
<Image
className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
</div>
const buttonStyle = loginForm.email && loginForm.password
? { backgroundColor: '#00629B' }
: { backgroundColor: '#6C6C6C', cursor: 'not-allowed' };

<div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className={`mb-3 text-2xl font-semibold`}>
Docs{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
-&gt;
</span>
</h2>
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
Find in-depth information about Next.js features and API.
</p>
</a>
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setLoginForm({ ...loginForm, [name]: value });
};

<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className={`mb-3 text-2xl font-semibold`}>
Learn{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
-&gt;
</span>
</h2>
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
Learn about Next.js in an interactive course with&nbsp;quizzes!
</p>
</a>
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const { email, password } = loginForm;

<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className={`mb-3 text-2xl font-semibold`}>
Templates{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
-&gt;
</span>
</h2>
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
Explore starter templates for Next.js.
</p>
</a>
try {
await signInWithEmailAndPassword(auth, email, password);
router.push('/home');
} catch (err: any) {
setError("The email or password is incorrect.");
}
};

useEffect(() => {
document.body.style.overflow = 'hidden';
const formFilled = loginForm.email.trim() !== '' && loginForm.password.trim() !== '';
setIsFormFilled(formFilled);

return () => {
document.body.style.overflow = 'visible';
};
}, [loginForm.email, loginForm.password]);

<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
target="_blank"
rel="noopener noreferrer"
>
<h2 className={`mb-3 text-2xl font-semibold`}>
Deploy{" "}
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
-&gt;
</span>
</h2>
<p className={`m-0 max-w-[30ch] text-sm opacity-50 text-balance`}>
Instantly deploy your Next.js site to a shareable URL with Vercel.
</p>
</a>
return (
<div className="flex items-center justify-center">
<div className="w-2/6 bg-white shadow-xl rounded-lg p-10 pt-8 flex flex-col items-center mt-24">
<Image src={search} alt="" width={60} height={60}/>
<h2 className="text-blue-950 text-xl font-bold my-4">Sideline Sidekick Admin Mode</h2>
<form onSubmit={handleSubmit} className="w-full">
<div className="mb-2">
<label htmlFor="email" className="block text-sm font-medium mb-1">Username</label>
<input
className="mb-4 p-2 border w-full rounded border-black border-opacity-40 "
type="text"
name="email"
id="email"
placeholder="Enter username or email"
onChange={handleInputChange}
/>
</div>
<div className="mb-2">
<label htmlFor="password" className="block text-sm font-medium mb-1">Password</label>
<input
className="mb-4 p-2 border w-full rounded border-black border-opacity-40"
type="password"
name="password"
id="password"
placeholder="Enter password"
onChange={handleInputChange}
/>
</div>
<div className="mb-6 flex items-center">
<input id="remember-me" type="checkbox" className="mr-2 h-4 w-4 accent-blue border-2 border-blue-900" name="rememberMe" />
<label htmlFor="remember-me" className="text-sm">
Remember me
</label>
</div>
<div className="mb-4">
<button
type="submit"
style={buttonStyle}
className={`p-2 w-full text-white font-bold rounded ${loginForm.email && loginForm.password ? 'hover:bg-blue-700' : ''}`}
disabled={!loginForm.email || !loginForm.password}>
Log in
</button>
{error && <p className="text-red-600 text-sm">{error}</p>}
</div>
</form>
<a className="text-sm self-start text-sky-700" href="/forgot-password">I forgot my username or password</a>
<div className="mt-1 self-start text-sm">
<span>Don't have an account?</span>
<a className="text-sky-700" href="/signup"> Create a new account.</a>
</div>
</div>
</main>
</div>

);
}
Loading
Loading