diff --git a/submit-api/src/submit_api/models/queries/project.py b/submit-api/src/submit_api/models/queries/project.py index a7994236..3b8eac87 100644 --- a/submit-api/src/submit_api/models/queries/project.py +++ b/submit-api/src/submit_api/models/queries/project.py @@ -13,6 +13,7 @@ # limitations under the License. """Model to handle all complex operations related to User.""" +from sqlalchemy import or_ from submit_api.models import AccountProject, Project, db from submit_api.models.account_project_search_options import AccountProjectSearchOptions from submit_api.models.package import Package @@ -33,11 +34,13 @@ def get_projects_by_proponent_id(cls, proponent_id: int): return query.all() @classmethod - def get_projects_by_account_id(cls, account_id: int, search_options: AccountProjectSearchOptions = None): + def get_filtered_projects(cls, account_id: int = None, search_options: AccountProjectSearchOptions = None): """Find projects by account_id with optional search and pagination.""" - query = db.session.query(AccountProject).filter( - AccountProject.account_id == account_id, - ).join(AccountProject.project) + query = db.session.query(AccountProject).join(AccountProject.project) + + # Apply account_id filter only if provided + if account_id is not None: + query = query.filter(AccountProject.account_id == account_id) # Apply search filters if provided if search_options and any(bool(search_option) for search_option in search_options.__dict__.values()): @@ -52,7 +55,7 @@ def filter_by_search_criteria(cls, project_query, search_options: AccountProject package_query = db.session.query(Package) if search_options.search_text: - package_query = cls._filter_by_submission_name(package_query, search_options.search_text) + package_query = cls._filter_by_search_text(package_query, search_options.search_text) if search_options.status: package_query = cls._filter_by_submission_status(package_query, search_options.status) if search_options.submitted_on_start or search_options.submitted_on_end: @@ -65,14 +68,19 @@ def filter_by_search_criteria(cls, project_query, search_options: AccountProject project_query = project_query.join(Package).filter( Package.id.in_(filtered_package_ids)).options( - db.contains_eager(AccountProject.packages)) + db.contains_eager(AccountProject._packages)) return project_query @classmethod - def _filter_by_submission_name(cls, query, search_text): + def _filter_by_search_text(cls, query, search_text): """Filter by search text across package name.""" - return query.filter(Package.name.ilike(f"%{search_text}%")) + return query.filter( + or_( + Package.name.ilike(f"%{search_text}%"), + Project.name.ilike(f"%{search_text}%") + ) + ) @classmethod def _filter_by_submission_status(cls, query, statuses): diff --git a/submit-api/src/submit_api/resources/staff/project.py b/submit-api/src/submit_api/resources/staff/project.py index 8ed8d96d..99f1b6ee 100644 --- a/submit-api/src/submit_api/resources/staff/project.py +++ b/submit-api/src/submit_api/resources/staff/project.py @@ -14,10 +14,13 @@ """API endpoints for managing a project resource.""" from http import HTTPStatus +from flask import request from flask_restx import Namespace, Resource, cors from submit_api.auth import auth +from submit_api.models.account_project_search_options import AccountProjectSearchOptions +from submit_api.models.package import PackageStatus from submit_api.resources.apihelper import Api as ApiHelper from submit_api.schemas.project import StaffAccountProjectSchema from submit_api.services.project_service import ProjectService @@ -52,7 +55,19 @@ class AccountProjects(Resource): @cors.crossdomain(origin="*") def get(): """Get all account projects.""" - account_projects = ProjectService.get_all_account_projects() + args = request.args + search_text = args.get('search_text') + submitted_on_start = args.get('submitted_on_start') + submitted_on_end = args.get('submitted_on_end') + status = list(map(PackageStatus, args.getlist('status[]'))) + search_options = AccountProjectSearchOptions( + search_text=search_text, + submitted_on_start=submitted_on_start, + submitted_on_end=submitted_on_end, + status=status, + ) + + account_projects = ProjectService.get_all_account_projects(search_options) return StaffAccountProjectSchema(many=True).dump(account_projects), HTTPStatus.OK diff --git a/submit-api/src/submit_api/services/project_service.py b/submit-api/src/submit_api/services/project_service.py index e617ee85..1d3b1230 100644 --- a/submit-api/src/submit_api/services/project_service.py +++ b/submit-api/src/submit_api/services/project_service.py @@ -17,7 +17,7 @@ def get_account_project_by_id(cls, account_project_id): @classmethod def get_projects_by_account_id(cls, account_id, search_options: AccountProjectSearchOptions): """Get projects by account id.""" - return ProjectQueries.get_projects_by_account_id(account_id, search_options) + return ProjectQueries.get_filtered_projects(account_id, search_options) @classmethod def get_projects_by_proponent_id(cls, proponent_id): @@ -35,9 +35,9 @@ def bulk_add_projects(cls, account_id: int, project_ids: list): return projects @classmethod - def get_all_account_projects(cls): + def get_all_account_projects(cls, search_options: AccountProjectSearchOptions): """Get projects by proponent id.""" - return AccountProjectModel.get_all() + return ProjectQueries.get_filtered_projects(None, search_options) @classmethod def get_all_account_projects_with_latest_packages(cls): diff --git a/submit-web/src/components/Filters/ProjectFilters.tsx b/submit-web/src/components/Filters/ProjectFilters.tsx index 65695396..eee792b2 100644 --- a/submit-web/src/components/Filters/ProjectFilters.tsx +++ b/submit-web/src/components/Filters/ProjectFilters.tsx @@ -7,7 +7,7 @@ import { useProjectFilters } from "./projectFilterStore"; import DateSubmittedFromFilter from "./DateSubmittedFromFilter"; import DateSubmittedToFilter from "./DateSubmittedToFilter"; -function ProjectFilters() { +function ProjectFilters({userType}: {userType: string;}) { const { resetFilters } = useProjectFilters(); return ( @@ -17,7 +17,7 @@ function ProjectFilters() { sx={{ maxWidth: "1448px", justifyContent: "space-between" }} > - + diff --git a/submit-web/src/components/Filters/SearchFilter.tsx b/submit-web/src/components/Filters/SearchFilter.tsx index fd642824..010e2c73 100644 --- a/submit-web/src/components/Filters/SearchFilter.tsx +++ b/submit-web/src/components/Filters/SearchFilter.tsx @@ -3,8 +3,9 @@ import { InputAdornment, IconButton, TextField } from "@mui/material"; import { Search, Clear } from "@mui/icons-material"; import { useProjectFilters } from "./projectFilterStore"; import { BCDesignTokens } from "epic.theme"; +import { USER_TYPE } from "@/models/User"; -export const SearchFilter = () => { +export const SearchFilter = ({userType}: {userType: string;}) => { const { filters, setFilters } = useProjectFilters(); const [searchText, setSearchText] = useState(filters.search_text); @@ -27,13 +28,17 @@ export const SearchFilter = () => { + ), diff --git a/submit-web/src/components/Filters/StatusFilter.tsx b/submit-web/src/components/Filters/StatusFilter.tsx index d2e6cbe2..377323ec 100644 --- a/submit-web/src/components/Filters/StatusFilter.tsx +++ b/submit-web/src/components/Filters/StatusFilter.tsx @@ -22,7 +22,7 @@ function StatusFilter() { (status) => status.value, ), }); - } else if (value.length <= 2) { + } else if (value.length <= 3) { setFilters({ status: value }); } }; diff --git a/submit-web/src/routes/proponent/_proponentLayout/projects/index.tsx b/submit-web/src/routes/proponent/_proponentLayout/projects/index.tsx index 3dda4a52..8c37293f 100644 --- a/submit-web/src/routes/proponent/_proponentLayout/projects/index.tsx +++ b/submit-web/src/routes/proponent/_proponentLayout/projects/index.tsx @@ -9,6 +9,7 @@ import { notify } from "@/components/Shared/Snackbar/snackbarStore"; import { PageGrid } from "@/components/Shared/PageGrid"; import ProjectFilters from "@/components/Filters/ProjectFilters"; import { useProjectFilters } from "@/components/Filters/projectFilterStore"; +import { USER_TYPE } from "@/models/User"; export const Route = createFileRoute( "/proponent/_proponentLayout/projects/", @@ -42,7 +43,7 @@ export function ProjectsPage() { return ( - + diff --git a/submit-web/src/routes/staff/_staffLayout/projects/index.tsx b/submit-web/src/routes/staff/_staffLayout/projects/index.tsx index c62143a0..a34635b6 100644 --- a/submit-web/src/routes/staff/_staffLayout/projects/index.tsx +++ b/submit-web/src/routes/staff/_staffLayout/projects/index.tsx @@ -8,6 +8,7 @@ import { Grid } from "@mui/material"; import { createFileRoute, Navigate } from "@tanstack/react-router"; import { useEffect } from "react"; import { Else, If, Then } from "react-if"; +import { USER_TYPE } from "@/models/User"; export const Route = createFileRoute("/staff/_staffLayout/projects/")({ component: ProjectsPage, @@ -37,7 +38,7 @@ function ProjectsPage() { return ( - +