Skip to content
Open
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
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
REACT_APP_FIREBASE_API_KEY=
REACT_APP_FIREBASE_AUTH_DOMAIN=
REACT_APP_FIREBASE_PROJECT_ID=
REACT_APP_FIREBASE_STORAGE_BUCKET=
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=
REACT_APP_FIREBASE_APP_ID=
16,261 changes: 2,135 additions & 14,126 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@mantine/hooks": "^6.0.1",
"@mantine/notifications": "^6.0.1",
"@mantine/nprogress": "^6.0.1",
"@tabler/icons-react": "^2.11.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
Expand All @@ -23,10 +24,12 @@
"firebaseui": "^0.600.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.9.0",
"react-scripts": "5.0.1",
"sass": "^1.59.2",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"zod": "^3.21.4"
},
"scripts": {
"start": "react-scripts start",
Expand Down
101 changes: 98 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,109 @@
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Login from "pages/Login";
import Signup from "pages/Signup";
import { MantineProvider } from "@mantine/core";
import { Notifications } from "@mantine/notifications";
import { HOME_PATH, LOGIN_PATH, SIGNUP_PATH } from "routes/constants";
import { AuthProvider } from "context/AuthContext";
import { getUserData } from "api/profile";

import "bootstrap/dist/css/bootstrap.min.css";
import RFPHistoryPage from 'pages/RFPHistory';
function App() {
return (
<MantineProvider withNormalizeCSS withGlobalStyles>
<AuthProvider>
<Notifications />
<BrowserRouter>
<Routes>
<Route path={HOME_PATH} element={<></>} />
<Route path={LOGIN_PATH} element={<Login />} />
<Route path={SIGNUP_PATH} element={<Signup />} />
</Routes>
</BrowserRouter>
</AuthProvider>
</MantineProvider>
);
}

export default App;

/*
import logo from "./logo.svg";
import "./App.css";
import { useEffect, useState } from "react";
import { db } from "./firebase/config";
import { doc, getDoc } from "@firebase/firestore";
import { login, signOut, signUp } from "api/auth";
import { updateUserData } from "api/profile";

function App() {
const [user, setUser] = useState<any>(null);
useEffect(() => {
(async () => {
console.log("test");
const docSnap = await getDoc(doc(db, "email", "jcsMut0LAVFz6yKcliHq"));
const user = await docSnap.data();
setUser(user);
console.log(user);
})();
}, []);

const buttonLogin = async () => {
console.log("logging in...");
const userCredential = await login({
email: "[email protected]",
password: "lala1234",
});
console.log("done");
};

const buttonSignup = async () => {
console.log("Signing up...");
const userCredential = await signUp({
name: "oh noo",
email: "[email protected]",
password: "lala1234",
subcomms: "Tech",
contactNum: "???",
});
console.log("Done");
};

const buttonSignout = async () => {
console.log("signing out...");
await signOut();
console.log("done");
};

const onUpdate = async () => {
console.log("updating...");
updateUserData({
profile: {
name: "ohlala",
},
});
console.log("done");
};

return (
<div className="App">
<RFPHistoryPage />
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
<button onClick={buttonLogin}>Log me in</button>
<button onClick={buttonSignup}>Sign me up</button>
<button onClick={buttonSignout}>Log me out</button>
<button onClick={onUpdate}>Update</button>
</header>
</div>
);
}

export default App;
*/
47 changes: 47 additions & 0 deletions src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { auth } from "firebase/config";
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut as firebaseSignOut,
} from "firebase/auth";
import { createUser } from "./profile";

type SignUpRequest = {
name: string;
email: string;
password: string;
subcomms: string;
contactNum: string;
};
export async function signUp(params: SignUpRequest) {
const { name, email, password, subcomms, contactNum } = params;

const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);

await createUser({
credential: userCredential,
profile: {
name,
email,
subcomms,
contactNum,
},
});
}

type LoginRequest = {
email: string;
password: string;
};
export async function login(param: LoginRequest) {
const { email, password } = param;
return signInWithEmailAndPassword(auth, email, password);
}

export async function signOut() {
await firebaseSignOut(auth);
}
50 changes: 50 additions & 0 deletions src/api/profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { UserCredential } from "firebase/auth";
import { auth, db } from "firebase/config";
import { doc, setDoc, updateDoc, getDoc } from "@firebase/firestore";
import { UserProfile } from "types/profile";

const USER_PATH = "users";

export type CreateUserRequest = {
credential: UserCredential;
profile: UserProfile;
};
export async function createUser(params: CreateUserRequest) {
const { credential, profile } = params;
const uid = credential.user.uid;

await setDoc(doc(db, USER_PATH, uid), profile);
}

export type UpdateUserRequest = {
profile: Partial<Omit<UserProfile, "user" | "email">>;
};
export async function updateUserData(params: UpdateUserRequest) {
const { profile } = params;

if (!auth.currentUser) {
console.error("User needs to log in");
return;
}

const uid = auth.currentUser.uid;

await updateDoc(doc(db, USER_PATH, uid), profile);
}

export async function getUserData(): Promise<UserProfile> {
if (!auth.currentUser) {
throw new Error("User needs to log in");
}

const uid = auth.currentUser.uid;

const docSnap = await getDoc(doc(db, USER_PATH, uid));

if (!docSnap.exists()) {
throw new Error("User does not exist");
}

const data = docSnap.data() as UserProfile;
return data;
}
65 changes: 65 additions & 0 deletions src/components/auth/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from "react";
import { useForm, zodResolver } from "@mantine/form";
import {
Button,
Group,
PasswordInput,
Stack,
TextInput,
Title,
} from "@mantine/core";
import { z } from "zod";

const schema = z.object({
email: z.string().email(),
password: z.string().min(6),
});

export type LoginFields = z.infer<typeof schema>;

type Props = {
onSubmit: (f: LoginFields) => void;
isLoading: boolean;
};

const Login = (props: Props) => {
const { onSubmit, isLoading } = props;

const form = useForm<LoginFields>({
initialValues: {
email: "",
password: "",
},

validate: zodResolver(schema),
});

return (
<Stack m="md" w="50%">
<Title order={1}>Welcome,</Title>
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
<TextInput
withAsterisk
label="Email"
placeholder="[email protected]"
{...form.getInputProps("email")}
/>
<PasswordInput
withAsterisk
label="Password"
placeholder="Password"
mt="md"
{...form.getInputProps("password")}
/>

<Group mt="lg" position="right">
<Button type="submit" loading={isLoading}>
Login
</Button>
</Group>
</form>
</Stack>
);
};

export default Login;
Loading