diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 21821be0..00000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,20 +0,0 @@ - -#### Description -A clear and concise description of what the issue is about. - -#### Screenshots -![Bunny and carrot](https://media.giphy.com/media/l0HlCc9qfYJ6lZrzO/giphy.gif) - -#### Files -A list of relevant files for this issue. This will help people navigate the project and offer some clues of where to start. - -#### To Reproduce -If this issue is describing a bug, include some steps to reproduce the behavior. - -#### Tasks -Include specific tasks in the order they need to be done in. Include links to specific lines of code where the task should happen at. -- [ ] Task 1 -- [ ] Task 2 -- [ ] Task 3 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 26b2840d..00000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,23 +0,0 @@ - -## Status: - - -## Description :star2: - -Fixes #number - -## TODOs :star: - diff --git a/.gitignore b/.gitignore index dd4995aa..ed17751f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,10 @@ backend/app.log tests/.env tests/cypress/screenshots/ -.cursor \ No newline at end of file +# Agents +.cursor +.factory +.kilocode +.claude +.agents +.agent diff --git a/backend/api/views/auth.py b/backend/api/views/auth.py index a3e37bef..a95b5d53 100644 --- a/backend/api/views/auth.py +++ b/backend/api/views/auth.py @@ -159,7 +159,7 @@ def newregister(): @auth.route("/login", methods=["POST"]) def login(): data = request.json - email = data.get("email") + email = (data.get("email") or "").strip().lower() password = data.get("password") role = int(data.get("role")) path = data.get("path", None) @@ -170,7 +170,7 @@ def login(): if not profile_model.objects(email=email): profile_model = get_profile_model(Account.PARTNER) - profile = profile_model.objects.get(email=email) + profile = profile_model.objects(email=email).first() try: firebase_user = firebase_client.auth().sign_in_with_email_and_password( diff --git a/backend/api/views/download.py b/backend/api/views/download.py index 953b0403..e08015ef 100644 --- a/backend/api/views/download.py +++ b/backend/api/views/download.py @@ -106,7 +106,7 @@ def download_accounts_info(): elif account_type == Account.PARTNER: # Check if we should include Hub accounts in Partners view include_hubs = data.get("include_hubs", "false").lower() == "true" - + if hub_user_id is not None: accounts = PartnerProfile.objects.filter( firebase_uid__nin=admin_ids, hub_id=hub_user_id @@ -152,7 +152,9 @@ def download_accounts_info(): temp = [] for account in accounts: if account.hub_id is not None: - account.hub_user_name = Hub_user_names_object.get(str(account.hub_id), "") + account.hub_user_name = Hub_user_names_object.get( + str(account.hub_id), "" + ) hub_details = Hub_user_details_object.get(str(account.hub_id)) if hub_details: account.hub_email = hub_details.email @@ -436,7 +438,7 @@ def download_partner_accounts(accounts): ",".join(acct.regions), acct.intro, acct.website, - acct.hub_user_name if hasattr(acct, 'hub_user_name') else "", + acct.hub_user_name if hasattr(acct, "hub_user_name") else "", acct.linkedin, acct.sdgs, acct.topics, @@ -474,7 +476,7 @@ def download_partner_accounts(accounts): def download_hub_accounts(accounts): """Download Hub accounts separately from Partners. - + Hubs are represented as PartnerProfile documents with a hub_id field. This function exports Hub-specific data. """ @@ -487,18 +489,30 @@ def download_hub_accounts(accounts): acct.organization, acct.location, acct.person_name, - ",".join(acct.regions) if hasattr(acct, 'regions') and acct.regions else "", - acct.intro if hasattr(acct, 'intro') else "", + ( + ",".join(acct.regions) + if hasattr(acct, "regions") and acct.regions + else "" + ), + acct.intro if hasattr(acct, "intro") else "", acct.website, - acct.hub_user_name if hasattr(acct, 'hub_user_name') else "", - acct.hub_email if hasattr(acct, 'hub_email') else "", - acct.hub_url if hasattr(acct, 'hub_url') else "", - acct.linkedin if hasattr(acct, 'linkedin') else "", - acct.sdgs if hasattr(acct, 'sdgs') else "", - acct.topics if hasattr(acct, 'topics') else "", + acct.hub_user_name if hasattr(acct, "hub_user_name") else "", + acct.hub_email if hasattr(acct, "hub_email") else "", + acct.hub_url if hasattr(acct, "hub_url") else "", + acct.linkedin if hasattr(acct, "linkedin") else "", + acct.sdgs if hasattr(acct, "sdgs") else "", + acct.topics if hasattr(acct, "topics") else "", acct.image.url if acct.image else "None", - len(acct.assign_mentors) if hasattr(acct, 'assign_mentors') and acct.assign_mentors else 0, - len(acct.assign_mentees) if hasattr(acct, 'assign_mentees') and acct.assign_mentees else 0, + ( + len(acct.assign_mentors) + if hasattr(acct, "assign_mentors") and acct.assign_mentors + else 0 + ), + ( + len(acct.assign_mentees) + if hasattr(acct, "assign_mentees") and acct.assign_mentees + else 0 + ), ( int(acct.text_notifications) if acct.text_notifications != None @@ -536,7 +550,7 @@ def download_hub_accounts(accounts): def download_guest_accounts(accounts): """Download Guest accounts. - + Guests have basic information: firebase_uid, email, name, and roomName. """ accts = [] @@ -547,7 +561,7 @@ def download_guest_accounts(accounts): acct.name, acct.email, acct.firebase_uid, - acct.roomName if hasattr(acct, 'roomName') and acct.roomName else "", + acct.roomName if hasattr(acct, "roomName") and acct.roomName else "", ] ) columns = [ @@ -561,7 +575,7 @@ def download_guest_accounts(accounts): def download_support_accounts(accounts): """Download Support accounts. - + Support accounts have basic information: firebase_uid, email, name, and roomName. """ accts = [] @@ -572,7 +586,7 @@ def download_support_accounts(accounts): acct.name, acct.email, acct.firebase_uid, - acct.roomName if hasattr(acct, 'roomName') and acct.roomName else "", + acct.roomName if hasattr(acct, "roomName") and acct.roomName else "", ] ) columns = [ @@ -586,7 +600,7 @@ def download_support_accounts(accounts): def download_moderator_accounts(accounts): """Download Moderator accounts. - + Moderator accounts have basic information: firebase_uid, email, name, and roomName. """ accts = [] @@ -597,7 +611,7 @@ def download_moderator_accounts(accounts): acct.name, acct.email, acct.firebase_uid, - acct.roomName if hasattr(acct, 'roomName') and acct.roomName else "", + acct.roomName if hasattr(acct, "roomName") and acct.roomName else "", ] ) columns = [ diff --git a/backend/api/views/main.py b/backend/api/views/main.py index 2851524b..57e2d1df 100644 --- a/backend/api/views/main.py +++ b/backend/api/views/main.py @@ -51,7 +51,6 @@ from api.utils.require_auth import all_users, mentee_only, verify_user from firebase_admin import auth as firebase_admin_auth - main = Blueprint("main", __name__) # initialize blueprint @@ -156,10 +155,10 @@ def get_accounts(account_type): "email": hub_user.email, "image": hub_user.image, } - + # Check if we should include Hub accounts in Partners view include_hubs = request.args.get("include_hubs", "false").lower() == "true" - + if "restricted" in request.args: if request.args["restricted"] == "true": if "hub_user_id" in request.args: @@ -192,7 +191,9 @@ def get_accounts(account_type): else: # Filter by include_hubs parameter if include_hubs: - accounts = PartnerProfile.objects.filter(restricted__ne=True) + accounts = PartnerProfile.objects.filter( + restricted__ne=True + ) else: accounts = PartnerProfile.objects.filter( restricted__ne=True, hub_id=None diff --git a/backend/api/views/meeting.py b/backend/api/views/meeting.py index 7f1e4a37..cb70a207 100644 --- a/backend/api/views/meeting.py +++ b/backend/api/views/meeting.py @@ -9,7 +9,6 @@ from api.utils.jaas_jwt_builder import JaaSJwtBuilder - meeting = Blueprint("meeting", __name__) API_KEY = os.environ.get("EIGHT_X_EIGHT_API_KEY") diff --git a/backend/api/views/messages.py b/backend/api/views/messages.py index 0d7454b3..2c95e3af 100644 --- a/backend/api/views/messages.py +++ b/backend/api/views/messages.py @@ -26,7 +26,6 @@ from mongoengine.queryset.visitor import Q from urllib.parse import unquote - messages = Blueprint("messages", __name__) diff --git a/backend/api/views/training.py b/backend/api/views/training.py index 7d7fbaac..78eec80a 100644 --- a/backend/api/views/training.py +++ b/backend/api/views/training.py @@ -39,7 +39,6 @@ import pytz from mongoengine.queryset.visitor import Q - training = Blueprint("training", __name__) # initialize blueprint diff --git a/backend/manage.py b/backend/manage.py index a3079459..88a79c12 100644 --- a/backend/manage.py +++ b/backend/manage.py @@ -4,7 +4,6 @@ from api import create_app, socketio from flask import request - # sets up the app app = create_app() diff --git a/backend/uv.lock b/backend/uv.lock index c0ba0910..a02a6a37 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,3 +1,3 @@ version = 1 revision = 3 -requires-python = ">=3.9" +requires-python = ">=3.10" diff --git a/frontend/src/components/AdminDropdowns.js b/frontend/src/components/AdminDropdowns.js index f0cbba80..386c7b56 100644 --- a/frontend/src/components/AdminDropdowns.js +++ b/frontend/src/components/AdminDropdowns.js @@ -39,9 +39,11 @@ export function SortByApptDropdown(props) { return ( - @@ -115,7 +117,7 @@ export function MenteeMentorDropdown(props) { ); return ( - @@ -249,7 +251,7 @@ export function HubsDropdown(props) { {props.options && props.options.map((element, i) => { return ( - handleClick(element.value, element.label)} > @@ -269,7 +271,9 @@ export function HubsDropdown(props) { trigger={["click"]} overlayStyle={{ overflowY: "auto", maxHeight: 300 }} > - diff --git a/frontend/src/components/pages/AdminAccountData.js b/frontend/src/components/pages/AdminAccountData.js index 6a2f063f..a160ce83 100644 --- a/frontend/src/components/pages/AdminAccountData.js +++ b/frontend/src/components/pages/AdminAccountData.js @@ -1,5 +1,14 @@ import React, { useState, useEffect } from "react"; -import { Button, Breadcrumb, Input, Spin, message, Tooltip, Badge, Switch } from "antd"; +import { + Button, + Breadcrumb, + Input, + Spin, + message, + Tooltip, + Badge, + Switch, +} from "antd"; import { DownloadOutlined, ReloadOutlined, @@ -24,10 +33,7 @@ import { downloadSupportersData, downloadModeratorsData, } from "../../utils/api"; -import { - SortByApptDropdown, - HubsDropdown, -} from "../AdminDropdowns"; +import { SortByApptDropdown, HubsDropdown } from "../AdminDropdowns"; import UploadEmails from "../UploadEmails"; import AddGuestModal from "../AddGuestModal"; import AdminDataTable from "../AdminDataTable"; @@ -49,14 +55,22 @@ const keys = { // Get display name for current view const getViewTitle = (displayOption) => { switch (displayOption) { - case keys.MENTORS: return "Mentors"; - case keys.MENTEES: return "Mentees"; - case keys.PARTNER: return "Partners"; - case keys.GUEST: return "Guests"; - case keys.SUPPORT: return "Supporters"; - case keys.HUB: return "Hubs"; - case keys.MODERATOR: return "Moderators"; - default: return "All Accounts"; + case keys.MENTORS: + return "Mentors"; + case keys.MENTEES: + return "Mentees"; + case keys.PARTNER: + return "Partners"; + case keys.GUEST: + return "Guests"; + case keys.SUPPORT: + return "Supporters"; + case keys.HUB: + return "Hubs"; + case keys.MODERATOR: + return "Moderators"; + default: + return "All Accounts"; } }; @@ -68,7 +82,11 @@ const ACCOUNT_TABS = [ { key: keys.HUB, label: "Hubs", accountType: ACCOUNT_TYPE.HUB }, { key: keys.GUEST, label: "Guests", accountType: ACCOUNT_TYPE.GUEST }, { key: keys.SUPPORT, label: "Supporters", accountType: ACCOUNT_TYPE.SUPPORT }, - { key: keys.MODERATOR, label: "Moderators", accountType: ACCOUNT_TYPE.MODERATOR }, + { + key: keys.MODERATOR, + label: "Moderators", + accountType: ACCOUNT_TYPE.MODERATOR, + }, ]; function AdminAccountData() { @@ -87,7 +105,7 @@ function AdminAccountData() { const [searchHubUserId, setSearchHubUserId] = useState(null); const [searchText, setSearchText] = useState(""); const [partnerSearchText, setPartnerSearchText] = useState(""); - + // Include Hub Accounts toggle (only for Partners view) const [includeHubAccounts, setIncludeHubAccounts] = useState(false); @@ -95,7 +113,15 @@ function AdminAccountData() { const hasActiveFilters = searchText || partnerSearchText || searchHubUserId; // Check if current tab supports download - const canDownload = [keys.MENTORS, keys.MENTEES, keys.PARTNER, keys.HUB, keys.GUEST, keys.SUPPORT, keys.MODERATOR].includes(displayOption); + const canDownload = [ + keys.MENTORS, + keys.MENTEES, + keys.PARTNER, + keys.HUB, + keys.GUEST, + keys.SUPPORT, + keys.MODERATOR, + ].includes(displayOption); useEffect(() => { async function getHubData() { @@ -139,7 +165,12 @@ function AdminAccountData() { break; case keys.PARTNER: // Fetch Partners with optional Hub accounts inclusion - const Partners = await fetchAccounts(ACCOUNT_TYPE.PARTNER, undefined, "", includeHubAccounts); + const Partners = await fetchAccounts( + ACCOUNT_TYPE.PARTNER, + undefined, + "", + includeHubAccounts + ); var partners_data = []; if (Partners) { Partners.map((item) => { @@ -244,7 +275,9 @@ function AdminAccountData() { default: break; } - message.success(`Downloaded ${getViewTitle(displayOption)} data successfully`); + message.success( + `Downloaded ${getViewTitle(displayOption)} data successfully` + ); } catch (error) { message.error(`Failed to download data`); console.error(error); @@ -339,7 +372,7 @@ function AdminAccountData() {