Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
be83e14
pages folder
clee3010 Mar 17, 2026
c8ba89b
added folders for project structure
ria29973 Mar 17, 2026
5078d6b
move src into webapp
vynguyen-26 Mar 17, 2026
cb36c02
Merge pull request #1 from BoG-Developer-Bootcamp-Spring-26/move_file
vynguyen-26 Mar 31, 2026
933a5dc
move api folder into pages
vynguyen-26 Mar 31, 2026
896b575
Merge branch 'BoG-Developer-Bootcamp-Spring-26:main' into main
vynguyen-26 Apr 2, 2026
8e82136
Mongodb connection
vynguyen-26 Apr 2, 2026
3d0ff95
move api folder into pages
vynguyen-26 Mar 31, 2026
ea98e75
Merge pull request #2 from BoG-Developer-Bootcamp-Spring-26/vivian
ria29973 Apr 4, 2026
49a3c1f
Merge branch 'BoG-Developer-Bootcamp-Spring-26:main' into ciana
clee3010 Apr 5, 2026
87335e6
file changes
clee3010 Apr 5, 2026
59423c9
Merge branch 'ciana' of https://github.com/BoG-Developer-Bootcamp-Spr…
clee3010 Apr 5, 2026
ea07995
Initialize _app.tsx with global styles and App component
clee3010 Apr 5, 2026
a50aef4
Add custom Document component for Next.js
clee3010 Apr 5, 2026
1369efd
Delete webapp/src/app/pages/.gitkeep
clee3010 Apr 5, 2026
62e7184
Delete webapp/src/app/layout.tsx
clee3010 Apr 5, 2026
6f8d36c
fixed src folder file structure
clee3010 Apr 5, 2026
a14fc6d
create login page
clee3010 Apr 5, 2026
0dd6056
Merge branch 'ciana' of https://github.com/BoG-Developer-Bootcamp-Spr…
clee3010 Apr 5, 2026
97f482f
Delete .gitkeep of wrong folders
clee3010 Apr 5, 2026
cab76f4
removed "Webapp" folder
clee3010 Apr 5, 2026
2b5aec0
Merge branch 'main' into ciana
clee3010 Apr 5, 2026
a273db9
Create login page layout
clee3010 Apr 5, 2026
9019834
Merge pull request #4 from BoG-Developer-Bootcamp-Spring-26/ciana
clee3010 Apr 6, 2026
1cd3129
completed backend for login/signup pages
ria29973 Apr 6, 2026
49e4040
Merge pull request #5 from BoG-Developer-Bootcamp-Spring-26/ria
ria29973 Apr 6, 2026
b1add17
Merge remote-tracking branch 'origin/main' into vivian
vynguyen-26 Apr 6, 2026
27ffb7d
Create sidebar & Relinked to Training Logs after login/signup
clee3010 Apr 7, 2026
2276ce1
Style sidebar
clee3010 Apr 7, 2026
35ecba5
Merge pull request #6 from BoG-Developer-Bootcamp-Spring-26/ciana
clee3010 Apr 7, 2026
6db561b
implement enpoints for create and get animals api and animal dasboard.
vynguyen-26 Apr 7, 2026
e3ebd43
Merge pull request #7 from BoG-Developer-Bootcamp-Spring-26/vivian
vynguyen-26 Apr 7, 2026
a81a08d
fixed side bar and added components
ria29973 Apr 8, 2026
9ef6af4
Merge pull request #8 from BoG-Developer-Bootcamp-Spring-26/ria
ria29973 Apr 8, 2026
97b26d9
small change in logout
ria29973 Apr 8, 2026
e89e927
Merge pull request #9 from BoG-Developer-Bootcamp-Spring-26/ria
ria29973 Apr 8, 2026
de88d23
Fixed sidebar format issue
clee3010 Apr 8, 2026
d63e00e
Merge pull request #10 from BoG-Developer-Bootcamp-Spring-26/ciana
clee3010 Apr 8, 2026
f4c69a5
Created training logs card
clee3010 Apr 9, 2026
02a25f6
Merge pull request #11 from BoG-Developer-Bootcamp-Spring-26/ciana
clee3010 Apr 9, 2026
b3e8181
Continue training logs
clee3010 Apr 14, 2026
fbbfb5f
Create new animal and display in new animal in animal dashboard
vynguyen-26 Apr 14, 2026
847eff6
Merge branch 'main' into vivian
vynguyen-26 Apr 14, 2026
b3e4662
Merge pull request #12 from BoG-Developer-Bootcamp-Spring-26/vivian
vynguyen-26 Apr 14, 2026
46d2ab9
completed all users page
ria29973 Apr 15, 2026
0813342
Merge pull request #13 from BoG-Developer-Bootcamp-Spring-26/ria
ria29973 Apr 15, 2026
c1771d2
Merge remote-tracking branch 'origin/main' into ciana
clee3010 Apr 15, 2026
a90624f
Fixed training log
clee3010 Apr 15, 2026
fb31528
fixed logout
ria29973 Apr 15, 2026
2139d6e
Merge pull request #14 from BoG-Developer-Bootcamp-Spring-26/ria
ria29973 Apr 15, 2026
b9474fd
side bar so up yay
vynguyen-26 Apr 15, 2026
0feca90
Merge branch 'main' into vivian
vynguyen-26 Apr 15, 2026
3e44ac8
Merge pull request #15 from BoG-Developer-Bootcamp-Spring-26/vivian
vynguyen-26 Apr 15, 2026
a37c35b
All animals and responsive design
vynguyen-26 Apr 16, 2026
c08dd11
Merge pull request #16 from BoG-Developer-Bootcamp-Spring-26/vivian
vynguyen-26 Apr 16, 2026
659feec
minor fix for side bar
vynguyen-26 Apr 16, 2026
e7271d6
Merge pull request #17 from BoG-Developer-Bootcamp-Spring-26/vivian
vynguyen-26 Apr 16, 2026
8c277d7
Merge branch 'main' into ciana
clee3010 Apr 16, 2026
f24c96c
Merge pull request #18 from BoG-Developer-Bootcamp-Spring-26/ciana
clee3010 Apr 16, 2026
323ee19
implement search bar
vynguyen-26 Apr 16, 2026
0e56c02
Merge branch 'main' into vivian
vynguyen-26 Apr 16, 2026
acb1f55
fix training log problem, responsive design and search bar functionality
vynguyen-26 Apr 16, 2026
7842d26
Merge pull request #19 from BoG-Developer-Bootcamp-Spring-26/vivian
vynguyen-26 Apr 16, 2026
c6399be
Add animal and user models
vynguyen-26 Apr 16, 2026
ce8e9dc
Merge pull request #20 from BoG-Developer-Bootcamp-Spring-26/vivian
vynguyen-26 Apr 16, 2026
b0649fa
ui changes to login and signup
ria29973 Apr 16, 2026
448a079
Merge pull request #21 from BoG-Developer-Bootcamp-Spring-26/ria
ria29973 Apr 16, 2026
10efb29
use auth context for user login and signup
vynguyen-26 Apr 16, 2026
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default function Page() {
## Backend Specifications

- We will be incorporating all we have learned thus far such as API endpoints and database querying!
- Create all endpoints in `src/app/api`
- Create all endpoints in `src/pages/api`
- Reminder that routes are created by using a `route.ts` file

### Create Operations
Expand Down
2 changes: 2 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
experimental: {},
reactStrictMode: true,
};

export default nextConfig;
968 changes: 698 additions & 270 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 10 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@
"start": "next start"
},
"dependencies": {
"argon2": "^0.44.0",
"bcrypt": "^6.0.0",
"bcryptjs": "^3.0.3",
"mongoose": "^9.3.3",
"next": "16.0.1",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.1"
"react-dom": "19.2.0"
},
"devDependencies": {
"typescript": "^5",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4"
"baseline-browser-mapping": "^2.10.13",
"tailwindcss": "^4",
"typescript": "^5"
}
}
Binary file added public/images/_.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added server/mongodb/.gitkeep
Empty file.
10 changes: 10 additions & 0 deletions server/mongodb/connectDb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import mongoose from "mongoose";

export default async function connectDb() {
try {
await mongoose.connect(process.env.MONGODB_URL!);
console.log("Connected to MongoDB!");
} catch (e) {
console.log("Unable to connect", e);
}
}
7 changes: 7 additions & 0 deletions server/mongodb/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// this will prevent our MongoDB from running at every instance
import connectDb from "./connectDb";

// register executes the connectDb once immediately when we run the server
export function register(){
connectDb();
}
13 changes: 13 additions & 0 deletions server/mongodb/models/Animal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import mongoose from "mongoose";

const AnimalSchema = new mongoose.Schema({
name: { type: String, required: true },
breed: { type: String, required: true },
owner: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
hoursTrained: { type: Number, default: 0 },
profilePicture: { type: String }
});

// Prevent model overwrite error in Next.js
const Animal = mongoose.models.Animal || mongoose.model("Animal", AnimalSchema);
export default Animal;
35 changes: 35 additions & 0 deletions server/mongodb/models/Training-Log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import mongoose from "mongoose";

const TrainingLogSchema = new mongoose.Schema(
{
title : {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
hours: {
type: Number,
required: true,
},
date: {
type: Date,
required: true,
},
animalId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Animal",
required: true,
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
},
{ timestamps: true }
);

export default mongoose.models.TrainingLog || mongoose.model("TrainingLog", TrainingLogSchema);
11 changes: 11 additions & 0 deletions server/mongodb/models/User.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mongoose from "mongoose";

const UserSchema = new mongoose.Schema({
fullName : {type: String, required: true},
email: { type: String, required: true},
password: {type : String, required: true},
isAdmin: {type: Boolean, default: false}
});

// Prevent model overwrite error in Next.js
export default mongoose.models.User || mongoose.model("User", UserSchema);
Empty file.
Empty file added server/mongodb/models/index.js
Empty file.
Empty file added server/utils/.gitkeep
Empty file.
63 changes: 63 additions & 0 deletions src/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { createContext, useContext, useState, useEffect, ReactNode } from "react";

type User = {
id: string;
isAdmin: boolean;
fullName: string,
} | null;


type AuthContextType = {
user: User;
login: (data: { userid: string; isAdmin: boolean, fullName: string }) => void;
logout: () => void;
};


const AuthContext = createContext<AuthContextType | undefined>(undefined);


type AuthProviderProps = {
children: ReactNode;
};

export function AuthProvider({ children }: AuthProviderProps) {
const [user, setUser] = useState<User>(null);


useEffect(() => {
const stored = localStorage.getItem("user");
if (stored) setUser(JSON.parse(stored));
}, []);

const login = (data: { userid: string; isAdmin: boolean, fullName: string }) => {
const newUser = {
id: data.userid,
isAdmin: data.isAdmin,
fullName: data.fullName,
};

setUser(newUser);
localStorage.setItem("user", JSON.stringify(newUser));
};

const logout = () => {
setUser(null);
localStorage.removeItem("user");
};

return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}


export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
}
48 changes: 48 additions & 0 deletions src/component/animal_card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Image from "next/image";
import { Animal } from "../types/animal";

interface AnimalCardProps {
animal: Animal;
compact?: boolean;
}

export const AnimalCard = ({ animal, compact = false }: AnimalCardProps) => {
return (
<div className="w-full overflow-hidden rounded-xl bg-white shadow-sm transition-all group hover:shadow-md">
{/* Image Container */}
<div className={`relative w-full overflow-hidden rounded-t-[20px] bg-gray-100 ${compact ? "h-40 lg:h-44" : "h-56 sm:h-60 lg:h-64"}`}>
<Image
src={animal.profilePicture}
alt={`${animal.name} - ${animal.breed}`}
fill className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>

{/* Info Section */}
<div className={`flex items-start ${compact ? "gap-3 px-4 py-4" : "gap-4 px-5 py-5 lg:gap-6 lg:px-6 lg:py-6"}`}>
{/* Owner Avater */}
<div className={`flex flex-shrink-0 items-center justify-center rounded-full bg-[#d13a2f] font-bold text-white ${compact ? "h-10 w-10 text-lg" : "h-12 w-12 text-xl lg:h-[50px] lg:w-[50px] lg:text-[24px]"}`}>
{/* Owner Name's first character */}
{animal.owner.fullName.charAt(0)}
</div>

<div className="flex flex-col min-w-0">
<h3
className={`font-heebo leading-tight text-gray-900 ${compact ? "truncate text-xl" : "truncate text-2xl lg:text-[27px]"}`}
title={`${animal.name} - ${animal.breed}`}
>
<span className="font-bold tracking-tight">
{animal.name}
</span>
<span className="font-bold">
{` - ${animal.breed}`}
</span>
</h3>
<p className={`font-heebo font-medium leading-[1.2] text-gray-600 ${compact ? "mt-2 text-sm" : "mt-3 text-base lg:mt-[12px] lg:text-[18px]"} truncate`}>
{animal.owner.fullName} • Trained: {animal.hoursTrained} hours
</p>
</div>
</div>
</div>
);
}
11 changes: 11 additions & 0 deletions src/component/dashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import { useRouter } from "next/router";
import Sidebar from "./sidebar";

export default function Dashboard() {
return (
<Sidebar/>
)
}
45 changes: 45 additions & 0 deletions src/component/progressbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
type ProgressBarProps = {
searchValue?: string;
onSearchChange?: (value: string) => void;
showSearch?: boolean;
};

export default function ProgressBar({
searchValue = "",
onSearchChange,
showSearch = true,
}: ProgressBarProps) {
return (
<header className="flex items-center justify-between border-b border-gray-300 px-4 py-3 shadow-md sm:px-6 sm:py-4 lg:px-8 lg:py-4">
<div className="flex w-[240px] shrink-0 items-center gap-3">
<img src="/images/appLogo.png" alt="App logo" className="h-9 w-auto sm:h-10" />
<h1 className="text-2xl font-medium text-black font-oswald sm:text-3xl lg:text-4xl">
Progress
</h1>
</div>

{showSearch ? (
<div className="flex flex-1 justify-center px-4">
<div className="relative h-[48px] w-full max-w-[550px]">
<img
src="/images/searchLogo.png"
alt="Search logo"
className="pointer-events-none absolute left-5 top-1/2 h-5 w-5 -translate-y-1/2"
/>
<input
type="text"
placeholder="Search"
value={searchValue}
onChange={(e) => onSearchChange?.(e.target.value)}
className="h-full w-full rounded-[15px] border-[1.5px] border-gray-300 bg-white pl-14 pr-4 font-heebo text-lg text-gray-700 outline-none placeholder:text-gray-400 focus:border-gray-400"
/>
</div>
</div>
) : (
<div className="flex-1" />
)}

<div className="flex w-[200px] shrink-0 justify-end" />
</header>
);
}
Loading