diff --git a/src/dispatch/__init__.py b/src/dispatch/__init__.py
index 4f9fc2738f83..021559539f51 100644
--- a/src/dispatch/__init__.py
+++ b/src/dispatch/__init__.py
@@ -73,6 +73,7 @@
)
from dispatch.forms.type.models import FormsType # noqa lgtm[py/unused-import]
from dispatch.forms.models import Forms # noqa lgtm[py/unused-import]
+ from dispatch.email_templates.models import EmailTemplates # noqa lgtm[py/unused-import]
except Exception:
diff --git a/src/dispatch/api.py b/src/dispatch/api.py
index e532d9de2b03..27b47843ecab 100644
--- a/src/dispatch/api.py
+++ b/src/dispatch/api.py
@@ -41,6 +41,7 @@
from dispatch.project.views import router as project_router
from dispatch.forms.views import router as forms_router
from dispatch.forms.type.views import router as forms_type_router
+from dispatch.email_templates.views import router as email_template_router
from dispatch.signal.views import router as signal_router
@@ -233,7 +234,7 @@ def get_organization_path(organization: OrganizationSlug):
authenticated_organization_api_router.include_router(
forms_type_router, prefix="/forms_type", tags=["forms_type"]
)
-
+authenticated_organization_api_router.include_router(email_template_router, prefix="/email_template", tags=["email_template"])
@api_router.get("/healthcheck", include_in_schema=False)
def healthcheck():
diff --git a/src/dispatch/database/revisions/tenant/versions/2024-03-11_91bd05855ad1.py b/src/dispatch/database/revisions/tenant/versions/2024-03-11_91bd05855ad1.py
new file mode 100644
index 000000000000..0f70337b1937
--- /dev/null
+++ b/src/dispatch/database/revisions/tenant/versions/2024-03-11_91bd05855ad1.py
@@ -0,0 +1,41 @@
+"""Adds email template table
+
+Revision ID: 91bd05855ad1
+Revises: 27e6558e26a8
+Create Date: 2024-03-11 10:40:08.313520
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+# revision identifiers, used by Alembic.
+revision = "91bd05855ad1"
+down_revision = "27e6558e26a8"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table(
+ "email_templates",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("email_template_type", sa.String(), nullable=True),
+ sa.Column("welcome_text", sa.String(), nullable=True),
+ sa.Column("welcome_body", sa.String(), nullable=True),
+ sa.Column("components", sa.String(), nullable=True),
+ sa.Column("enabled", sa.Boolean(), nullable=True),
+ sa.Column("project_id", sa.Integer(), nullable=True),
+ sa.Column("created_at", sa.DateTime(), nullable=True),
+ sa.Column("updated_at", sa.DateTime(), nullable=True),
+ sa.ForeignKeyConstraint(["project_id"], ["project.id"], ondelete="CASCADE"),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("email_template_type", "project_id"),
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table("email_templates")
+ # ### end Alembic commands ###
diff --git a/src/dispatch/email_templates/enums.py b/src/dispatch/email_templates/enums.py
new file mode 100644
index 000000000000..16ad719a9612
--- /dev/null
+++ b/src/dispatch/email_templates/enums.py
@@ -0,0 +1,5 @@
+from dispatch.enums import DispatchEnum
+
+
+class EmailTemplateTypes(DispatchEnum):
+ welcome = "Incident Welcome Email"
diff --git a/src/dispatch/email_templates/models.py b/src/dispatch/email_templates/models.py
new file mode 100644
index 000000000000..5982676979b9
--- /dev/null
+++ b/src/dispatch/email_templates/models.py
@@ -0,0 +1,49 @@
+from datetime import datetime
+from pydantic import Field
+from typing import Optional, List
+
+from sqlalchemy import Column, Integer, String, Boolean, UniqueConstraint
+
+from dispatch.database.core import Base
+from dispatch.models import DispatchBase, TimeStampMixin, PrimaryKey, Pagination, ProjectMixin
+from dispatch.project.models import ProjectRead
+
+
+class EmailTemplates(TimeStampMixin, ProjectMixin, Base):
+ __table_args__ = (UniqueConstraint("email_template_type", "project_id"),)
+ # Columns
+ id = Column(Integer, primary_key=True)
+ email_template_type = Column(String, nullable=True)
+ welcome_text = Column(String, nullable=True)
+ welcome_body = Column(String, nullable=True)
+ components = Column(String, nullable=True)
+ enabled = Column(Boolean, default=True)
+
+
+# Pydantic models
+class EmailTemplatesBase(DispatchBase):
+ email_template_type: Optional[str] = Field(None, nullable=True)
+ welcome_text: Optional[str] = Field(None, nullable=True)
+ welcome_body: Optional[str] = Field(None, nullable=True)
+ components: Optional[str] = Field(None, nullable=True)
+ enabled: Optional[bool]
+
+
+class EmailTemplatesCreate(EmailTemplatesBase):
+ project: Optional[ProjectRead]
+
+
+class EmailTemplatesUpdate(EmailTemplatesBase):
+ id: PrimaryKey = None
+
+
+class EmailTemplatesRead(EmailTemplatesBase):
+ id: PrimaryKey
+ project: Optional[ProjectRead]
+ created_at: Optional[datetime] = None
+ updated_at: Optional[datetime] = None
+
+
+class EmailTemplatesPagination(Pagination):
+ items: List[EmailTemplatesRead]
+ total: int
diff --git a/src/dispatch/email_templates/service.py b/src/dispatch/email_templates/service.py
new file mode 100644
index 000000000000..279834939e73
--- /dev/null
+++ b/src/dispatch/email_templates/service.py
@@ -0,0 +1,78 @@
+import logging
+from typing import List, Optional
+
+from sqlalchemy.orm import Session
+
+from .models import EmailTemplates, EmailTemplatesUpdate, EmailTemplatesCreate
+from dispatch.project import service as project_service
+
+log = logging.getLogger(__name__)
+
+
+def get(*, email_template_id: int, db_session: Session) -> Optional[EmailTemplates]:
+ """Gets an email template by its id."""
+ return (
+ db_session.query(EmailTemplates)
+ .filter(EmailTemplates.id == email_template_id)
+ .one_or_none()
+ )
+
+
+def get_by_type(*, email_template_type: str, project_id: int, db_session: Session) -> Optional[EmailTemplates]:
+ """Gets an email template by its type."""
+ return (
+ db_session.query(EmailTemplates)
+ .filter(EmailTemplates.project_id == project_id)
+ .filter(EmailTemplates.email_template_type == email_template_type)
+ .filter(EmailTemplates.enabled == True) # noqa
+ .first()
+ )
+
+
+def get_all(*, db_session: Session) -> List[Optional[EmailTemplates]]:
+ """Gets all email templates."""
+ return db_session.query(EmailTemplates)
+
+
+def create(*, email_template_in: EmailTemplatesCreate, db_session: Session) -> EmailTemplates:
+ """Creates email template data."""
+ project = project_service.get_by_name_or_raise(
+ db_session=db_session, project_in=email_template_in.project
+ )
+
+ email_template = EmailTemplates(
+ **email_template_in.dict(exclude={"project"}), project=project
+ )
+
+ db_session.add(email_template)
+ db_session.commit()
+ return email_template
+
+
+def update(
+ *,
+ email_template: EmailTemplates,
+ email_template_in: EmailTemplatesUpdate,
+ db_session: Session,
+) -> EmailTemplates:
+ """Updates an email template."""
+ new_template = email_template.dict()
+ update_data = email_template_in.dict(skip_defaults=True)
+
+ for field in new_template:
+ if field in update_data:
+ setattr(email_template, field, update_data[field])
+
+ db_session.commit()
+ return email_template
+
+
+def delete(*, db_session, email_template_id: int):
+ """Deletes an email template."""
+ email_template = (
+ db_session.query(EmailTemplates)
+ .filter(EmailTemplates.id == email_template_id)
+ .one_or_none()
+ )
+ db_session.delete(email_template)
+ db_session.commit()
diff --git a/src/dispatch/email_templates/views.py b/src/dispatch/email_templates/views.py
new file mode 100644
index 000000000000..5bc4abb3099f
--- /dev/null
+++ b/src/dispatch/email_templates/views.py
@@ -0,0 +1,110 @@
+import logging
+from fastapi import APIRouter, HTTPException, status, Depends
+from pydantic.error_wrappers import ErrorWrapper, ValidationError
+
+from sqlalchemy.exc import IntegrityError
+
+from dispatch.auth.permissions import (
+ SensitiveProjectActionPermission,
+ PermissionsDependency,
+)
+from dispatch.database.core import DbSession
+from dispatch.auth.service import CurrentUser
+from dispatch.database.service import search_filter_sort_paginate, CommonParameters
+from dispatch.models import PrimaryKey
+from dispatch.exceptions import ExistsError
+
+from .models import EmailTemplatesRead, EmailTemplatesUpdate, EmailTemplatesPagination, EmailTemplatesCreate
+from .service import get, create, update, delete
+
+log = logging.getLogger(__name__)
+router = APIRouter()
+
+
+@router.get("", response_model=EmailTemplatesPagination)
+def get_email_templates(commons: CommonParameters):
+ """Get all email templates, or only those matching a given search term."""
+ return search_filter_sort_paginate(model="EmailTemplates", **commons)
+
+
+@router.get("/{email_template_id}", response_model=EmailTemplatesRead)
+def get_email_template(db_session: DbSession, email_template_id: PrimaryKey):
+ """Get an email template by its id."""
+ email_template = get(db_session=db_session, email_template_id=email_template_id)
+ if not email_template:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=[{"msg": "An email template with this id does not exist."}],
+ )
+ return email_template
+
+
+@router.post("", response_model=EmailTemplatesRead)
+def create_email_template(
+ db_session: DbSession,
+ email_template_in: EmailTemplatesCreate,
+ current_user: CurrentUser,
+):
+ """Create a new email template."""
+ try:
+ return create(
+ db_session=db_session, email_template_in=email_template_in
+ )
+ except IntegrityError:
+ raise ValidationError(
+ [
+ ErrorWrapper(
+ ExistsError(msg="An email template with this type already exists."), loc="name"
+ )
+ ],
+ model=EmailTemplatesRead,
+ ) from None
+
+
+@router.put(
+ "/{email_template_id}",
+ response_model=EmailTemplatesRead,
+ dependencies=[Depends(PermissionsDependency([SensitiveProjectActionPermission]))],
+)
+def update_email_template(
+ db_session: DbSession,
+ email_template_id: PrimaryKey,
+ email_template_in: EmailTemplatesUpdate,
+):
+ """Update a search filter."""
+ email_template = get(db_session=db_session, email_template_id=email_template_id)
+ if not email_template:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=[{"msg": "An email template with this id does not exist."}],
+ )
+ try:
+ email_template = update(
+ db_session=db_session, email_template=email_template, email_template_in=email_template_in
+ )
+ except IntegrityError:
+ raise ValidationError(
+ [
+ ErrorWrapper(
+ ExistsError(msg="An email template with this type already exists."), loc="name"
+ )
+ ],
+ model=EmailTemplatesUpdate,
+ ) from None
+ return email_template
+
+
+@router.delete(
+ "/{email_template_id}",
+ response_model=None,
+ dependencies=[Depends(PermissionsDependency([SensitiveProjectActionPermission]))],
+)
+def delete_email_template(db_session: DbSession, email_template_id: PrimaryKey):
+ """Delete an email template, returning only an HTTP 200 OK if successful."""
+ email_template = get(db_session=db_session, email_template_id=email_template_id)
+ if not email_template:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=[{"msg": "An email template with this id does not exist."}],
+ )
+ delete(db_session=db_session, email_template_id=email_template_id)
diff --git a/src/dispatch/incident/messaging.py b/src/dispatch/incident/messaging.py
index 19be4b0f1b87..6c862712abc3 100644
--- a/src/dispatch/incident/messaging.py
+++ b/src/dispatch/incident/messaging.py
@@ -4,13 +4,19 @@
:copyright: (c) 2019 by Netflix Inc., see AUTHORS for more
:license: Apache, see LICENSE for more details.
"""
+
import logging
+from typing import Optional
+
from dispatch.decorators import timer
from dispatch.config import DISPATCH_UI_URL
from dispatch.conversation.enums import ConversationCommands
from dispatch.database.core import SessionLocal, resolve_attr
from dispatch.document import service as document_service
+from dispatch.email_templates.models import EmailTemplates
+from dispatch.email_templates import service as email_template_service
+from dispatch.email_templates.enums import EmailTemplateTypes
from dispatch.event import service as event_service
from dispatch.incident.enums import IncidentStatus
from dispatch.incident.models import Incident, IncidentRead
@@ -30,7 +36,6 @@
INCIDENT_NOTIFICATION_COMMON,
INCIDENT_OPEN_TASKS,
INCIDENT_PARTICIPANT_SUGGESTED_READING_ITEM,
- INCIDENT_PARTICIPANT_WELCOME_MESSAGE,
INCIDENT_PRIORITY_CHANGE,
INCIDENT_REVIEW_DOCUMENT,
INCIDENT_SEVERITY_CHANGE,
@@ -38,6 +43,7 @@
INCIDENT_TYPE_CHANGE,
INCIDENT_COMPLETED_FORM_MESSAGE,
MessageType,
+ generate_welcome_message,
)
from dispatch.participant import service as participant_service
from dispatch.participant_role import service as participant_role_service
@@ -67,7 +73,11 @@ def get_suggested_documents(db_session, incident: Incident) -> list:
def send_welcome_ephemeral_message_to_participant(
- participant_email: str, incident: Incident, db_session: SessionLocal
+ *,
+ participant_email: str,
+ incident: Incident,
+ db_session: SessionLocal,
+ welcome_template: Optional[EmailTemplates] = None,
):
"""Sends an ephemeral welcome message to the participant."""
if not incident.conversation:
@@ -135,7 +145,7 @@ def send_welcome_ephemeral_message_to_participant(
incident.conversation.channel_id,
participant_email,
"Incident Welcome Message",
- INCIDENT_PARTICIPANT_WELCOME_MESSAGE,
+ generate_welcome_message(welcome_template),
MessageType.incident_participant_welcome,
**message_kwargs,
)
@@ -144,7 +154,11 @@ def send_welcome_ephemeral_message_to_participant(
def send_welcome_email_to_participant(
- participant_email: str, incident: Incident, db_session: SessionLocal
+ *,
+ participant_email: str,
+ incident: Incident,
+ db_session: SessionLocal,
+ welcome_template: Optional[EmailTemplates] = None,
):
"""Sends a welcome email to the participant."""
# we load the incident instance
@@ -209,7 +223,7 @@ def send_welcome_email_to_participant(
plugin.instance.send(
participant_email,
notification_text,
- INCIDENT_PARTICIPANT_WELCOME_MESSAGE,
+ generate_welcome_message(welcome_template),
MessageType.incident_participant_welcome,
**message_kwargs,
)
@@ -219,9 +233,7 @@ def send_welcome_email_to_participant(
log.debug(f"Welcome email sent to {participant_email}.")
-def send_completed_form_email(
- participant_email: str, form: Forms, db_session: SessionLocal
-):
+def send_completed_form_email(participant_email: str, form: Forms, db_session: SessionLocal):
"""Sends an email to notify about a completed incident form."""
plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=form.project.id, plugin_type="email"
@@ -270,14 +282,33 @@ def send_completed_form_email(
@timer
def send_incident_welcome_participant_messages(
- participant_email: str, incident: Incident, db_session: SessionLocal
+ participant_email: str,
+ incident: Incident,
+ db_session: SessionLocal,
):
"""Sends welcome messages to the participant."""
+ # check to see if there is an override welcome message template
+ welcome_template = email_template_service.get_by_type(
+ db_session=db_session,
+ project_id=incident.project_id,
+ email_template_type=EmailTemplateTypes.welcome,
+ )
+
# we send the welcome ephemeral message
- send_welcome_ephemeral_message_to_participant(participant_email, incident, db_session)
+ send_welcome_ephemeral_message_to_participant(
+ participant_email=participant_email,
+ incident=incident,
+ db_session=db_session,
+ welcome_template=welcome_template,
+ )
# we send the welcome email
- send_welcome_email_to_participant(participant_email, incident, db_session)
+ send_welcome_email_to_participant(
+ participant_email=participant_email,
+ incident=incident,
+ db_session=db_session,
+ welcome_template=welcome_template,
+ )
log.debug(f"Welcome participant messages sent {participant_email}.")
diff --git a/src/dispatch/messaging/strings.py b/src/dispatch/messaging/strings.py
index 4eea22d27074..4dad13923673 100644
--- a/src/dispatch/messaging/strings.py
+++ b/src/dispatch/messaging/strings.py
@@ -1,12 +1,13 @@
import copy
-from typing import List
+from typing import List, Optional
from dispatch.messaging.email.filters import env
from dispatch.conversation.enums import ConversationButtonActions
from dispatch.incident.enums import IncidentStatus
from dispatch.case.enums import CaseStatus
from dispatch.enums import Visibility
+from dispatch.email_templates.models import EmailTemplates
from dispatch import config
from dispatch.enums import DispatchEnum, DocumentResourceTypes, DocumentResourceReferenceTypes
@@ -915,3 +916,41 @@ def render_message_template(message_template: List[dict], **kwargs):
data.append(d)
return data
+
+
+def generate_welcome_message(welcome_message: EmailTemplates) -> Optional[List[dict]]:
+ """Generates the welcome message."""
+ if welcome_message is None:
+ return INCIDENT_PARTICIPANT_WELCOME_MESSAGE
+
+ participant_welcome = {
+ "title": welcome_message.welcome_text,
+ "title_link": "{{ticket_weblink}}",
+ "text": welcome_message.welcome_body,
+ }
+
+ component_mapping = {
+ "Title": INCIDENT_TITLE,
+ "Description": INCIDENT_DESCRIPTION,
+ "Visibility": INCIDENT_VISIBILITY,
+ "Status": INCIDENT_STATUS,
+ "Type": INCIDENT_TYPE,
+ "Severity": INCIDENT_SEVERITY,
+ "Priority": INCIDENT_PRIORITY,
+ "Reporter": INCIDENT_REPORTER,
+ "Commander": INCIDENT_COMMANDER,
+ "Investigation Document": INCIDENT_INVESTIGATION_DOCUMENT,
+ "Storage": INCIDENT_STORAGE,
+ "Conference": INCIDENT_CONFERENCE,
+ "Slack Commands": INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT,
+ "FAQ Document": INCIDENT_FAQ_DOCUMENT,
+ }
+
+ message = [participant_welcome]
+
+ for component in component_mapping.keys():
+ # if the component type is in welcome_message.components, then add it
+ if component in welcome_message.components:
+ message.append(component_mapping[component])
+
+ return message
diff --git a/src/dispatch/static/dispatch/src/email_templates/DeleteDialog.vue b/src/dispatch/static/dispatch/src/email_templates/DeleteDialog.vue
new file mode 100644
index 000000000000..86210e31e225
--- /dev/null
+++ b/src/dispatch/static/dispatch/src/email_templates/DeleteDialog.vue
@@ -0,0 +1,35 @@
+
+
+
+
+ Delete Email Template?
+
+
+ Are you sure you would like to delete this email template?
+
+
+
+ Cancel
+ Delete
+
+
+
+
+
+
diff --git a/src/dispatch/static/dispatch/src/email_templates/NewEditSheet.vue b/src/dispatch/static/dispatch/src/email_templates/NewEditSheet.vue
new file mode 100644
index 000000000000..883a1522c408
--- /dev/null
+++ b/src/dispatch/static/dispatch/src/email_templates/NewEditSheet.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+ Edit
+ New
+ Email Template
+
+
+
+ mdi-content-save
+
+
+ mdi-close
+
+
+
+
+
+
+
+
+
+ Details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/dispatch/static/dispatch/src/email_templates/Table.vue b/src/dispatch/static/dispatch/src/email_templates/Table.vue
new file mode 100644
index 000000000000..456f8549816b
--- /dev/null
+++ b/src/dispatch/static/dispatch/src/email_templates/Table.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+ New
+
+
+
+
+
+ Email templates are used to modify the default templates sent to participants.
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+ {{ formatRelativeDate(item.created_at) }}
+
+ {{ formatDate(item.created_at) }}
+
+
+
+
+
+ {{ formatRelativeDate(value) }}
+
+ {{ formatDate(value) }}
+
+
+
+
+
+
+ mdi-dots-vertical
+
+
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/dispatch/static/dispatch/src/email_templates/api.js b/src/dispatch/static/dispatch/src/email_templates/api.js
new file mode 100644
index 000000000000..f00c4f82448f
--- /dev/null
+++ b/src/dispatch/static/dispatch/src/email_templates/api.js
@@ -0,0 +1,25 @@
+import API from "@/api"
+
+const resource = "/email_template"
+
+export default {
+ getAll(options) {
+ return API.get(`${resource}`, { params: { ...options } })
+ },
+
+ get(emailTemplateId) {
+ return API.get(`${resource}/${emailTemplateId}`)
+ },
+
+ create(payload) {
+ return API.post(`${resource}`, payload)
+ },
+
+ update(emailTemplateId, payload) {
+ return API.put(`${resource}/${emailTemplateId}`, payload)
+ },
+
+ delete(emailTemplateId) {
+ return API.delete(`${resource}/${emailTemplateId}`)
+ },
+}
diff --git a/src/dispatch/static/dispatch/src/email_templates/store.js b/src/dispatch/static/dispatch/src/email_templates/store.js
new file mode 100644
index 000000000000..cc56d5847494
--- /dev/null
+++ b/src/dispatch/static/dispatch/src/email_templates/store.js
@@ -0,0 +1,168 @@
+import { getField, updateField } from "vuex-map-fields"
+import { debounce } from "lodash"
+
+import SearchUtils from "@/search/utils"
+import EmailTemplatesApi from "@/email_templates/api"
+
+const getDefaultSelectedState = () => {
+ return {
+ id: null,
+ email_template_type: null,
+ welcome_text: null,
+ welcome_body: null,
+ components: "[]",
+ enabled: true,
+ project: null,
+ created_at: null,
+ updated_at: null,
+ loading: false,
+ }
+}
+
+const state = {
+ selected: {
+ ...getDefaultSelectedState(),
+ },
+ dialogs: {
+ showCreateEdit: false,
+ showRemove: false,
+ },
+ table: {
+ rows: {
+ items: [],
+ total: null,
+ },
+ options: {
+ q: "",
+ page: 1,
+ itemsPerPage: 25,
+ sortBy: ["email_template_type"],
+ descending: [false],
+ filters: {
+ project: [],
+ },
+ },
+ loading: false,
+ },
+}
+
+const getters = {
+ getField,
+}
+
+const actions = {
+ getAll: debounce(({ commit, state }) => {
+ commit("SET_TABLE_LOADING", "primary")
+ let params = SearchUtils.createParametersFromTableOptions(
+ { ...state.table.options },
+ "EmailTemplates"
+ )
+ return EmailTemplatesApi.getAll(params)
+ .then((response) => {
+ commit("SET_TABLE_LOADING", false)
+ commit("SET_TABLE_ROWS", response.data)
+ })
+ .catch(() => {
+ commit("SET_TABLE_LOADING", false)
+ })
+ }, 500),
+ createEditShow({ commit }, emailTemplate) {
+ commit("SET_DIALOG_CREATE_EDIT", true)
+ if (emailTemplate) {
+ commit("SET_SELECTED", emailTemplate)
+ }
+ },
+ removeShow({ commit }, emailTemplate) {
+ commit("SET_DIALOG_DELETE", true)
+ commit("SET_SELECTED", emailTemplate)
+ },
+ closeCreateEdit({ commit }) {
+ commit("SET_DIALOG_CREATE_EDIT", false)
+ commit("RESET_SELECTED")
+ },
+ closeRemove({ commit }) {
+ commit("SET_DIALOG_DELETE", false)
+ commit("RESET_SELECTED")
+ },
+ save({ commit, dispatch }) {
+ commit("SET_SELECTED_LOADING", true)
+ if (!state.selected.id) {
+ return EmailTemplatesApi.create(state.selected)
+ .then(() => {
+ commit("SET_SELECTED_LOADING", false)
+ dispatch("closeCreateEdit")
+ dispatch("getAll")
+ commit(
+ "notification_backend/addBeNotification",
+ { text: "Email template created successfully.", type: "success" },
+ { root: true }
+ )
+ })
+ .catch(() => {
+ commit("SET_SELECTED_LOADING", false)
+ })
+ } else {
+ return EmailTemplatesApi.update(state.selected.id, state.selected)
+ .then(() => {
+ commit("SET_SELECTED_LOADING", false)
+ dispatch("closeCreateEdit")
+ dispatch("getAll")
+ commit(
+ "notification_backend/addBeNotification",
+ { text: "Email template updated successfully.", type: "success" },
+ { root: true }
+ )
+ })
+ .catch(() => {
+ commit("SET_SELECTED_LOADING", false)
+ })
+ }
+ },
+ remove({ commit, dispatch }) {
+ return EmailTemplatesApi.delete(state.selected.id).then(function () {
+ dispatch("closeRemove")
+ dispatch("getAll")
+ commit(
+ "notification_backend/addBeNotification",
+ { text: "Email template deleted successfully.", type: "success" },
+ { root: true }
+ )
+ })
+ },
+}
+
+const mutations = {
+ updateField,
+ SET_SELECTED(state, value) {
+ state.selected = Object.assign(state.selected, value)
+ },
+ SET_SELECTED_LOADING(state, value) {
+ state.selected.loading = value
+ },
+ SET_TABLE_LOADING(state, value) {
+ state.table.loading = value
+ },
+ SET_TABLE_ROWS(state, value) {
+ state.table.rows = value
+ },
+ SET_DIALOG_CREATE_EDIT(state, value) {
+ state.dialogs.showCreateEdit = value
+ },
+ SET_DIALOG_DELETE(state, value) {
+ state.dialogs.showRemove = value
+ },
+ RESET_SELECTED(state) {
+ // do not reset project
+ let project = state.selected.project
+ state.selected = { ...getDefaultSelectedState() }
+ state.selected.project = project
+ },
+}
+
+export default {
+ namespaced: true,
+ state,
+ getters,
+ actions,
+ mutations,
+}
diff --git a/src/dispatch/static/dispatch/src/router/config.js b/src/dispatch/static/dispatch/src/router/config.js
index 17758925e111..2c502728e49a 100644
--- a/src/dispatch/static/dispatch/src/router/config.js
+++ b/src/dispatch/static/dispatch/src/router/config.js
@@ -433,6 +433,12 @@ export const protectedRoute = [
meta: { title: "Cost Models", subMenu: "project", group: "general" },
component: () => import("@/cost_model/Table.vue"),
},
+ {
+ path: "emailTemplates",
+ name: "emailTemplatesTable",
+ meta: { title: "Email Templates", subMenu: "project", group: "general" },
+ component: () => import("@/email_templates/Table.vue"),
+ },
{
path: "incidentTypes",
name: "IncidentTypeTable",
diff --git a/src/dispatch/static/dispatch/src/store.js b/src/dispatch/static/dispatch/src/store.js
index 1aebfaf51ce1..55c4339952af 100644
--- a/src/dispatch/static/dispatch/src/store.js
+++ b/src/dispatch/static/dispatch/src/store.js
@@ -9,6 +9,7 @@ import case_type from "@/case/type/store"
import cost_model from "@/cost_model/store"
import definition from "@/definition/store"
import document from "@/document/store"
+import email_templates from "@/email_templates/store"
import entity from "@/entity/store"
import entity_type from "@/entity_type/store"
import forms from "@/forms/store"
@@ -60,6 +61,7 @@ export default createStore({
case_type,
definition,
document,
+ email_templates,
entity,
entity_type,
forms,