diff --git a/Common/Models/DatabaseModels/StatusPageAnnouncement.ts b/Common/Models/DatabaseModels/StatusPageAnnouncement.ts index 4a3af52073..e53ebd5a19 100644 --- a/Common/Models/DatabaseModels/StatusPageAnnouncement.ts +++ b/Common/Models/DatabaseModels/StatusPageAnnouncement.ts @@ -164,7 +164,12 @@ export default class StatusPageAnnouncement extends BaseModel { Permission.ProjectMember, Permission.ReadStatusPageAnnouncement, ], - update: [], + update: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.EditStatusPageAnnouncement, + ], }) @TableColumn({ required: false, diff --git a/Common/UI/Components/ModelTable/BaseModelTable.tsx b/Common/UI/Components/ModelTable/BaseModelTable.tsx index 7b3807d047..a9b1ffa616 100644 --- a/Common/UI/Components/ModelTable/BaseModelTable.tsx +++ b/Common/UI/Components/ModelTable/BaseModelTable.tsx @@ -30,7 +30,7 @@ import Field from "../Detail/Field"; import ErrorMessage from "../ErrorMessage/ErrorMessage"; import ClassicFilterType from "../Filters/Types/Filter"; import FilterData from "../Filters/Types/FilterData"; -import { FormProps } from "../Forms/BasicForm"; +import { FormProps, FormSummaryConfig } from "../Forms/BasicForm"; import { ModelField } from "../Forms/ModelForm"; import { FormStep } from "../Forms/Types/FormStep"; import FormValues from "../Forms/Types/FormValues"; @@ -215,6 +215,8 @@ export interface BaseTableProps< initialFilterData?: FilterData | undefined; saveFilterProps?: SaveFilterProps | undefined; + + formSummary?: FormSummaryConfig | undefined; } export interface ComponentProps< diff --git a/Common/UI/Components/ModelTable/ModelTable.tsx b/Common/UI/Components/ModelTable/ModelTable.tsx index 65829f6637..6fbedbbd7c 100644 --- a/Common/UI/Components/ModelTable/ModelTable.tsx +++ b/Common/UI/Components/ModelTable/ModelTable.tsx @@ -142,6 +142,7 @@ const ModelTable: ( onBeforeCreate={onBeforeCreate} modelType={props.modelType} formProps={{ + summary: props.formSummary, name: `create-${props.modelType.name}-from`, modelType: props.modelType, id: `create-${props.modelType.name}-from`, diff --git a/Dashboard/src/Components/Announcement/AnnouncementsTable.tsx b/Dashboard/src/Components/Announcement/AnnouncementsTable.tsx new file mode 100644 index 0000000000..f56966ca23 --- /dev/null +++ b/Dashboard/src/Components/Announcement/AnnouncementsTable.tsx @@ -0,0 +1,276 @@ +import DashboardNavigation from "../../Utils/Navigation"; +import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType"; +import ModelTable from "Common/UI/Components/ModelTable/ModelTable"; +import FieldType from "Common/UI/Components/Types/FieldType"; +import Navigation from "Common/UI/Utils/Navigation"; +import StatusPageAnnouncement from "Common/Models/DatabaseModels/StatusPageAnnouncement"; +import React, { Fragment, FunctionComponent, ReactElement } from "react"; +import Query from "Common/Types/BaseDatabase/Query"; +import StatusPage from "Common/Models/DatabaseModels/StatusPage"; +import FormValues from "Common/UI/Components/Forms/Types/FormValues"; +import ObjectID from "Common/Types/ObjectID"; +import FetchStatusPages from "../StatusPage/FetchStatusPages"; +import StatusPagesElement from "../StatusPage/StatusPagesElement"; +import { ModalWidth } from "Common/UI/Components/Modal/Modal"; + + +export interface ComponentProps { + query?: Query | undefined; + initialValues?: FormValues | undefined; + title?: string; + description?: string; +} + +const AnnouncementTable: FunctionComponent = ( + props: ComponentProps, +): ReactElement => { + return ( + + + modelType={StatusPageAnnouncement} + id="table-status-page-note" + isDeleteable={true} + isCreateable={true} + showViewIdButton={true} + isEditable={true} + name="Status Page > Announcements" + isViewable={false} + createInitialValues={props.initialValues} + query={{ + ...(props.query || {}), + projectId: DashboardNavigation.getProjectId()!, + }} + cardProps={{ + title: props.title || "Status Page Announcements", + description: + props.description || + "Create and manage announcements that will be shown on status pages.", + }} + noItemsMessage={"No announcements found."} + formSummary={{ + enabled: true + }} + formSteps={[ + { + title: "Basic", + id: "basic", + }, + { + title: "Status Pages", + id: "status-pages", + }, + { + title: "More", + id: "more", + }, + ]} + formFields={[ + { + field: { + title: true, + }, + title: "Announcement Title", + stepId: "basic", + description: "Title of announcement", + fieldType: FormFieldSchemaType.Text, + required: true, + placeholder: "Title", + }, + { + field: { + description: true, + }, + title: "Description", + stepId: "basic", + fieldType: FormFieldSchemaType.Markdown, + required: false, + description: "Add an announcement note. This is in Markdown.", + }, + { + field: { + statusPages: true, + }, + title: "Show announcement on these status pages ", + stepId: "status-pages", + description: "Select status pages to show this announcement on", + fieldType: FormFieldSchemaType.MultiSelectDropdown, + dropdownModal: { + type: StatusPage, + labelField: "name", + valueField: "_id", + }, + required: true, + placeholder: "Select Status Pages", + getSummaryElement: ( + item: FormValues, + ) => { + if (!item.statusPages || !Array.isArray(item.statusPages)) { + return ( +

+ No status pages selected for this announcement. +

+ ); + } + + const statusPageIds: Array = []; + + for (const statusPage of item.statusPages) { + if (typeof statusPage === "string") { + statusPageIds.push(new ObjectID(statusPage)); + continue; + } + + if (statusPage instanceof ObjectID) { + statusPageIds.push(statusPage); + continue; + } + + if (statusPage instanceof StatusPage) { + statusPageIds.push( + new ObjectID(statusPage._id?.toString() || ""), + ); + continue; + } + } + + return ( +
+ +
+ ); + }, + }, + { + field: { + showAnnouncementAt: true, + }, + stepId: "more", + title: "Start Showing Announcement At", + fieldType: FormFieldSchemaType.DateTime, + required: true, + placeholder: "Pick Date and Time", + }, + { + field: { + endAnnouncementAt: true, + }, + stepId: "more", + title: "End Showing Announcement At", + fieldType: FormFieldSchemaType.DateTime, + required: false, + placeholder: "Pick Date and Time", + }, + { + field: { + shouldStatusPageSubscribersBeNotified: true, + }, + + title: "Notify Status Page Subscribers", + stepId: "more", + description: "Should status page subscribers be notified?", + fieldType: FormFieldSchemaType.Checkbox, + defaultValue: true, + required: false, + }, + ]} + createEditModalWidth={ModalWidth.Large} + showRefreshButton={true} + viewPageRoute={Navigation.getCurrentRoute()} + filters={[ + { + field: { + title: true, + }, + title: "Title", + type: FieldType.Text, + }, + { + field: { + showAnnouncementAt: true, + }, + title: "Show Announcement At", + type: FieldType.Date, + }, + { + field: { + endAnnouncementAt: true, + }, + title: "End Announcement At", + type: FieldType.Date, + }, + { + field: { + shouldStatusPageSubscribersBeNotified: true, + }, + title: "Subscribers Notified", + type: FieldType.Boolean, + }, + { + field: { + statusPages: { + name: true, + _id: true, + projectId: true, + }, + }, + title: "Shown on Status Pages", + type: FieldType.EntityArray, + + filterEntityType: StatusPage, + filterQuery: { + projectId: DashboardNavigation.getProjectId()!, + }, + filterDropdownField: { + label: "name", + value: "_id", + }, + }, + ]} + columns={[ + { + field: { + title: true, + }, + title: "Title", + type: FieldType.Text, + }, + { + field: { + showAnnouncementAt: true, + }, + title: "Show Announcement At", + type: FieldType.DateTime, + }, + { + field: { + endAnnouncementAt: true, + }, + title: "End Announcement At", + type: FieldType.DateTime, + noValueMessage: "-", + }, { + field: { + statusPages: { + name: true, + }, + }, + title: "Shown on Status Pages", + type: FieldType.Element, + getElement: (item: StatusPageAnnouncement) => { + if (!item.statusPages || !Array.isArray(item.statusPages)) { + return

No status pages selected for this announcement.

; + } + return ( +
+ +
+ ); + } + } + ]} + /> +
+ ); +}; + +export default AnnouncementTable; diff --git a/Dashboard/src/Pages/ScheduledMaintenanceEvents/Create.tsx b/Dashboard/src/Pages/ScheduledMaintenanceEvents/Create.tsx index f939321535..b71ca70753 100644 --- a/Dashboard/src/Pages/ScheduledMaintenanceEvents/Create.tsx +++ b/Dashboard/src/Pages/ScheduledMaintenanceEvents/Create.tsx @@ -395,7 +395,7 @@ const ScheduledMaintenanceCreate: FunctionComponent< continue; } - if (statusPage instanceof Monitor) { + if (statusPage instanceof StatusPage) { statusPageIds.push( new ObjectID(statusPage._id?.toString() || ""), ); diff --git a/Dashboard/src/Pages/StatusPages/Announcements.tsx b/Dashboard/src/Pages/StatusPages/Announcements.tsx index c73bdeaacc..c7d1bd4584 100644 --- a/Dashboard/src/Pages/StatusPages/Announcements.tsx +++ b/Dashboard/src/Pages/StatusPages/Announcements.tsx @@ -1,164 +1,13 @@ -import DashboardNavigation from "../../Utils/Navigation"; import PageComponentProps from "../PageComponentProps"; -import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType"; -import ModelTable from "Common/UI/Components/ModelTable/ModelTable"; -import FieldType from "Common/UI/Components/Types/FieldType"; -import Navigation from "Common/UI/Utils/Navigation"; -import StatusPageAnnouncement from "Common/Models/DatabaseModels/StatusPageAnnouncement"; import React, { Fragment, FunctionComponent, ReactElement } from "react"; +import AnnouncementTable from "../../Components/Announcement/AnnouncementsTable"; const StatusPageDelete: FunctionComponent = ( _props: PageComponentProps, ): ReactElement => { return ( - - modelType={StatusPageAnnouncement} - id="table-status-page-note" - isDeleteable={true} - isCreateable={true} - showViewIdButton={true} - isEditable={true} - name="Status Page > Announcements" - isViewable={false} - query={{ - projectId: DashboardNavigation.getProjectId()!, - }} - cardProps={{ - title: "Announcements", - description: - "Here are announcements for this status page. This will show up on the status page.", - }} - noItemsMessage={"No announcements found."} - formSteps={[ - { - title: "Basic", - id: "basic", - }, - { - title: "More", - id: "more", - }, - ]} - formFields={[ - { - field: { - title: true, - }, - title: "Announcement Title", - stepId: "basic", - description: "Title of announcement", - fieldType: FormFieldSchemaType.Text, - required: true, - placeholder: "Title", - }, - { - field: { - description: true, - }, - title: "Description", - stepId: "basic", - fieldType: FormFieldSchemaType.Markdown, - required: false, - description: "Add an announcement note. This is in Markdown.", - }, - { - field: { - showAnnouncementAt: true, - }, - stepId: "more", - title: "Start Showing Announcement At", - fieldType: FormFieldSchemaType.DateTime, - required: true, - placeholder: "Pick Date and Time", - }, - { - field: { - endAnnouncementAt: true, - }, - stepId: "more", - title: "End Showing Announcement At", - fieldType: FormFieldSchemaType.DateTime, - required: false, - placeholder: "Pick Date and Time", - }, - { - field: { - shouldStatusPageSubscribersBeNotified: true, - }, - - title: "Notify Status Page Subscribers", - stepId: "more", - description: "Should status page subscribers be notified?", - fieldType: FormFieldSchemaType.Checkbox, - defaultValue: true, - required: false, - }, - ]} - showRefreshButton={true} - viewPageRoute={Navigation.getCurrentRoute()} - filters={[ - { - field: { - title: true, - }, - title: "Title", - type: FieldType.Text, - }, - { - field: { - showAnnouncementAt: true, - }, - title: "Show Announcement At", - type: FieldType.Date, - }, - { - field: { - endAnnouncementAt: true, - }, - title: "End Announcement At", - type: FieldType.Date, - }, - { - field: { - shouldStatusPageSubscribersBeNotified: true, - }, - title: "Subscribers Notified", - type: FieldType.Boolean, - }, - ]} - columns={[ - { - field: { - title: true, - }, - title: "Title", - type: FieldType.Text, - }, - { - field: { - showAnnouncementAt: true, - }, - title: "Show Announcement At", - type: FieldType.DateTime, - }, - { - field: { - endAnnouncementAt: true, - }, - title: "End Announcement At", - type: FieldType.DateTime, - noValueMessage: "-", - }, - { - field: { - shouldStatusPageSubscribersBeNotified: true, - }, - title: "Subscribers Notified", - type: FieldType.Boolean, - }, - ]} - /> + ); }; diff --git a/Dashboard/src/Pages/StatusPages/Layout.tsx b/Dashboard/src/Pages/StatusPages/Layout.tsx index fcba35f47b..ed9ff3b90d 100644 --- a/Dashboard/src/Pages/StatusPages/Layout.tsx +++ b/Dashboard/src/Pages/StatusPages/Layout.tsx @@ -16,7 +16,7 @@ const ScheduledMaintenancesLayout: FunctionComponent< title={"Status Pages"} sideMenu={ props.hideSideMenu ? undefined : ( - + ) } breadcrumbLinks={getStatusPagesBreadcrumbs(path)} diff --git a/Dashboard/src/Pages/StatusPages/SideMenu.tsx b/Dashboard/src/Pages/StatusPages/SideMenu.tsx index f55685ef65..0356abbb3c 100644 --- a/Dashboard/src/Pages/StatusPages/SideMenu.tsx +++ b/Dashboard/src/Pages/StatusPages/SideMenu.tsx @@ -5,15 +5,10 @@ import IconProp from "Common/Types/Icon/IconProp"; import SideMenu from "Common/UI/Components/SideMenu/SideMenu"; import SideMenuItem from "Common/UI/Components/SideMenu/SideMenuItem"; import SideMenuSection from "Common/UI/Components/SideMenu/SideMenuSection"; -import Project from "Common/Models/DatabaseModels/Project"; import React, { FunctionComponent, ReactElement } from "react"; -export interface ComponentProps { - project?: Project | undefined; -} +const DashboardSideMenu: FunctionComponent = ( -const DashboardSideMenu: FunctionComponent = ( - _props: ComponentProps, ): ReactElement => { return ( diff --git a/Dashboard/src/Pages/StatusPages/View/Announcements.tsx b/Dashboard/src/Pages/StatusPages/View/Announcements.tsx index 80a8e7b872..71d25c5bfe 100644 --- a/Dashboard/src/Pages/StatusPages/View/Announcements.tsx +++ b/Dashboard/src/Pages/StatusPages/View/Announcements.tsx @@ -1,187 +1,29 @@ import DashboardNavigation from "../../../Utils/Navigation"; import PageComponentProps from "../../PageComponentProps"; -import BadDataException from "Common/Types/Exception/BadDataException"; import ObjectID from "Common/Types/ObjectID"; -import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType"; -import ModelTable from "Common/UI/Components/ModelTable/ModelTable"; -import FieldType from "Common/UI/Components/Types/FieldType"; import Navigation from "Common/UI/Utils/Navigation"; import StatusPage from "Common/Models/DatabaseModels/StatusPage"; -import StatusPageAnnouncement from "Common/Models/DatabaseModels/StatusPageAnnouncement"; import React, { Fragment, FunctionComponent, ReactElement } from "react"; +import AnnouncementTable from "../../../Components/Announcement/AnnouncementsTable"; const StatusPageDelete: FunctionComponent = ( - props: PageComponentProps, + _props: PageComponentProps, ): ReactElement => { const modelId: ObjectID = Navigation.getLastParamAsObjectID(1); const statusPage: StatusPage = new StatusPage(); statusPage.id = modelId; + const query={ + statusPages: [statusPage], + projectId: DashboardNavigation.getProjectId()!, + } + return ( - - modelType={StatusPageAnnouncement} - id="table-status-page-note" - isDeleteable={true} - isCreateable={true} - showViewIdButton={true} - isEditable={true} - name="Status Page > Announcements" - isViewable={false} - query={{ - statusPages: [statusPage], - projectId: DashboardNavigation.getProjectId()!, - }} - onBeforeCreate={( - item: StatusPageAnnouncement, - ): Promise => { - if (!props.currentProject || !props.currentProject._id) { - throw new BadDataException("Project ID cannot be null"); - } - - const statusPage: StatusPage = new StatusPage(); - statusPage.id = modelId; - - item.statusPages = [statusPage]; - item.projectId = new ObjectID(props.currentProject._id); - return Promise.resolve(item); - }} - cardProps={{ - title: "Announcements", - description: - "Here are announcements for this status page. This will show up on the status page.", - }} - noItemsMessage={"No announcements found."} - formSteps={[ - { - title: "Basic", - id: "basic", - }, - { - title: "More", - id: "more", - }, - ]} - formFields={[ - { - field: { - title: true, - }, - title: "Announcement Title", - stepId: "basic", - description: "Title of announcement", - fieldType: FormFieldSchemaType.Text, - required: true, - placeholder: "Title", - }, - { - field: { - description: true, - }, - title: "Description", - stepId: "basic", - fieldType: FormFieldSchemaType.Markdown, - required: false, - description: "Add an announcement note. This is in Markdown.", - }, - { - field: { - showAnnouncementAt: true, - }, - stepId: "more", - title: "Start Showing Announcement At", - fieldType: FormFieldSchemaType.DateTime, - required: true, - placeholder: "Pick Date and Time", - }, - { - field: { - endAnnouncementAt: true, - }, - stepId: "more", - title: "End Showing Announcement At", - fieldType: FormFieldSchemaType.DateTime, - required: false, - placeholder: "Pick Date and Time", - }, - { - field: { - shouldStatusPageSubscribersBeNotified: true, - }, - - title: "Notify Status Page Subscribers", - stepId: "more", - description: "Should status page subscribers be notified?", - fieldType: FormFieldSchemaType.Checkbox, - defaultValue: true, - required: false, - }, - ]} - showRefreshButton={true} - viewPageRoute={Navigation.getCurrentRoute()} - filters={[ - { - field: { - title: true, - }, - title: "Title", - type: FieldType.Text, - }, - { - field: { - showAnnouncementAt: true, - }, - title: "Show Announcement At", - type: FieldType.Date, - }, - { - field: { - endAnnouncementAt: true, - }, - title: "End Announcement At", - type: FieldType.Date, - }, - { - field: { - shouldStatusPageSubscribersBeNotified: true, - }, - title: "Subscribers Notified", - type: FieldType.Boolean, - }, - ]} - columns={[ - { - field: { - title: true, - }, - title: "Title", - type: FieldType.Text, - }, - { - field: { - showAnnouncementAt: true, - }, - title: "Show Announcement At", - type: FieldType.DateTime, - }, - { - field: { - endAnnouncementAt: true, - }, - title: "End Announcement At", - type: FieldType.DateTime, - noValueMessage: "-", - }, - { - field: { - shouldStatusPageSubscribersBeNotified: true, - }, - title: "Subscribers Notified", - type: FieldType.Boolean, - }, - ]} - /> + ); }; diff --git a/Dashboard/src/Utils/RouteMap.ts b/Dashboard/src/Utils/RouteMap.ts index e5572c97ec..ad0bcded80 100644 --- a/Dashboard/src/Utils/RouteMap.ts +++ b/Dashboard/src/Utils/RouteMap.ts @@ -826,7 +826,7 @@ const RouteMap: Dictionary = { ), [PageMap.STATUS_PAGE_ANNOUNCEMENTS]: new Route( - `/dashboard/${RouteParams.ProjectID}/status-page/${ + `/dashboard/${RouteParams.ProjectID}/status-pages/${ StatusPagesRoutePath[PageMap.STATUS_PAGE_ANNOUNCEMENTS] }`, ),