From ac463341644e21a347b108e8b87c2a7192ff0f42 Mon Sep 17 00:00:00 2001 From: mary Date: Sat, 18 Apr 2026 21:21:02 -0400 Subject: [PATCH 1/2] update package.json script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ef3e4fdc3..44ea527fa 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "server/server.js", "type": "module", "scripts": { - "start": "npm run reset && nodemon --require dotenv/config server.js", + "start": "npm run reset && nodemon --require dotenv/config server/server.js", "reset": "cd server/config && node --require dotenv/config reset.js" }, "repository": { From d2f703124e4d35eb7304674e1b249d67847093e2 Mon Sep 17 00:00:00 2001 From: mary Date: Sun, 19 Apr 2026 15:00:28 -0400 Subject: [PATCH 2/2] add controllers, routes, and floriography data in data.js --- server/.env | 4 +- server/config/data.js | 172 ++++++++++++++++++++++++++++++++++ server/controllers/flowers.js | 86 +++++++++++++++++ server/routes/flowers.js | 18 ++++ server/server.js | 21 +++-- server/services/plantAPI.js | 17 ++++ 6 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 server/controllers/flowers.js create mode 100644 server/routes/flowers.js create mode 100644 server/services/plantAPI.js diff --git a/server/.env b/server/.env index eb836d120..21d742e55 100644 --- a/server/.env +++ b/server/.env @@ -2,4 +2,6 @@ PGDATABASE="flowerhunt" PGHOST="dpg-d7ghkn57vvec739k76o0-a.oregon-postgres.render.com" PGPASSWORD="uWb6kOYyJkBxrk2IkHsmvTR9ZZHd4ebW" PGPORT="5432" -PGUSER="flowerhunt_user" \ No newline at end of file +PGUSER="flowerhunt_user" + +PLANT_API_KEY=your_key_here diff --git a/server/config/data.js b/server/config/data.js index e69de29bb..65ea088ac 100644 --- a/server/config/data.js +++ b/server/config/data.js @@ -0,0 +1,172 @@ +import { pool } from "./database.js"; +import { fetchFlowersFromAPI } from "../services/plantAPI.js"; + + + +const flowerMeanings ={ + "Abatina": "Fickleness", + "Acanthus": "The fine art, Artifice", + "Aloe": "Grief, Woe", + "Amaryllis": "Pride, pastorial poetry", + "Anemone": "Forsaken", + "Angelica": "Inspiration", + "Apple Blossom": "Preference", + "Arborvitae": "Unchanging friendship", + "Aster": "symbol of love, daintiness", + "Azalea": "Temperance, fragile passion, chinese symbol of womanhood", + "Baby's Breath": "Everlasting love", + "Bachelor's Button": "Single blessedness", + "Basil": "Good wishes, best wishes", + "Bay tree": "Glory", + "Begonia": "Beware", + "Belledonna":"Silence", + "Bells-of-Ireland": "Good luck", + "Bittersweet": "Truth", + "Black-eyed Susan": "Justice", + "Bluebell": "Humility", + "Borage" : "Bluntness", + "Butterfly weed": "Let me go", + "Cactus": "Endurance", + "Calla lily": "Magnificent beauty", + "Pink Camellia": "Longing", + "Red Camellia": "Passion", + "White Camellia": "Adoration", + "Candytuft": "Indifference", + "Carnation":"Fascination, women love", + "Pink Carnation": "I'll never forget you", + "Red Carnation": "Admiration, Deep love", + "purple Carnation": "Capriciousness", + "yellow Carnation": "Disappointment, rejection", + "White Carnation": "Pure love, good luck", + "Cattail": "Peace, prosperity", + "Chamomile": "Patience, rest", + "Chives": "Usefulness", + "Red Chrysanthemum": "I love you", + "White Chrysanthemum": "Loyal love", + "Yellow Chrysanthemum": "Slighted love", + "Clemantis": "Mental beauty", + "Cleome": "Elope with me", + "Clover": "Good luck", + "Columbine": "Foolishness, Folly", + "Coreopsis": "Always cheerful", + "Crocus": "Cheerfulness", + "Cyclamen": "Resignation and goodbye", + "Daffodil": "Unrequited love, regard", + "Daisy": "Innocence, loyal love, purity", + "Dandelion": "Happiness,faithfulness", + "Daylily": "Coquetry, Chinese symbol of motherhood", + "Edelweiss": "Devotion", + "Evening Primrose": "Inconstancy", + "Fennel": "Flattery", + "Fern": "Fasination, secret bond of love", + "Fir": "Time", + "Flax": "Domestic symbol", + "Forget-Me-Not": "True love, remembrance", + "Forsythia": "Anticipation", + "Gardenia": "Secret love", + "Garlic": "Strength, courage", + "Geranium": "Foolishness", + "Gladiolus": "Sincerity, victory", + "Gloxinia": "Love at first sight", + "Goldenrod": "Encouragement", + "Heliotrope": "Devotion, eternal love", + "Hibiscus": "Delicate beauty", + "Holly": "Defense, domestic happiness", + "Honeysuckle": "Generosity", + "Hyacinth": "Playfulness, rashness", + "Blue Hyacinth": "Constancy", + "Purple Hyacinth": "Sorrow", + "Yellow Hyacinth": "Jealousy", + "White Hyacinth": "Loveliness, prayers for someone", + "Hydrangea": "Frigidity and heartlessness", + "Hyssop": "Sacrifice, purification", + "Iris": "Faith, hope, wisdom", + "Ivy": "Fidelity", + "Jasmine": "Grace, elegance", + "Lady's Slipper": "Capricious beauty", + "Larkspur": "Levity, fickleness", + "Lilac": "Old love, joy of youth", + "Lily": "Purity, refined beauty", + "Lily of the Valley": "Return of happiness, Sweetness, humility", + "Lavender": "Distrust", + "lotus": "Enlightenment, rebirth", + "Magnolia": "Dignity, nobility", + "Marigold": "Despair and grief over a loved one", + "marjoram": "Happiness", + "Mint": "Virtue", + "Mistletoe": "Affection", + "Mock orange": "Deceit", + "Morning Glory": "Affection", + "Monkshood": "Caution", + "Moss": "Maternal love", + "Mugwort": "Happiness in love", + "Myrtle": "Love in marriage", + "Narcissus": "Egotism", + "Nasturtium": "Patriotism, victory in battle", + "oak": "Strength and endurance", + "Orchid": "Refinement, beauty, love", + "Pansy": "Thoughts", + "Peony": "Shame, bashfulness", + "Periwinkle": "Friendship", + "Petunia": "Resentment and anger", + "Pine":"Hope, humility, piety", + "Poppy": "Consolation, remembrance, eternal sleep", + "Primrose": "Young love", + "Rhododendron": "Danger", + "Rosebud": "Beauty, innocent love", + "Pink Rose": "Admiration, gratitude", + "Red Rose": "Love, passion", + "yellow Rose": "Decrease of love, jealousy, infidelity", + "White Rose": "Purity, innocence", + "Rosemary": "Remembrance", + "Sage": "Wisdom, immortality", + "Saffron": "Joy and happiness", + "Salvia": "Forever mine", + "Smilax": "Loveliness", + "Snapdragon": "Graciousness, deception", + "sorrel": "Affection", + "Sweet Pea": "Goodbye", + "Sweet William": "Gallantry", + "Tarragon": "Fascination", + "Tiger Lily": "Wealth and pride", + "Thistle": "Nobility of character", + "Thyme": "Courage, bravery", + "Tulip": "Perfect love", + "red Tulip": "Declaration of love", + "yellow Tulip": "Cheerful thoughts", + "White Tulip": "Forgiveness", + "Violet": "Modesty, faithfulness, watchfulness", + "Willow": "Sadness", + "Wisteria": "Welcome, blissful, devotion", + "Yarrow": "Everlasting love", + "Zinnia": "Thoughts of absent friends", +} + +export const seedFlowers = async () => { + const flowers = await fetchFlowersFromAPI(); + for (const flower of flowers) { + // const nameKey = flower.name?.toLowerCase().trim(); + // const meaning = flowerMeanings[nameKey] ?? flower.flower_meaning; + const nameKey = flower.name?.toLowerCase().trim(); + // Try exact match first, then partial match + const meaning = + flowerMeanings[nameKey] ?? + Object.entries(flowerMeanings).find(([k]) => nameKey?.includes(k))?.[1] ?? + flower.flower_meaning; + + await pool.query( + `INSERT INTO flowers (name, description, flower_family, flower_meaning, image_url) + VALUES ($1, $2, $3, $4, $5) + ON CONFLICT DO NOTHING`, + [ + flower.name, + flower.description, + flower.flower_family, + meaning, // <-- use the enriched meaning here + flower.image_url, + ], + ); + } + + console.log(`Seeded ${flowers.length} flowers`); +}; diff --git a/server/controllers/flowers.js b/server/controllers/flowers.js new file mode 100644 index 000000000..b4d79fafb --- /dev/null +++ b/server/controllers/flowers.js @@ -0,0 +1,86 @@ +import { pool } from "../config/database.js"; + +const getFlowers = async (req, res) => { + try { + const { meaning, family, search } = req.query; + let query = "SELECT * FROM flowers WHERE true"; + const params = []; + + if (meaning) { + params.push(`%${meaning}%`); + query += ` AND flower_meaning ILIKE $${params.length}`; + } + if (family) { + params.push(family); + query += ` AND flower_family = $${params.length}`; + } + if (search) { + params.push(`%${search}%`); + query += ` AND name ILIKE $${params.length}`; + } + + const { rows } = await pool.query(query, params); + res.json(rows); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +const getFlowerById = async (req, res) => { + try { + const { rows } = await pool.query( + "SELECT * FROM flowers WHERE id = $1", + [req.params.id] + ); + if (!rows.length) return res.status(404).json({ error: "Flower not found" }); + res.json(rows[0]); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +const createFlower = async (req, res) => { + try { + const { name, description, flower_family, flower_meaning, image_url } = req.body; + const { rows } = await pool.query( + `INSERT INTO flowers (name, description, flower_family, flower_meaning, image_url) + VALUES ($1,$2,$3,$4,$5) RETURNING *`, + [name, description, flower_family, flower_meaning, image_url] + ); + res.status(201).json(rows[0]); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +const updateFlower = async (req, res) => { + try { + const { name, description, flower_family, flower_meaning, image_url } = req.body; + const { rows } = await pool.query( + `UPDATE flowers SET name=$1, description=$2, flower_family=$3, + flower_meaning=$4, image_url=$5 WHERE id=$6 RETURNING *`, + [name, description, flower_family, flower_meaning, image_url, req.params.id] + ); + if (!rows.length) return res.status(404).json({ error: "Flower not found" }); + res.json(rows[0]); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +const deleteFlower = async (req, res) => { + try { + await pool.query("DELETE FROM flowers WHERE id = $1", [req.params.id]); + res.status(204).send(); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +export default { + getFlowers, + getFlowerById, + createFlower, + updateFlower, + deleteFlower, +}; diff --git a/server/routes/flowers.js b/server/routes/flowers.js new file mode 100644 index 000000000..70fe1ab89 --- /dev/null +++ b/server/routes/flowers.js @@ -0,0 +1,18 @@ +import { Router } from "express"; +import { + getFlowers, + getFlowerById, + createFlower, + updateFlower, + deleteFlower, +} from "../controllers/flowers.js"; + +const router = Router(); + +router.get("/", getFlowers); +router.get("/:id", getFlowerById); +router.post("/", createFlower); +router.patch("/:id", updateFlower); +router.delete("/:id", deleteFlower); + +export default router; diff --git a/server/server.js b/server/server.js index eba3e6adc..6ddf10b19 100644 --- a/server/server.js +++ b/server/server.js @@ -1,19 +1,28 @@ import express from "express"; import cors from "cors"; +import flowerRoutes from "./routes/flowers.js"; const app = express(); app.use(express.json()); app.use(cors()); -app.get("/", (req, res) => { - res - .status(200) - .send( - '

🌸FlowerHunt API

', - ); +app.use("/api/flowers", flowerRoutes); + +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ error: "Something went wrong" }); }); + +// app.get("/", (req, res) => { +// res +// .status(200) +// .send( +// '

🌸FlowerHunt API

', +// ); +// }); + const PORT = process.env.PORT || 3001; app.listen(PORT, () => { diff --git a/server/services/plantAPI.js b/server/services/plantAPI.js new file mode 100644 index 000000000..872cb5ed6 --- /dev/null +++ b/server/services/plantAPI.js @@ -0,0 +1,17 @@ +// Fetches flowers from external API and maps them to your DB schema +const API_BASE = "..."; // or your chosen API + +export const fetchFlowersFromAPI = async (page = 1) => { + const res = await fetch( + `${API_BASE}/species-list?key=${process.env.PLANT_API_KEY}&page=${page}&type=flower`, + ); + const json = await res.json(); + + return json.data.map((plant) => ({ + name: plant.common_name, + description: plant.description ?? null, + flower_family: plant.family ?? null, + flower_meaning: null, // most APIs don't have this; you'll add manually or skip + image_url: plant.default_image?.regular_url ?? null, + })); +};