Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 19 additions & 6 deletions Backend/app/main/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -12,7 +12,8 @@
SkillsNeeded,
org_skills_connection,
OrgProfile,
TranslationCache
TranslationCache,
serialize_org_skill_connection
)

main = Blueprint("main", __name__)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
],
}
)

Expand Down Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions Backend/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
99 changes: 98 additions & 1 deletion Backend/app/profile/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down Expand Up @@ -815,4 +912,4 @@ def test_gcs_connection():
{"success": False, "message": "GCS test failed", "results": results}
),
500,
)
)
9 changes: 8 additions & 1 deletion Frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ 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';
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() {
Expand All @@ -37,12 +41,15 @@ function App() {
<Route path="/volunteer" element={<VolunteerPage />} />
<Route path="/home" element={<DisplayPage />} />
<Route path="/find-gos" element={<DisplayPage />} />
<Route path="/" element={<Navigate to="/find-gos" />} />
<Route path="/" element={<HomePage />} />
<Route path="/about-us" element={<AboutUsPage />} />
<Route path="/founder-resources" element={<FoundersResourcesPage />} />
<Route path="/onboarding" element={<OnboardingForm />} />
<Route path="/org_profile_form" element={<OrgProfileForm />} />
<Route path="/org_profile" element={<OrgProfile />} />
<Route path="/upload-images" element={<ProfileImages />} />
<Route path="/edit_profile" element={<EditProfile />} />
<Route path="/volunteer/edit" element={<EditVolunteerProfilePage />} />

<Route path="/establishment-guide" element={<EstablishmentGuide />} />
<Route path="/proposal-builder" element={<ProposalBuilderPage />} />
Expand Down
2 changes: 1 addition & 1 deletion Frontend/src/components/EditingComponents/EditBasicInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion Frontend/src/components/EditingComponents/EditSection.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function EditSection({
showEditButton = true
}) {
return (
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
<div className="bg-white rounded-lg p-6 mb-6">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold text-gray-900"><Translate>{title}</Translate></h3>
{showEditButton && !isEditing && (
Expand Down
9 changes: 4 additions & 5 deletions Frontend/src/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -51,7 +50,7 @@ export default function Header() {
};

return (
<header className="bg-white shadow-md">
<header className="bg-white">
<nav
className="mx-auto flex max-w-7xl items-center justify-between p-4 lg:px-8"
aria-label="Global"
Expand Down
14 changes: 7 additions & 7 deletions Frontend/src/components/KeyWordSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,11 @@ export default function KeyWordSearch({ orgData = [], onSearchResults }) {
};

return (
<div className="bg-teal-600 rounded-xl">
<div className="bg-teal-50 rounded-xl">
<div className="p-8">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-semibold text-white flex items-center gap-2">
<h2 className="text-2xl font-semibold text-gray flex items-center gap-2">
<Translate>Find Organizations</Translate>
</h2>
{searchResults.length > 0 && (
Expand Down Expand Up @@ -241,13 +241,13 @@ export default function KeyWordSearch({ orgData = [], onSearchResults }) {
<div className="space-y-4">
<button
onClick={() => setIsFiltersVisible(!isFiltersVisible)}
className="flex items-center gap-2 text-sm text-teal-50 hover:text-white transition-colors"
className="flex items-center gap-2 text-sm text-teal-600 hover:text-gray transition-colors"
>
<ChevronDown
className={`h-4 w-4 transition-transform duration-200
className={`h-4 w-4 transition-transform duration-200
${isFiltersVisible ? 'rotate-180' : ''}`}
/>
<span className="font-bold text-xl">
<span className="font-bold text-xl text-teal-600">
<Translate>Advanced Filters</Translate>
</span>
</button>
Expand All @@ -259,7 +259,7 @@ export default function KeyWordSearch({ orgData = [], onSearchResults }) {
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 pt-4">
<div>
<label className="block text-l font-bold text-teal-50 mb-2">
<label className="block text-l font-bold text-teal-600 mb-2">
<Translate>Focus Area</Translate>
</label>
<div className="relative backdrop-blur-sm">
Expand All @@ -286,7 +286,7 @@ export default function KeyWordSearch({ orgData = [], onSearchResults }) {
</div>

<div>
<label className="block text-l font-bold text-teal-50 mb-2">
<label className="block text-l font-bold text-teal-600 mb-2">
<Translate>Skills Needed</Translate>
</label>
<div className="relative backdrop-blur-sm">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const ContactInfo = forwardRef(({ initialValues = { email: '', phone: '' } }, re
name="email"
value={state.email}
onChange={handleInputChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500 sm:text-base"
className="mt-1 block w-full rounded-md border-gray-300 focus:border-teal-500 focus:ring-teal-500 sm:text-base"
placeholder="organization@example.com"
/>
{errors.email && (
Expand All @@ -104,7 +104,7 @@ const ContactInfo = forwardRef(({ initialValues = { email: '', phone: '' } }, re
name="phone"
value={state.phone}
onChange={handleInputChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500 sm:text-base"
className="mt-1 block w-full rounded-md border-gray-300 focus:border-teal-500 focus:ring-teal-500 sm:text-base"
placeholder="+254... or 0..."
/>
{errors.phone && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const MissionStatement = forwardRef(({ initialValue = '' }, ref) => {
ref={textAreaRef}
value={statement}
onChange={handleInputChange}
className="w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500
className="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="Enter your organization's mission statement..."
style={{ resize: 'none', overflow: 'hidden' }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ return (
name="districtTown"
value={state.districtTown}
onChange={handleInputChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500 sm:text-base"
className="mt-1 block w-full rounded-md border-gray-300 focus:border-teal-500 focus:ring-teal-500 sm:text-base"
placeholder="e.g., Westlands"
/>
{errors.districtTown && (
Expand Down Expand Up @@ -187,7 +187,7 @@ return (
name="poBox"
value={state.poBox}
onChange={handleInputChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500 sm:text-base"
className="mt-1 block w-full rounded-md border-gray-300 focus:border-teal-500 focus:ring-teal-500 sm:text-base"
placeholder="e.g., P.O. Box 12345-00100"
/>
{errors.poBox && (
Expand Down Expand Up @@ -224,7 +224,7 @@ return (
name="physicalDescription"
value={state.physicalDescription}
onChange={handleInputChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500 sm:text-base min-h-[100px]"
className="mt-1 block w-full rounded-md border-gray-300 focus:border-teal-500 focus:ring-teal-500 sm:text-base min-h-[100px]"
placeholder="e.g., Located on the 3rd floor of Kilimani Business Center, next to Shell Petrol Station"
style={{ resize: 'none', overflow: 'hidden' }}
/>
Expand All @@ -241,7 +241,7 @@ return (
<p className="mt-1 text-sm text-gray-500">
Share your Google Maps link to help people find your exact location
</p>
<div className="mt-1 relative rounded-md shadow-sm">
<div className="mt-1 relative rounded-md ">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<MapPin className="h-5 w-5 text-gray-400" />
</div>
Expand Down
Loading