Skip to content

Commit

Permalink
Differentiated consumer and business account
Browse files Browse the repository at this point in the history
  • Loading branch information
TPH777 committed Jul 9, 2024
1 parent d559910 commit 2f873ee
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 76 deletions.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LoginPage } from "./pages/Login";
import { Layout } from "./Layout";
import { RegisterPage } from "./pages/Register";
import { Dashboard } from "./pages/Dashboard";
import { FavoritePage } from "./pages/Favorites";

function App() {
return (
Expand All @@ -15,6 +16,7 @@ function App() {
<Route path="/login" element={<LoginPage />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/register" element={<RegisterPage />} />
<Route path="/favorites" element={<FavoritePage />} />
</Route>
</Routes>
</Router>
Expand Down
16 changes: 13 additions & 3 deletions src/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { User, onAuthStateChanged, signOut } from "firebase/auth";
import { useNavigate } from "react-router-dom";
import { auth } from "../config/firebase";
import { auth, db } from "../config/firebase";
import { useEffect, useState } from "react";
import { Button, Container, Nav, Navbar } from "react-bootstrap";
import { doc } from "firebase/firestore";

export function NavBar() {
let navigate = useNavigate();

const [user, setUser] = useState<User>();
const [isConsumer, setIsConsumer] = useState<boolean>(false);

const getUser = async () => {
onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
if (doc(db, "consumer", user.uid)) {
// Consumer account
setIsConsumer(true);
}
}
});
};
Expand Down Expand Up @@ -45,7 +51,11 @@ export function NavBar() {
{user ? (
<>
<Nav className="me-auto">
<Nav.Link href="#dashboard">Dashboard</Nav.Link>
{isConsumer ? (
<Nav.Link href="#favorites">Favorites</Nav.Link>
) : (
<Nav.Link href="#dashboard">Dashboard</Nav.Link>
)}
</Nav>
<Navbar.Text onClick={Logout} style={{ cursor: "pointer" }}>
Logout
Expand Down
10 changes: 10 additions & 0 deletions src/functions/GetError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const getErrorMessage = (error: any) => {
switch (error.code) {
case "auth/invalid-credential":
return "Invalid credentials. Please check your email and password.";
case "auth/invalid-email":
return "Invalid email address.";
default:
return error.message;
}
};
2 changes: 1 addition & 1 deletion src/functions/Get.tsx → src/functions/GetFood.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { collection, getDocs, writeBatch } from "firebase/firestore";
import { db } from "../config/firebase";
import { FoodItem } from "../interface/FoodItem";
import { timestampToDate } from "../functions/Date";
import { timestampToDate } from "./Date";

export const getFoodList = async () => {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { deleteObject, getStorage, ref } from "firebase/storage";
import { Search } from "../components/Search";
import { FoodItem } from "../interface/FoodItem";
import { deleteSuccess, deleteWarning } from "../functions/Alert";
import { getFoodList } from "../functions/Get";
import { getFoodList } from "../functions/GetFood";
import { Spinner } from "react-bootstrap";

export function Dashboard() {
Expand Down
126 changes: 126 additions & 0 deletions src/pages/Favorites.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { useEffect, useState } from "react";
import Card from "react-bootstrap/Card";
import { Badge, Button, Col, Row, Spinner } from "react-bootstrap";
import { Search } from "../components/Search";
import { FoodItem } from "../interface/FoodItem";
import { timestampToString } from "../functions/Date";
import { getFoodList } from "../functions/GetFood";

export function FavoritePage() {
const [foodList, setFoodList] = useState<FoodItem[]>([]);
const [search, setSearch] = useState<string>("");
const [cuisine, setCuisine] = useState<string>("~Cuisine~");
const [sort, setSort] = useState<string>("~Sort~");
const [business, setBusiness] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);

const fetchFoodList = async () => {
try {
setIsLoading(true);
const updatedFoodList = await getFoodList();
const postedFoodList = updatedFoodList.filter((food) => {
// Display posted food items only
return food.post === true;
});
setFoodList(postedFoodList);
setIsLoading(false);
} catch (error) {
console.error("Error fetching food items:", error);
}
};

useEffect(() => {
fetchFoodList();
}, []);

const searchFoodList = foodList.filter((food) => {
const nameMatches = food.name.toLowerCase().includes(search.toLowerCase()); // Search Bar
const cuisineMatches = cuisine === "~Cuisine~" || food.cuisine === cuisine; // Filter
const businessMatches = business === "" || food.business === business; // Badges
return nameMatches && cuisineMatches && businessMatches;
});

searchFoodList.sort((a, b) => {
// Sort
if (sort === "Name") {
return a.name > b.name ? 1 : -1;
} else if (sort === "Price") {
return a.price > b.price ? 1 : -1;
} else if (sort === "Cuisine") {
return a.cuisine > b.cuisine ? 1 : -1;
} else {
return a.date > b.date ? 1 : -1; // Default By Date
}
});

return (
<>
<Search
search={search}
cuisine={cuisine}
sort={sort}
setSearch={setSearch}
setCuisine={setCuisine}
setSort={setSort}
/>

<Button
variant="secondary"
className="mb-4"
hidden={!business}
onClick={() => setBusiness("")}
>
Showing {business}'s results only, Click to Return
</Button>

{isLoading && (
<Spinner animation="border" role="status">
<span className="visually-hidden">Loading...</span>
</Spinner>
)}

{!isLoading && (
<Row md={4} className="g-4">
{searchFoodList.map((food, index) => (
<Col key={index}>
<Card className="flex" key={food.id}>
<Card.Img variant="top" src={food.imageURL} />
<Card.Body>
<Card.Title>{food.name}</Card.Title>
<Card.Subtitle>${food.price}</Card.Subtitle>
<Card.Text>
{food.date
? `Date: ${timestampToString(food.date)}`
: "No Date"}
</Card.Text>
<Badge
style={{ cursor: "pointer" }}
pill
className="ms-2"
bg="warning"
onClick={() => setCuisine(food.cuisine)}
>
{food.cuisine}
</Badge>
<Badge
style={{ cursor: "pointer" }}
pill
className="ms-2"
bg="dark"
onClick={() => setBusiness(food.business)}
>
{food.business}
</Badge>
</Card.Body>
</Card>
</Col>
))}
</Row>
)}

{!isLoading && searchFoodList.length == 0 && (
<h1 className="mt-3">No Results</h1>
)}
</>
);
}
2 changes: 1 addition & 1 deletion src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Badge, Button, Col, Row, Spinner } from "react-bootstrap";
import { Search } from "../components/Search";
import { FoodItem } from "../interface/FoodItem";
import { timestampToString } from "../functions/Date";
import { getFoodList } from "../functions/Get";
import { getFoodList } from "../functions/GetFood";

export function Home() {
const [foodList, setFoodList] = useState<FoodItem[]>([]);
Expand Down
92 changes: 36 additions & 56 deletions src/pages/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
import { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import ErrorText from "../components/ErrorText";
import { auth, googleProvider } from "../config/firebase";
import { auth, db, googleProvider } from "../config/firebase";
import { signInWithEmailAndPassword, signInWithPopup } from "firebase/auth";
import GoogleButton from "react-google-button";
import { ButtonGroup, ToggleButton } from "react-bootstrap";
import { getErrorMessage } from "../functions/GetError";
import { doc, setDoc } from "firebase/firestore";

export const LoginPage = () => {
const [authenticating, setAuthenticating] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [user, setUser] = useState<boolean>(true);
const [authenticating, setAuthenticating] = useState<boolean>(false);
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");
const [error, setError] = useState<string>("");

let navigate = useNavigate();

const defaultSignIn = async (e: any) => {
e.preventDefault();
setAuthenticating(true);

try {
await signInWithEmailAndPassword(auth, email, password);
navigate("/dashboard");
} catch (error) {
setAuthenticating(false);
setError(getErrorMessage(error));
Expand All @@ -30,25 +27,19 @@ export const LoginPage = () => {

const googleSignIn = async () => {
setAuthenticating(true);

try {
await signInWithPopup(auth, googleProvider);
navigate("/dashboard");
} catch (error: any) {
setAuthenticating(false);
setError(error.message);
}
};

const getErrorMessage = (error: any) => {
switch (error.code) {
case "auth/invalid-credential":
return "Invalid credentials. Please check your email and password.";
case "auth/invalid-email":
return "Invalid email address.";
default:
return error.message;
}
await signInWithPopup(auth, googleProvider)
.then(async (userCredential) => {
const user = userCredential.user;
if (!doc(db, "consumer", user.uid)) {
// Document don't exist (New user)
await setDoc(doc(db, "consumer", user.uid), {});
}
navigate("/");
})
.catch((error) => {
setAuthenticating(false);
setError(error.message);
});
};

return (
Expand Down Expand Up @@ -80,35 +71,28 @@ export const LoginPage = () => {
</div>

<div className="mb-4">
<ButtonGroup>
<ToggleButton
id="post-checked"
type="radio"
variant={"outline-success"}
value={1}
checked={user === true}
onChange={() => setUser(true)}
>
Consumer
</ToggleButton>
<ToggleButton
id="post-unchecked"
type="radio"
variant={"outline-dark"}
value={2}
checked={user === false}
onChange={() => setUser(false)}
>
Business
</ToggleButton>
</ButtonGroup>
<button
type="submit"
disabled={authenticating}
className="btn btn-primary ms-3"
className="btn btn-primary ms-5 me-3"
>
{authenticating ? "Signing In..." : "Sign In"}
</button>
|
<button
onClick={googleSignIn}
disabled={authenticating}
className="btn btn-dark ms-3"
>
<img
src="/pictures/google-logo.png"
alt="logo"
width="30"
height="30"
className="d-inline-block align-top me-2"
/>
{authenticating ? "Signing In..." : "Google Sign In (Consumer)"}
</button>
</div>

<small>
Expand All @@ -118,10 +102,6 @@ export const LoginPage = () => {
</small>

<ErrorText error={error} />

<div className="mt-3">
<GoogleButton onClick={googleSignIn} />
</div>
</form>
);
};
Loading

0 comments on commit 2f873ee

Please sign in to comment.