diff --git a/submit-api/migrations/versions/77862b346adf_add_requested_by_eao_package_status.py b/submit-api/migrations/versions/77862b346adf_add_requested_by_eao_package_status.py new file mode 100644 index 000000000..551903f33 --- /dev/null +++ b/submit-api/migrations/versions/77862b346adf_add_requested_by_eao_package_status.py @@ -0,0 +1,22 @@ +"""Add REQUESTED_BY_EAO package status + +Revision ID: 77862b346adf +Revises: e27054af162b +Create Date: 2026-03-02 16:02:40.724455 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '77862b346adf' +down_revision = 'e27054af162b' +branch_labels = None +depends_on = None + +def upgrade(): + op.execute("ALTER TYPE packagestatus ADD VALUE IF NOT EXISTS 'REQUESTED_BY_EAO';") + +def downgrade(): + pass \ No newline at end of file diff --git a/submit-api/src/submit_api/models/account_project.py b/submit-api/src/submit_api/models/account_project.py index 11ef6bda9..ecdd88171 100644 --- a/submit-api/src/submit_api/models/account_project.py +++ b/submit-api/src/submit_api/models/account_project.py @@ -33,7 +33,8 @@ class AccountProject(BaseModel): primaryjoin='AccountProjectWork.account_project_id==AccountProject.id', lazy='select', cascade='all, delete', - passive_deletes=True) + passive_deletes=True, + back_populates='account_project') @property def latest_packages(self): diff --git a/submit-api/src/submit_api/models/account_project_work.py b/submit-api/src/submit_api/models/account_project_work.py index 72ac35931..d46b54841 100644 --- a/submit-api/src/submit_api/models/account_project_work.py +++ b/submit-api/src/submit_api/models/account_project_work.py @@ -40,7 +40,12 @@ class AccountProjectWork(BaseModel): comment='Whether this association is currently active' ) - account_project = db.relationship('AccountProject', foreign_keys=[account_project_id], lazy='joined') + account_project = db.relationship( + 'AccountProject', + foreign_keys=[account_project_id], + lazy='joined', + back_populates='account_project_works') + work = db.relationship('TrackWork', foreign_keys=[work_id], lazy='joined') packages = db.relationship( @@ -48,8 +53,8 @@ class AccountProjectWork(BaseModel): primaryjoin='Package.account_project_work_id==AccountProjectWork.id', lazy='select', cascade='all, delete', - passive_deletes=True - ) + passive_deletes=True, + back_populates='account_project_work') @classmethod def find_by_account_project_id(cls, account_project_id: int): diff --git a/submit-api/src/submit_api/models/package.py b/submit-api/src/submit_api/models/package.py index 3dff44e1f..e7db7a456 100644 --- a/submit-api/src/submit_api/models/package.py +++ b/submit-api/src/submit_api/models/package.py @@ -57,6 +57,7 @@ class PackageStatus(enum.Enum): REVISION_REQUIRED = 'REVISION_REQUIRED' NO_REVISION_REQUIRED = 'NO_REVISION_REQUIRED' RESUBMITTED = 'RESUBMITTED' + REQUESTED_BY_EAO = 'REQUESTED_BY_EAO' @classmethod def check_value(cls, value): @@ -83,7 +84,7 @@ class Package(BaseModel): account_project_work_id = Column(db.Integer, ForeignKey( 'account_project_works.id', ondelete='SET NULL'), nullable=True) account_project_work = db.relationship('AccountProjectWork', foreign_keys=[ - account_project_work_id], lazy='joined') + account_project_work_id], lazy='joined', back_populates='packages') submitted_on = Column(db.DateTime, nullable=True) submitted_by = Column(db.String, ForeignKey( 'users.auth_guid', name='packages_submitted_by_fkey'), nullable=True) diff --git a/submit-api/src/submit_api/schemas/project.py b/submit-api/src/submit_api/schemas/project.py index 6f96f2d5c..41032287d 100644 --- a/submit-api/src/submit_api/schemas/project.py +++ b/submit-api/src/submit_api/schemas/project.py @@ -8,6 +8,7 @@ from submit_api.schemas.package import PackageSchema, StaffPackageSchema from submit_api.schemas.proponent import ProponentSchema +from submit_api.schemas.track_work import TrackWorkSchema class ProjectSchema(Schema): @@ -48,6 +49,19 @@ class Meta: # pylint: disable=too-few-public-methods items = fields.Function(lambda obj: []) +class AccountProjectWorkSchema(Schema): + """Account project work schema.""" + + class Meta: # pylint: disable=too-few-public-methods + """Exclude unknown fields in the deserialized output.""" + + unknown = EXCLUDE + + id = fields.Int(data_key="id") + work_id = fields.Int(data_key="work_id") + work = fields.Nested(TrackWorkSchema, data_key="work") + + class AccountProjectSchema(Schema): """Account project schema.""" @@ -61,6 +75,7 @@ class Meta: # pylint: disable=too-few-public-methods project_id = fields.Int(data_key="project_id") project = fields.Nested(ProjectSchema, data_key="project") latest_packages = fields.List(fields.Nested(AccountProjectPackageSchema), data_key="packages") + account_project_works = fields.List(fields.Nested(AccountProjectWorkSchema, data_key="account_project_works")) class StaffAccountProjectPackageSchema(StaffPackageSchema): diff --git a/submit-api/src/submit_api/schemas/track_phase.py b/submit-api/src/submit_api/schemas/track_phase.py new file mode 100644 index 000000000..fe40ab8a1 --- /dev/null +++ b/submit-api/src/submit_api/schemas/track_phase.py @@ -0,0 +1,23 @@ +"""TrackPhase schema. + +This module defines the schema for the track_phase entity. +""" +from marshmallow import EXCLUDE, Schema, fields + + +class TrackPhaseSchema(Schema): + """Track phase schema.""" + + class Meta: # pylint: disable=too-few-public-methods + """Exclude unknown fields in the deserialized output.""" + + unknown = EXCLUDE + + id = fields.Int(data_key="id") + name = fields.Str(data_key="name") + ea_act_id = fields.Int(data_key="ea_act_id", allow_none=True) + work_type_id = fields.Int(data_key="work_type_id") + work_type_name = fields.Str(data_key="work_type_name", allow_none=True) + sort_order = fields.Int(data_key="sort_order", allow_none=True) + number_of_days = fields.Int(data_key="number_of_days", allow_none=True) + legislated = fields.Boolean(data_key="legislated") diff --git a/submit-api/src/submit_api/schemas/track_work.py b/submit-api/src/submit_api/schemas/track_work.py new file mode 100644 index 000000000..ffaedcf30 --- /dev/null +++ b/submit-api/src/submit_api/schemas/track_work.py @@ -0,0 +1,23 @@ +"""TrackWork schema. + +This module defines the schema for the track_work entity. +""" +from marshmallow import EXCLUDE, Schema, fields + +from submit_api.schemas.track_phase import TrackPhaseSchema + + +class TrackWorkSchema(Schema): + """Track work schema.""" + + class Meta: # pylint: disable=too-few-public-methods + """Exclude unknown fields in the deserialized output.""" + + unknown = EXCLUDE + + id = fields.Int(data_key="id") + project_id = fields.Int(data_key="project_id") + current_phase_id = fields.Int(data_key="current_phase_id", allow_none=True) + work_state = fields.Str(data_key="work_state", allow_none=True) + title = fields.Str(data_key="title", allow_none=True) + current_phase = fields.Nested(TrackPhaseSchema, data_key="current_phase", allow_none=True) diff --git a/submit-web/src/components/App/PackageStatusChip/index.tsx b/submit-web/src/components/App/PackageStatusChip/index.tsx index f799dc042..ea14b80f1 100644 --- a/submit-web/src/components/App/PackageStatusChip/index.tsx +++ b/submit-web/src/components/App/PackageStatusChip/index.tsx @@ -229,6 +229,16 @@ const statusStyles: Record< }, label: "Resubmitted", }, + REQUESTED_BY_EAO: { + sx: { + borderRadius: 1, + border: `1px solid ${BCDesignTokens.supportBorderColorWarning}`, + background: BCDesignTokens.supportSurfaceColorWarning, + height: "24px", + width: "139px", + }, + label: "Requested by EAO", + }, }; type PackageStatusChipProps = Readonly<{ diff --git a/submit-web/src/components/App/Projects/Project.tsx b/submit-web/src/components/App/Projects/Project.tsx index 490064d34..337e6453a 100644 --- a/submit-web/src/components/App/Projects/Project.tsx +++ b/submit-web/src/components/App/Projects/Project.tsx @@ -1,17 +1,9 @@ -import { Box, Button, Divider, styled, Typography } from "@mui/material"; -import { BCDesignTokens } from "epic.theme"; -import AddIcon from "@mui/icons-material/Add"; -import { ProjectStatus } from "@/components/App/registration/addProjects/ProjectStatus"; -import { PROJECT_STATUS } from "@/components/App/registration/addProjects/ProjectCard/constants"; -import ProjectTable from "@/components/App/Projects/ProjectTable"; +import { ContentBox } from "@/components/Shared/Layouts/ContentBox"; import { AccountProject } from "@/models/Project"; +import { Box, styled } from "@mui/material"; import { useNavigate } from "@tanstack/react-router"; -import { ContentBox } from "@/components/Shared/Layouts/ContentBox"; -import { When } from "react-if"; -import { useAccount } from "@/store/accountStore"; -import { USER_TYPE } from "@/models/User"; -import PermissionsGate from "@/components/Shared/PermissionGate"; -import { ACCOUNT_USER_PERMISSIONS } from "@/models/Role"; +import { ProjectSubmissionsCard } from "./ProjectSubmissionsCard"; +import { PROJECT_STATUS } from "@/components/App/registration/addProjects/ProjectCard/constants"; export const CardInnerBox = styled(Box)({ display: "flex", @@ -28,16 +20,10 @@ type ProjectParam = { export const Project = ({ accountProject }: ProjectParam) => { const navigate = useNavigate(); - const { userType } = useAccount(); - - const activeSubmissionPackages = accountProject.packages.filter( - (subPackage) => !subPackage.completed_on, - ); - const pastSubmissionPackages = accountProject.packages.filter((subPackage) => - Boolean(subPackage.completed_on), - ); const { name, ea_certificate } = accountProject.project; + const currentPhase = + accountProject.account_project_works?.at(-1)?.work?.current_phase ?? null; const handleNewSubmission = () => { navigate({ @@ -52,89 +38,21 @@ export const Project = ({ accountProject }: ProjectParam) => { topLabel={accountProject.project.proponent?.name || ""} bottomLabel={ea_certificate ? `EAC # ${ea_certificate}` : ""} > - - - - - Management Plans & Related Documents - - - - - - - - - - - - - - - Active Submissions - - - - - - - Review Completed by the EAO - - - - - - + {currentPhase?.work_type_name == "ASSESSMENT" ? ( + + ) : ( + + )} ); }; diff --git a/submit-web/src/components/App/Projects/ProjectSubmissionsCard.tsx b/submit-web/src/components/App/Projects/ProjectSubmissionsCard.tsx new file mode 100644 index 000000000..2b13ac0e2 --- /dev/null +++ b/submit-web/src/components/App/Projects/ProjectSubmissionsCard.tsx @@ -0,0 +1,129 @@ +import ProjectTable from "@/components/App/Projects/ProjectTable"; +import { ProjectStatus } from "@/components/App/registration/addProjects/ProjectStatus"; +import PermissionsGate from "@/components/Shared/PermissionGate"; +import { SubmissionPackage } from "@/models/Package"; +import { ACCOUNT_USER_PERMISSIONS } from "@/models/Role"; +import { USER_TYPE } from "@/models/User"; +import { useAccount } from "@/store/accountStore"; +import AddIcon from "@mui/icons-material/Add"; +import { Box, Button, Divider, styled, Typography } from "@mui/material"; +import { BCDesignTokens } from "epic.theme"; +import { When } from "react-if"; +import { PROJECT_STATUS } from "../registration/addProjects/ProjectCard/constants"; + +export const CardInnerBox = styled(Box)({ + display: "flex", + alignItems: "flex-start", + justifyContent: "center", + flexDirection: "column", + height: "100%", + padding: "0 12px", +}); + +type ProjectSubmissionsCardProps = { + title: string; + status: string; + packages: SubmissionPackage[]; + onNewSubmission: () => void; +}; + +export const ProjectSubmissionsCard = ({ + title, + status, + packages, + onNewSubmission, +}: ProjectSubmissionsCardProps) => { + const { userType } = useAccount(); + + const activeSubmissionPackages = packages.filter( + (subPackage) => !subPackage.completed_on, + ); + const pastSubmissionPackages = packages.filter((subPackage) => + Boolean(subPackage.completed_on), + ); + + return ( + + + + + {title} + + + + + + + + + + + + + + + + Active Submissions + + + + + + + + + Review Completed by the EAO + + + + + + + + ); +}; diff --git a/submit-web/src/components/App/Projects/ProjectTable/ProponentTableRow.tsx b/submit-web/src/components/App/Projects/ProjectTable/ProponentTableRow.tsx index 103643211..7bb3e66fa 100644 --- a/submit-web/src/components/App/Projects/ProjectTable/ProponentTableRow.tsx +++ b/submit-web/src/components/App/Projects/ProjectTable/ProponentTableRow.tsx @@ -53,10 +53,12 @@ export default function ProponentTableRow({ subPackage }: ProjectRowProps) { - {dateUtils.formatDate(subPackage.submitted_on)} + {subPackage.submitted_on + ? dateUtils.formatDate(subPackage.submitted_on) + : "-"} - {subPackage.submitted_by ?? ""} + {subPackage.submitted_by ?? "-"} = { width: "128px", }, }, + REQUESTED_BY_EAO: { + sx: { + borderRadius: 1, + border: `1px solid ${BCDesignTokens.supportBorderColorInfo}`, + background: BCDesignTokens.themeBlue20, + height: "24px", + width: "104px", + }, + label: "Requested by EAO", + }, }; type SubmissionStatusChipProps = Readonly<{ diff --git a/submit-web/src/components/App/registration/addProjects/ProjectCard/constants.ts b/submit-web/src/components/App/registration/addProjects/ProjectCard/constants.ts index 6b6b9e0a9..c1984057b 100644 --- a/submit-web/src/components/App/registration/addProjects/ProjectCard/constants.ts +++ b/submit-web/src/components/App/registration/addProjects/ProjectCard/constants.ts @@ -1,3 +1,4 @@ export const PROJECT_STATUS = { - POST_DECISION: "Post Decision", + EARLY_ENGAGEMENT: "EARLY_ENGAGEMENT", + POST_DECISION: "POST_DECISION", }; diff --git a/submit-web/src/components/App/registration/addProjects/ProjectStatus.tsx b/submit-web/src/components/App/registration/addProjects/ProjectStatus.tsx index ab7d727d6..c57a23fbe 100644 --- a/submit-web/src/components/App/registration/addProjects/ProjectStatus.tsx +++ b/submit-web/src/components/App/registration/addProjects/ProjectStatus.tsx @@ -1,32 +1,45 @@ import { Caption2 } from "@/components/Shared/Typographies"; -import { Case, Default, Switch } from "react-if"; import ModeStandbyIcon from "@mui/icons-material/ModeStandby"; import { Stack } from "@mui/material"; -import { PROJECT_STATUS } from "./ProjectCard/constants"; import { EAOColors } from "epic.theme"; +type StyleProps = { + color: string; + label: string; +}; + +const statusStyles: Record = { + POST_DECISION: { + color: EAOColors.DecisionDark, + label: "Post-Decision", + }, + EARLY_ENGAGEMENT: { + color: "#5583B5", + label: "Early Engagement", + }, +}; + type ProjectStatusProps = { status: string; }; export const ProjectStatus = ({ status }: ProjectStatusProps) => { + const style = statusStyles[status]; + + if (!style) { + return null; + } + return ( - - - - - - Post-Decision - - - - - {status} - - + + + + {style.label} + + ); }; diff --git a/submit-web/src/models/Package.ts b/submit-web/src/models/Package.ts index c80f07673..39919634b 100644 --- a/submit-web/src/models/Package.ts +++ b/submit-web/src/models/Package.ts @@ -44,7 +44,8 @@ export type PackageStatus = | "PASSED_CONSULTATION_CHECK" | "AWAITING_MANAGER_APPROVAL" | "FAILED_CONSULTATION_CHECK" - | "CREATED"; + | "CREATED" + | "REQUESTED_BY_EAO"; export const PACKAGE_STATUS: Record< PackageStatus, @@ -118,6 +119,10 @@ export const PACKAGE_STATUS: Record< value: "REVIEWED", label: "Reviewed", }, + REQUESTED_BY_EAO: { + value: "REQUESTED_BY_EAO", + label: "Requested by EAO", + }, }; export type SubmissionPackageMeta = Record; diff --git a/submit-web/src/models/Project.ts b/submit-web/src/models/Project.ts index cbaf8c133..c9d1f177f 100644 --- a/submit-web/src/models/Project.ts +++ b/submit-web/src/models/Project.ts @@ -1,5 +1,6 @@ import { SubmissionPackage } from "./Package"; import { Proponent } from "./Proponent"; +import { TrackWork } from "./TrackWork"; export type Project = { id: number; @@ -14,12 +15,19 @@ export const getProjectProponentId = (project: Project): number => { return project.proponent_id ?? 0; }; +export type AccountProjectWork = { + id: number; + work_id: number; + work: TrackWork; +}; + export type AccountProject = { id: number; project_id: number; account_id: number; project: Project; packages: SubmissionPackage[]; + account_project_works?: AccountProjectWork[]; }; export const createDefaultAccountProject = (): AccountProject => ({ diff --git a/submit-web/src/models/TrackPhase.ts b/submit-web/src/models/TrackPhase.ts new file mode 100644 index 000000000..9aec376b2 --- /dev/null +++ b/submit-web/src/models/TrackPhase.ts @@ -0,0 +1,10 @@ +export type TrackPhase = { + id: number; + name: string; + ea_act_id?: number; + work_type_id: number; + work_type_name?: string; + sort_order?: number; + number_of_days?: number; + legislated: boolean; +}; diff --git a/submit-web/src/models/TrackWork.ts b/submit-web/src/models/TrackWork.ts new file mode 100644 index 000000000..55e041754 --- /dev/null +++ b/submit-web/src/models/TrackWork.ts @@ -0,0 +1,10 @@ +import { TrackPhase } from "./TrackPhase"; + +export type TrackWork = { + id: number; + project_id: number; + current_phase_id?: number; + work_state?: string; + title?: string; + current_phase?: TrackPhase; +};