diff --git a/Backend/app/main/routes.py b/Backend/app/main/routes.py index d268746..859c73d 100644 --- a/Backend/app/main/routes.py +++ b/Backend/app/main/routes.py @@ -3,7 +3,7 @@ import os from app import db from functools import lru_cache -from datetime import datetime, timedelta +from datetime import datetime from google.oauth2 import service_account import logging from sqlalchemy.sql import text @@ -12,7 +12,8 @@ SkillsNeeded, org_skills_connection, OrgProfile, - TranslationCache + TranslationCache, + serialize_org_skill_connection ) main = Blueprint("main", __name__) @@ -52,6 +53,10 @@ def get_all_orgs(): "focus_areas": [focus_area.serialize() for focus_area in org.focus_areas], "skills_needed": [skill.serialize() for skill in org.skills_needed], "org_logo_filename": org.logo_url, + "org_mission_statement": org.org_mission_statement, + "org_year_established": org.org_year_established, + "org_district_town": org.org_district_town, + "org_county": org.org_county, } orgs_data.append(org_data) @@ -87,14 +92,23 @@ def match_volunteer_skills(): # Prepare the response data org_matches = [] for org in matching_orgs: - + # Get the skills connections for this organization + org_skills = ( + db.session.query(org_skills_connection) + .join(SkillsNeeded) + .filter(org_skills_connection.c.org_id == org.id) + .all() + ) org_matches.append( { "org_id": org.id, "org_name": org.org_name, "user_id": org.user_id, "focus_areas": [focus_area.serialize() for focus_area in org.focus_areas], - # "matching_skills": matching_skills, + "skills_needed": [ + serialize_org_skill_connection(skill_connection) + for skill_connection in org_skills + ], } ) @@ -178,8 +192,7 @@ def get_cached_translation(text, target_language): ) # Check if cache is still valid (less than 30 days old) - if (cached_entry and - cached_entry.created_at > datetime.utcnow() - timedelta(days=30)): + if cached_entry: return { 'translatedText': cached_entry.translated_text, 'fromCache': True diff --git a/Backend/app/models.py b/Backend/app/models.py index c5239e0..1c4fc8d 100644 --- a/Backend/app/models.py +++ b/Backend/app/models.py @@ -57,6 +57,23 @@ def serialize(self): db.Column("description", db.Text, nullable=True) ) + +def serialize_org_skill_connection(connection): + """ + Serializes a single row from the org_skills_connection table + and includes the related skill information + """ + # Get the related skill + skill = SkillsNeeded.query.get(connection.skill_id) + + return { + "org_id": connection.org_id, + "skill_id": connection.skill_id, + "description": connection.description, + "skill_name": skill.skill, # Add the skill name + "status": skill.status, # Add the status + } + # Define the Focus Areas table org_focus_areas = db.Table( "org_focus_areas", diff --git a/Backend/app/profile/routes.py b/Backend/app/profile/routes.py index a623e92..41fc635 100644 --- a/Backend/app/profile/routes.py +++ b/Backend/app/profile/routes.py @@ -330,6 +330,103 @@ def get_all_skills(): return jsonify([skill.serialize() for skill in skills]), 200 +@profile.route("/profile/volunteer/edit", methods=["POST"]) +def edit_volunteer_profile(): + data = request.get_json() + + # Get user_id from the request data + user_id = data.get("userId") + + if not user_id: + return jsonify({"message": "User ID is required"}), 400 + + try: + user = User.query.filter_by(id=user_id).one() + except NoResultFound: + return jsonify({"message": "User not found"}), 404 + + if user.is_admin: + return ( + jsonify({"message": "Admin users cannot edit a volunteer profile"}), + 403, + ) + + # Get current skills + current_tech_skills = { + skill.skill for skill in user.skills if skill.status == "tech" + } + current_non_tech_skills = { + skill.skill for skill in user.skills if skill.status == "non-tech" + } + + # Get skills to add and remove + new_tech_skills = set(data.get("techSkills", [])) + new_non_tech_skills = set(data.get("nonTechSkills", [])) + + try: + # Handle tech skills changes + tech_skills_to_add = new_tech_skills - current_tech_skills + tech_skills_to_remove = current_tech_skills - new_tech_skills + + # Handle non-tech skills changes + non_tech_skills_to_add = new_non_tech_skills - current_non_tech_skills + non_tech_skills_to_remove = current_non_tech_skills - new_non_tech_skills + + # Remove skills that are no longer needed + for skill in user.skills[ + : + ]: # Create a copy of the list to avoid modification during iteration + if (skill.status == "tech" and skill.skill in tech_skills_to_remove) or ( + skill.status == "non-tech" and skill.skill in non_tech_skills_to_remove + ): + user.skills.remove(skill) + + # Add new tech skills + for skill_name in tech_skills_to_add: + skill_obj = SkillsNeeded.query.filter_by(skill=skill_name).first() + if not skill_obj: + skill_obj = SkillsNeeded(skill=skill_name, status="tech") + db.session.add(skill_obj) + user.skills.append(skill_obj) + + # Add new non-tech skills + for skill_name in non_tech_skills_to_add: + skill_obj = SkillsNeeded.query.filter_by(skill=skill_name).first() + if not skill_obj: + skill_obj = SkillsNeeded(skill=skill_name, status="non-tech") + db.session.add(skill_obj) + user.skills.append(skill_obj) + + db.session.commit() + + # Return summary of changes + changes = { + "added": { + "tech": list(tech_skills_to_add), + "non_tech": list(non_tech_skills_to_add), + }, + "removed": { + "tech": list(tech_skills_to_remove), + "non_tech": list(non_tech_skills_to_remove), + }, + } + + return ( + jsonify( + { + "message": "Volunteer skills updated successfully", + "changes": changes, + "user": user.serialize(), + } + ), + 200, + ) + + except Exception as e: + db.session.rollback() + return jsonify({"message": f"An error occurred: {str(e)}"}), 500 + + # Edit basic organization profile information @profile.route("/profile/edit_basic_info", methods=["POST"]) def edit_org_profile(): @@ -815,4 +912,4 @@ def test_gcs_connection(): {"success": False, "message": "GCS test failed", "results": results} ), 500, - ) \ No newline at end of file + ) diff --git a/Frontend/src/App.js b/Frontend/src/App.js index 8bca853..080b0cf 100644 --- a/Frontend/src/App.js +++ b/Frontend/src/App.js @@ -10,6 +10,7 @@ import OnboardingForm from './pages/OnboardingForm'; import OrgProfileForm from './pages/OrgProfileForm'; import OrgProfile from './pages/OrgProfile'; import DisplayPage from './pages/DisplayPage'; +import HomePage from './pages/HomePage'; import SignupPage from './pages/SignupPage'; import LoginPage from './pages/LoginPage'; import VolunteerForm from './pages/VolunteerForm'; @@ -17,8 +18,11 @@ import VolunteerPage from './pages/VolunteerPage'; import SelectUserTypePage from './pages/SelectUserTypePage'; import EstablishmentGuide from './pages/EstablishmentGuide'; import EditProfile from './pages/EditProfilePage'; +import EditVolunteerProfilePage from './pages/EditVolunteerProfilePage'; import ProfileImages from "./components/OrgProfileFormComponents/ProfileImages"; import ProposalBuilderPage from './pages/ProposalBuilderPage'; +import AboutUsPage from './pages/AboutUsPage'; +import FoundersResourcesPage from './pages/FoundersResourcesPage'; function App() { @@ -37,12 +41,15 @@ function App() { } /> } /> } /> - } /> + } /> + } /> + } /> } /> } /> } /> } /> } /> + } /> } /> } /> diff --git a/Frontend/src/components/EditingComponents/EditBasicInfo.js b/Frontend/src/components/EditingComponents/EditBasicInfo.js index 5a3867c..74d3c07 100644 --- a/Frontend/src/components/EditingComponents/EditBasicInfo.js +++ b/Frontend/src/components/EditingComponents/EditBasicInfo.js @@ -241,7 +241,7 @@ const EditBasicInfo = ({ ref={overviewTextAreaRef} value={localData.orgProfile.org_overview} onChange={handleOverviewChange} - className="block w-full rounded-md border-gray-300 shadow-sm + className="block w-full rounded-md border-gray-300 focus:border-teal-500 focus:ring-teal-500 sm:text-base min-h-[100px] transition-height duration-200" placeholder="Provide a brief description of your organization" diff --git a/Frontend/src/components/EditingComponents/EditSection.js b/Frontend/src/components/EditingComponents/EditSection.js index 193d7db..5cf5254 100644 --- a/Frontend/src/components/EditingComponents/EditSection.js +++ b/Frontend/src/components/EditingComponents/EditSection.js @@ -16,7 +16,7 @@ export default function EditSection({ showEditButton = true }) { return ( -
+

{title}

{showEditButton && !isEditing && ( diff --git a/Frontend/src/components/Header.js b/Frontend/src/components/Header.js index d36b6fc..9760b08 100644 --- a/Frontend/src/components/Header.js +++ b/Frontend/src/components/Header.js @@ -13,11 +13,10 @@ import { Translate, LanguageSwitch } from '../contexts/TranslationProvider'; // Navigation links const navigation = [ { name: "Home", href: "/" }, - { name: "Find GOs", href: "/find-gos" }, - { name: "Establishment Guide", href: "/establishment-guide" }, - { name: "Proposal Builder", href: "/proposal-builder" }, + { name: "Find CBOs", href: "/find-gos" }, + { name: "Founder Resources", href: "/founder-resources" }, { name: "Volunteer", href: "/volunteer" }, - { name: "About", href: "/about" }, + { name: "About Us", href: "/about-us" }, ]; // Logo component @@ -51,7 +50,7 @@ export default function Header() { }; return ( -
+