From 67736c848db9b5d500ab3d64b669cbe9addf82e8 Mon Sep 17 00:00:00 2001 From: leodube-aot Date: Mon, 2 Mar 2026 15:43:25 -0800 Subject: [PATCH 1/3] Create schemas for new db models --- .../src/submit_api/enums/package_type.py | 2 ++ .../src/submit_api/models/account_project.py | 3 ++- .../submit_api/models/account_project_work.py | 11 ++++++--- submit-api/src/submit_api/models/package.py | 3 ++- submit-api/src/submit_api/schemas/project.py | 15 ++++++++++++ .../src/submit_api/schemas/track_phase.py | 23 +++++++++++++++++++ .../src/submit_api/schemas/track_work.py | 23 +++++++++++++++++++ 7 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 submit-api/src/submit_api/schemas/track_phase.py create mode 100644 submit-api/src/submit_api/schemas/track_work.py diff --git a/submit-api/src/submit_api/enums/package_type.py b/submit-api/src/submit_api/enums/package_type.py index a32abe0d..1e47396f 100644 --- a/submit-api/src/submit_api/enums/package_type.py +++ b/submit-api/src/submit_api/enums/package_type.py @@ -12,6 +12,7 @@ class PackageTypeEnum(enum.Enum): MANAGEMENT_PLAN = 'Management Plan' IEM = 'IEM' + IPD = 'IPD' class PackageTypeId(enum.Enum): @@ -19,3 +20,4 @@ class PackageTypeId(enum.Enum): MANAGEMENT_PLAN = 1 IEM = 2 + IPD = 3 diff --git a/submit-api/src/submit_api/models/account_project.py b/submit-api/src/submit_api/models/account_project.py index 11ef6bda..ecdd8817 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 72ac3593..d46b5484 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 3dff44e1..e7db7a45 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 6f96f2d5..41032287 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 00000000..fe40ab8a --- /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 00000000..ffaedcf3 --- /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) From be9490680f5e09de030e236a00dfd149dbe1522a Mon Sep 17 00:00:00 2001 From: leodube-aot Date: Mon, 2 Mar 2026 16:09:10 -0800 Subject: [PATCH 2/3] Add card for early engagement --- ...adf_add_requested_by_eao_package_status.py | 22 +++ .../src/submit_api/enums/package_type.py | 2 - .../App/PackageStatusChip/index.tsx | 10 ++ .../src/components/App/Projects/Project.tsx | 124 +++-------------- .../App/Projects/ProjectSubmissionsCard.tsx | 129 ++++++++++++++++++ .../RegistrationUrl/RegistrationUrl.tsx | 2 +- .../App/SubmissionStatusChip/index.tsx | 10 ++ .../addProjects/ProjectCard/constants.ts | 3 +- .../addProjects/ProjectStatus.tsx | 53 ++++--- submit-web/src/models/Package.ts | 7 +- submit-web/src/models/Project.ts | 8 ++ submit-web/src/models/TrackPhase.ts | 10 ++ submit-web/src/models/TrackWork.ts | 10 ++ 13 files changed, 262 insertions(+), 128 deletions(-) create mode 100644 submit-api/migrations/versions/77862b346adf_add_requested_by_eao_package_status.py create mode 100644 submit-web/src/components/App/Projects/ProjectSubmissionsCard.tsx create mode 100644 submit-web/src/models/TrackPhase.ts create mode 100644 submit-web/src/models/TrackWork.ts 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 00000000..551903f3 --- /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/enums/package_type.py b/submit-api/src/submit_api/enums/package_type.py index 1e47396f..a32abe0d 100644 --- a/submit-api/src/submit_api/enums/package_type.py +++ b/submit-api/src/submit_api/enums/package_type.py @@ -12,7 +12,6 @@ class PackageTypeEnum(enum.Enum): MANAGEMENT_PLAN = 'Management Plan' IEM = 'IEM' - IPD = 'IPD' class PackageTypeId(enum.Enum): @@ -20,4 +19,3 @@ class PackageTypeId(enum.Enum): MANAGEMENT_PLAN = 1 IEM = 2 - IPD = 3 diff --git a/submit-web/src/components/App/PackageStatusChip/index.tsx b/submit-web/src/components/App/PackageStatusChip/index.tsx index f799dc04..f7067503 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.supportBorderColorInfo}`, + background: BCDesignTokens.themeBlue20, + height: "24px", + width: "104px", + }, + 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 490064d3..337e6453 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 00000000..2b13ac0e --- /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/Proponents/RegistrationUrl/RegistrationUrl.tsx b/submit-web/src/components/App/Proponents/RegistrationUrl/RegistrationUrl.tsx index ea70c3b8..e70f76fa 100644 --- a/submit-web/src/components/App/Proponents/RegistrationUrl/RegistrationUrl.tsx +++ b/submit-web/src/components/App/Proponents/RegistrationUrl/RegistrationUrl.tsx @@ -40,7 +40,7 @@ export const RegistrationUrl = ({ const url = `${AppConfig.appUrl}/proponent/account-registration?token=${pendingInvitation?.token}`; const helperText = pendingInvitation ? pendingInvitation.is_expired - ? "This link has expired, You can renew the link by clicking the 'Renew Link' button" + ? "This link has expired, you can renew the link by clicking the 'Renew Link' button" : pendingInvitation.expiry_date ? "This link will expire on " + new Date(pendingInvitation.expiry_date).toISOString().split("T")[0] diff --git a/submit-web/src/components/App/SubmissionStatusChip/index.tsx b/submit-web/src/components/App/SubmissionStatusChip/index.tsx index 9af5f273..4e83d19b 100644 --- a/submit-web/src/components/App/SubmissionStatusChip/index.tsx +++ b/submit-web/src/components/App/SubmissionStatusChip/index.tsx @@ -228,6 +228,16 @@ const statusStyles: Record = { 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 6b6b9e0a..c1984057 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 ab7d727d..c57a23fb 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 c80f0767..39919634 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 cbaf8c13..c9d1f177 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 00000000..9aec376b --- /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 00000000..55e04175 --- /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; +}; From e0e32bc685f1686ed7c8a239c0e663d7196fada6 Mon Sep 17 00:00:00 2001 From: leodube-aot Date: Mon, 2 Mar 2026 16:23:19 -0800 Subject: [PATCH 3/3] Handle empty table values --- submit-web/src/components/App/PackageStatusChip/index.tsx | 6 +++--- .../App/Projects/ProjectTable/ProponentTableRow.tsx | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/submit-web/src/components/App/PackageStatusChip/index.tsx b/submit-web/src/components/App/PackageStatusChip/index.tsx index f7067503..ea14b80f 100644 --- a/submit-web/src/components/App/PackageStatusChip/index.tsx +++ b/submit-web/src/components/App/PackageStatusChip/index.tsx @@ -232,10 +232,10 @@ const statusStyles: Record< REQUESTED_BY_EAO: { sx: { borderRadius: 1, - border: `1px solid ${BCDesignTokens.supportBorderColorInfo}`, - background: BCDesignTokens.themeBlue20, + border: `1px solid ${BCDesignTokens.supportBorderColorWarning}`, + background: BCDesignTokens.supportSurfaceColorWarning, height: "24px", - width: "104px", + width: "139px", }, label: "Requested by EAO", }, diff --git a/submit-web/src/components/App/Projects/ProjectTable/ProponentTableRow.tsx b/submit-web/src/components/App/Projects/ProjectTable/ProponentTableRow.tsx index 10364321..7bb3e66f 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 ?? "-"}