From abfa2bc2ba790182350e45856893aca624b8d1e7 Mon Sep 17 00:00:00 2001 From: David Whittaker Date: Fri, 13 Dec 2024 10:40:23 -0800 Subject: [PATCH 1/4] feat(ui): allow project to have a display name separate from its key --- src/dispatch/case/models.py | 1 + .../versions/2024-12-12_2d9e4d392ea4.py | 33 +++++++++++++++++++ src/dispatch/incident/models.py | 1 + src/dispatch/incident/priority/models.py | 1 + .../dispatch_slack/case/interactive.py | 10 +++--- src/dispatch/plugins/dispatch_slack/fields.py | 2 +- .../dispatch_slack/incident/interactive.py | 18 ++-------- src/dispatch/project/models.py | 3 ++ .../dispatch/src/case/CaseSummaryTable.vue | 4 +-- .../dispatch/src/case/ReportReceiptCard.vue | 2 +- .../src/case/ReportSubmissionCard.vue | 2 +- .../static/dispatch/src/case/Table.vue | 6 ++-- .../case/priority/CasePriorityCombobox.vue | 4 +-- .../case/severity/CaseSeverityCombobox.vue | 4 +-- .../src/case/type/CaseTypeCombobox.vue | 4 +-- .../incident/IncidentDrillDownTable.vue | 4 +-- .../src/data/query/QuerySummaryTable.vue | 4 +-- .../static/dispatch/src/data/query/Table.vue | 6 ++-- .../src/data/source/SourceSummaryTable.vue | 4 +-- .../static/dispatch/src/data/source/Table.vue | 6 ++-- .../src/document/DocumentSummaryTable.vue | 4 +-- .../dispatch/src/feedback/incident/Table.vue | 6 ++-- .../dispatch/src/feedback/service/Table.vue | 6 ++-- .../static/dispatch/src/forms/table/Table.vue | 2 +- .../src/incident/IncidentSummaryTable.vue | 4 +-- .../src/incident/ReportReceiptCard.vue | 2 +- .../static/dispatch/src/incident/Table.vue | 4 +-- .../priority/IncidentPriorityCombobox.vue | 4 +-- .../severity/IncidentSeverityCombobox.vue | 4 +-- .../incident/type/IncidentTypeCombobox.vue | 4 +-- .../src/incident/type/IncidentTypeSelect.vue | 2 +- .../dispatch/src/project/NewEditSheet.vue | 18 ++++++++-- .../dispatch/src/project/ProjectCombobox.vue | 4 +-- .../dispatch/src/project/ProjectSelect.vue | 7 ++-- .../static/dispatch/src/project/Table.vue | 3 +- .../static/dispatch/src/signal/Table.vue | 4 +-- .../dispatch/src/signal/TableInstance.vue | 4 +-- .../src/tag/TagFilterAutoComplete.vue | 7 ++-- .../dispatch/src/tag/TagSummaryTable.vue | 4 +-- .../src/tag_type/TagTypeFilterCombobox.vue | 6 ++-- .../static/dispatch/src/task/Table.vue | 6 ++-- .../dispatch/src/task/TaskSummaryTable.vue | 4 +-- 42 files changed, 135 insertions(+), 93 deletions(-) create mode 100644 src/dispatch/database/revisions/tenant/versions/2024-12-12_2d9e4d392ea4.py diff --git a/src/dispatch/case/models.py b/src/dispatch/case/models.py index 7405bd194282..f227d8f6430d 100644 --- a/src/dispatch/case/models.py +++ b/src/dispatch/case/models.py @@ -230,6 +230,7 @@ class SignalInstanceRead(DispatchBase): class ProjectRead(DispatchBase): id: Optional[PrimaryKey] name: NameStr + display_name: Optional[str] color: Optional[str] allow_self_join: Optional[bool] = Field(True, nullable=True) diff --git a/src/dispatch/database/revisions/tenant/versions/2024-12-12_2d9e4d392ea4.py b/src/dispatch/database/revisions/tenant/versions/2024-12-12_2d9e4d392ea4.py new file mode 100644 index 000000000000..f54a1fadb877 --- /dev/null +++ b/src/dispatch/database/revisions/tenant/versions/2024-12-12_2d9e4d392ea4.py @@ -0,0 +1,33 @@ +"""Adding display name to the projct model + +Revision ID: 2d9e4d392ea4 +Revises: 575ca7d954a8 +Create Date: 2024-12-12 16:34:58.098426 + +""" + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "2d9e4d392ea4" +down_revision = "575ca7d954a8" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("project", sa.Column("display_name", sa.String(), nullable=True)) + + # Copy data from 'name' column to 'display_name' column + op.execute("UPDATE project SET display_name = name") + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("project", "display_name") + # ### end Alembic commands ### diff --git a/src/dispatch/incident/models.py b/src/dispatch/incident/models.py index d5e727d6ecd6..4094ee820550 100644 --- a/src/dispatch/incident/models.py +++ b/src/dispatch/incident/models.py @@ -242,6 +242,7 @@ class ProjectRead(DispatchBase): color: Optional[str] stable_priority: Optional[IncidentPriorityRead] = None allow_self_join: Optional[bool] = Field(True, nullable=True) + display_name: Optional[str] = Field(None, nullable=True) class CaseRead(DispatchBase): diff --git a/src/dispatch/incident/priority/models.py b/src/dispatch/incident/priority/models.py index 4920201e7281..da088f6993d2 100644 --- a/src/dispatch/incident/priority/models.py +++ b/src/dispatch/incident/priority/models.py @@ -38,6 +38,7 @@ class IncidentPriority(Base, ProjectMixin): class ProjectRead(DispatchBase): id: Optional[PrimaryKey] name: NameStr + display_name: Optional[str] # Pydantic models... diff --git a/src/dispatch/plugins/dispatch_slack/case/interactive.py b/src/dispatch/plugins/dispatch_slack/case/interactive.py index 5343a4b51b8c..6708b06ca037 100644 --- a/src/dispatch/plugins/dispatch_slack/case/interactive.py +++ b/src/dispatch/plugins/dispatch_slack/case/interactive.py @@ -184,7 +184,7 @@ def handle_escalate_case_command( default_title = case.name default_description = case.description - default_project = {"text": case.project.name, "value": case.project.id} + default_project = {"text": case.project.display_name, "value": case.project.id} blocks = [ Context(elements=[MarkdownText(text="Accept the defaults or adjust as needed.")]), @@ -1303,7 +1303,7 @@ def escalate_button_click( description_input(initial_value=case.description), project_select( db_session=db_session, - initial_option={"text": case.project.name, "value": case.project.id}, + initial_option={"text": case.project.display_name, "value": case.project.id}, action_id=CaseEscalateActions.project_select, dispatch_action=True, ), @@ -1358,7 +1358,7 @@ def handle_project_select_action( description_input(), project_select( db_session=db_session, - initial_option={"text": project.name, "value": project.id}, + initial_option={"text": project.display_name, "value": project.id}, action_id=CaseEscalateActions.project_select, dispatch_action=True, ), @@ -2138,7 +2138,7 @@ def handle_report_project_select_action( description_input(), project_select( db_session=db_session, - initial_option={"text": project.name, "value": project.id}, + initial_option={"text": project.display_name, "value": project.id}, action_id=CaseReportActions.project_select, dispatch_action=True, ), @@ -2243,7 +2243,7 @@ def handle_report_case_type_select_action( description_input(), project_select( db_session=db_session, - initial_option={"text": project.name, "value": project.id}, + initial_option={"text": project.display_name, "value": project.id}, action_id=CaseReportActions.project_select, dispatch_action=True, ), diff --git a/src/dispatch/plugins/dispatch_slack/fields.py b/src/dispatch/plugins/dispatch_slack/fields.py index 2c4541db4c12..2dc9c956c542 100644 --- a/src/dispatch/plugins/dispatch_slack/fields.py +++ b/src/dispatch/plugins/dispatch_slack/fields.py @@ -301,7 +301,7 @@ def project_select( ): """Creates a project select.""" projects = [ - {"text": p.name, "value": p.id} + {"text": p.display_name, "value": p.id} for p in project_service.get_all(db_session=db_session) if p.enabled ] diff --git a/src/dispatch/plugins/dispatch_slack/incident/interactive.py b/src/dispatch/plugins/dispatch_slack/incident/interactive.py index a27253e04600..33068ffda671 100644 --- a/src/dispatch/plugins/dispatch_slack/incident/interactive.py +++ b/src/dispatch/plugins/dispatch_slack/incident/interactive.py @@ -309,7 +309,7 @@ def handle_update_incident_project_select_action( incident_status_select(initial_option={"text": incident.status, "value": incident.status}), project_select( db_session=db_session, - initial_option={"text": project.name, "value": project.id}, + initial_option={"text": project.display_name, "value": project.id}, action_id=IncidentUpdateActions.project_select, dispatch_action=True, ), @@ -2114,20 +2114,8 @@ def handle_update_incident_command( description_input(initial_value=incident.description), resolution_input(initial_value=incident.resolution), incident_status_select(initial_option={"text": incident.status, "value": incident.status}), - project_select( - db_session=db_session, - initial_option={"text": incident.project.name, "value": incident.project.id}, - action_id=IncidentUpdateActions.project_select, - dispatch_action=True, - ), - incident_type_select( - db_session=db_session, - initial_option={ - "text": incident.incident_type.name, - "value": incident.incident_type.id, - }, - project_id=incident.project.id, - ), + Section(text=f"*Project*: {incident.project.display_name}"), + Context(elements=[MarkdownText(text="Project is read-only")]), incident_severity_select( db_session=db_session, initial_option={ diff --git a/src/dispatch/project/models.py b/src/dispatch/project/models.py index b6620c8a7fe0..b50210e977b4 100644 --- a/src/dispatch/project/models.py +++ b/src/dispatch/project/models.py @@ -40,6 +40,8 @@ class Project(Base): cascade="all, delete-orphan", ) + display_name = Column(String, nullable=True) + enabled = Column(Boolean, default=True, server_default="t") allow_self_join = Column(Boolean, default=True, server_default="t") @@ -82,6 +84,7 @@ def slug(self): class ProjectBase(DispatchBase): id: Optional[PrimaryKey] name: NameStr + display_name: Optional[str] = Field(None, nullable=True) owner_email: Optional[EmailStr] = Field(None, nullable=True) owner_conversation: Optional[str] = Field(None, nullable=True) annual_employee_cost: Optional[int] diff --git a/src/dispatch/static/dispatch/src/case/CaseSummaryTable.vue b/src/dispatch/static/dispatch/src/case/CaseSummaryTable.vue index e6a186679bca..d0a780ba702b 100644 --- a/src/dispatch/static/dispatch/src/case/CaseSummaryTable.vue +++ b/src/dispatch/static/dispatch/src/case/CaseSummaryTable.vue @@ -6,7 +6,7 @@ -