Skip to content

Commit

Permalink
adding more pages
Browse files Browse the repository at this point in the history
  • Loading branch information
prince rusweka committed May 2, 2025
1 parent 4fab635 commit a3f322e
Show file tree
Hide file tree
Showing 10 changed files with 444 additions and 2 deletions.
Binary file modified backend/__pycache__/utils.cpython-311.pyc
Binary file not shown.
51 changes: 51 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,58 @@ def add_pet():
except sqlite3.Error as e:
print(f"An error occurred: {e}")
return jsonify({"error": "Internal Server Error"}), 500


@app.route("/api/saved_pets", methods=["POST"])
def save_pet():
try:
data = request.get_json()
pet_id = data.get("pet_id")
if not pet_id:
return jsonify({"error": "Missing pet_id"}), 400

conn = get_db()
cursor = conn.cursor()
cursor.execute("INSERT INTO saved_pets (pet_id) VALUES (?)", (pet_id,))
conn.commit()
conn.close()
return jsonify({"message": "Pet saved successfully!"}), 201
except sqlite3.Error as e:
print(f"An error occurred: {e}")
return jsonify({"error": "Internal Server Error"}), 500


@app.route("/api/saved_pets", methods=["GET"])
def get_saved_pets():
try:
conn = get_db()
cursor = conn.cursor()
cursor.execute("""
SELECT animals.*
FROM animals
INNER JOIN saved_pets ON animals.id = saved_pets.pet_id
""")
pets = cursor.fetchall()
pet_list = [dict(pet) for pet in pets]
conn.close()
return jsonify(pet_list), 200
except sqlite3.Error as e:
print(f"An error occurred: {e}")
return jsonify({"error": "Internal Server Error"}), 500


@app.route("/api/saved_pets/<int:pet_id>", methods=["DELETE"])
def remove_saved_pet(pet_id):
try:
conn = get_db()
cursor = conn.cursor()
cursor.execute("DELETE FROM saved_pets WHERE pet_id = ?", (pet_id,))
conn.commit()
conn.close()
return jsonify({"message": "Pet removed from saved list"}), 200
except sqlite3.Error as e:
print(f"An error occurred: {e}")
return jsonify({"error": "Internal Server Error"}), 500

def before_first_request():
"""Run once before the first request to initialize the database."""
Expand Down
19 changes: 19 additions & 0 deletions backend/migrate_add_saved_pets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import sqlite3

# Connect to the existing database
conn = sqlite3.connect("animal_shelter.db")
cursor = conn.cursor()

# Create the saved_pets table if it doesn't exist
cursor.execute("""
CREATE TABLE IF NOT EXISTS saved_pets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pet_id INTEGER NOT NULL,
FOREIGN KEY(pet_id) REFERENCES animals(id)
)
""")

conn.commit()
conn.close()

print(" saved_pets table created (or already exists).")
8 changes: 8 additions & 0 deletions backend/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,12 @@ def create_tables(conn):
"""
)

cursor.execute("""
CREATE TABLE IF NOT EXISTS saved_pets (
id INTEGER PRIMARY KEY,
pet_id INTEGER NOT NULL,
FOREIGN KEY(pet_id) REFERENCES animals(id)
)
""")

conn.commit()
7 changes: 7 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import Search from './pages/Search';
import MoreInfo from "./pages/MoreInfo";
import PetProfile from './pages/PetProfile';
import Profile from './pages/Profile';
import PetDetails from "./pages/PetDetails";
import AdoptionPage from "./pages/AdoptionPage";
import MyList from './pages/MyList';


function App() {
return (
Expand All @@ -14,8 +18,11 @@ function App() {
<Route path="/" element={<Home />} />
<Route path="/search" element={<Search />} />
<Route path="/pets" element={<PetProfile />} />
<Route path="/pets/:id" element={<PetDetails />} />
<Route path="/profile" element={<Profile />} />
<Route path="/more-info" element={<MoreInfo />} />
<Route path="/adopt/:name" element={<AdoptionPage />} />
<Route path="/my-list" element={<MyList />} />
</Routes>
</>
);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default function NavBar() {
<Link to="/search" style={{ color: "white" }}><FaSearch /></Link>
<Link to="/profile" style={{ color: "white" }}><FaUser /></Link>
<Link to="/pets" style={{ color: "white" }}>Pet Profile</Link>
<Link to="/my-list" style={{ color: "white" }}>My List</Link>
</nav>
);
}
140 changes: 140 additions & 0 deletions frontend/src/pages/AdoptionPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { useState, CSSProperties } from "react";
import { useParams } from "react-router-dom";
import instagramLogo from "../assets/instagram_logo.png";
import facebookLogo from "../assets/facebook_logo.png";
import twitterLogo from "../assets/twitter_logo.png";
import youtubeLogo from "../assets/youtube_logo.png";
import menuLogo from "../assets/menu_logo.png";

export default function AdoptionForm() {
const { name } = useParams<{ name: string }>();
const [responses, setResponses] = useState({
care: "",
reason: "",
home: "",
});

return (
<div style={containerStyle}>
<div style={formWrapperStyle}>
<h2 style={formTitle}>Adoption Application Form for <em>{name}</em></h2>

<label style={labelStyle}>What will you do to care for <em>{name}?</em></label>
<input
style={inputStyle}
value={responses.care}
onChange={(e) => setResponses({ ...responses, care: e.target.value })}
/>

<label style={labelStyle}>Why do you want to adopt <em>{name}?</em></label>
<input
style={inputStyle}
value={responses.reason}
onChange={(e) => setResponses({ ...responses, reason: e.target.value })}
/>

<label style={labelStyle}>
Where will you raise <em>{name}?</em> (Rent/Own? Fenced yard? Other pets? Roommates?)
</label>
<textarea
style={textareaStyle}
value={responses.home}
onChange={(e) => setResponses({ ...responses, home: e.target.value })}
/>

<p style={smallTextStyle}>Please be specific and thorough when responding to these questions.</p>

<div style={{ textAlign: "right" as CSSProperties["textAlign"] }}>
<button style={submitButton}>Submit</button>
</div>
</div>

<footer style={footerStyle}>
<div style={footerLeftStyle}>
<img src={instagramLogo} alt="Instagram" style={iconStyle} />
<img src={facebookLogo} alt="Facebook" style={iconStyle} />
<img src={twitterLogo} alt="Twitter" style={iconStyle} />
<img src={youtubeLogo} alt="YouTube" style={iconStyle} />
</div>
<img src={menuLogo} alt="Menu" style={iconStyle} />
</footer>
</div>
);
}

// ========== Styles ==========

const containerStyle: CSSProperties = {
backgroundColor: "rgb(180, 164, 143)",
minHeight: "100vh",
display: "flex",
flexDirection: "column" as CSSProperties["flexDirection"],
justifyContent: "space-between",
};

const formWrapperStyle: CSSProperties = {
padding: "2rem",
maxWidth: "700px",
margin: "auto",
width: "100%",
display: "flex",
flexDirection: "column" as CSSProperties["flexDirection"],
gap: "1rem",
color: "white"
};

const formTitle: CSSProperties = {
textAlign: "center" as CSSProperties["textAlign"],
marginBottom: "1rem",
};

const labelStyle: CSSProperties = {
fontWeight: "bold",
};

const inputStyle: CSSProperties = {
padding: "0.75rem",
fontSize: "1rem",
borderRadius: "8px",
border: "none",
backgroundColor: "#e0d9d9",
width: "100%",
};

const textareaStyle: CSSProperties = {
...inputStyle,
height: "100px",
resize: "vertical" as CSSProperties["resize"],
};

const smallTextStyle: CSSProperties = {
fontSize: "0.85rem",
};

const submitButton: CSSProperties = {
padding: "0.75rem 1.5rem",
backgroundColor: "black",
color: "white",
border: "none",
borderRadius: "10px",
cursor: "pointer",
fontSize: "1rem",
};

const footerStyle: CSSProperties = {
backgroundColor: "black",
padding: "1rem",
display: "flex",
justifyContent: "space-between" as CSSProperties["justifyContent"],
alignItems: "center" as CSSProperties["alignItems"],
};

const footerLeftStyle: CSSProperties = {
display: "flex",
gap: "1.5rem",
};

const iconStyle: CSSProperties = {
width: "36px",
height: "36px",
};
82 changes: 82 additions & 0 deletions frontend/src/pages/MyList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useEffect, useState } from "react";

type Pet = {
id: number;
name: string;
breed: string;
age: number;
image_path: string;
};

export default function MyList() {
const [pets, setPets] = useState<Pet[]>([]);

useEffect(() => {
fetch("http://localhost:5000/api/saved_pets")
.then(res => res.json())
.then(data => setPets(data))
.catch(err => console.error("Error fetching saved pets:", err));
}, []);

const handleRemove = (petId: number) => {
fetch(`http://localhost:5000/api/saved_pets/${petId}`, {
method: "DELETE",
})
.then(() => setPets(prev => prev.filter(p => p.id !== petId)))
.catch(err => console.error("Error removing pet:", err));
};

return (
<div style={{ backgroundColor: "rgb(180, 164, 143)", minHeight: "100vh", padding: "2rem" }}>
<h2 style={{ color: "white", textAlign: "center", marginBottom: "2rem" }}>My Saved Pets</h2>
{pets.length === 0 ? (
<p style={{ color: "white", textAlign: "center" }}>You haven’t saved any pets yet.</p>
) : (
<div style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
gap: "2rem"
}}>
{pets.map(pet => (
<div key={pet.id} style={{
backgroundColor: "#7b7267",
padding: "1rem",
borderRadius: "10px",
color: "white",
textAlign: "center",
boxShadow: "0 2px 8px rgba(0,0,0,0.2)"
}}>
<img
src={`http://localhost:5000${pet.image_path}`}
alt={pet.name}
style={{
width: "100%",
height: "200px",
objectFit: "contain",
borderRadius: "8px",
backgroundColor: "#b4a48f"
}}
/>
<h3>{pet.name}</h3>
<p>Breed: {pet.breed}</p>
<p>Age: {pet.age}</p>
<button onClick={() => handleRemove(pet.id)} style={buttonStyle}>
Remove from List
</button>
</div>
))}
</div>
)}
</div>
);
}

const buttonStyle = {
backgroundColor: "black",
color: "white",
border: "none",
borderRadius: "6px",
padding: "0.5rem 1rem",
marginTop: "1rem",
cursor: "pointer"
};
Loading

0 comments on commit a3f322e

Please sign in to comment.