From 7e79900973da430179292ffc865ad44908763035 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Fri, 24 May 2024 14:02:37 +0530 Subject: [PATCH] feat: [SIG-582]: added planned maintenance create dialog (#4863) * feat: [SIG-582]: added planned maintenance create dialog * feat: [SIG-582]: added planned maintenance - listing * feat: [SIG-582]: added alert rule select * feat: [SIG-582]: added - planned maintenance list and createform ui fixes * feat: [SIG-582]: added - alert-rule tag styles * feat: [SIG-582]: added - style changes * feat: [SIG-582]: added - crud API integration and delete modal * feat: [SIG-582]: added - reccurrence form details * feat: [SIG-582]: added - duration and timezone * feat: [SIG-582]: removed console logs * feat: [SIG-582]: added - form validation for duration and endTime * feat: [SIG-582]: code refactor * feat: [SIG-582]: code refactor * feat: [SIG-582]: code refactor * feat: [SIG-582]: code refactor * feat: [SIG-582]: light mode styles * feat: [SIG-582]: code refactor * feat: [SIG-582]: code refactor and comment resolve * feat: [SIG-582]: code refactor and removed filters * feat: [SIG-582]: changed coming up on to start time * feat: [SIG-582]: added planned downtime behind FF - PLANNED_MAINTENANCE --- .../plannedDowntime/createDowntimeSchedule.ts | 44 ++ .../plannedDowntime/deleteDowntimeSchedule.ts | 19 + .../getAllDowntimeSchedules.ts | 50 ++ .../plannedDowntime/updateDowntimeSchedule.ts | 37 ++ frontend/src/constants/features.ts | 1 + .../DropdownWithSubMenu.styles.scss | 127 ++++ .../DropdownWithSubMenu.tsx | 230 +++++++ .../PlannedDowntime.styles.scss | 535 ++++++++++++++++ .../PlannedDowntime/PlannedDowntime.tsx | 152 +++++ .../PlannedDowntimeDeleteModal.tsx | 60 ++ .../PlannedDowntime/PlannedDowntimeForm.tsx | 432 +++++++++++++ .../PlannedDowntime/PlannedDowntimeList.tsx | 348 ++++++++++ .../PlannedDowntime/PlannedDowntimeutils.ts | 176 ++++++ frontend/src/pages/AlertList/index.tsx | 47 +- frontend/src/utils/timeZoneUtil.ts | 595 ++++++++++++++++++ 15 files changed, 2838 insertions(+), 15 deletions(-) create mode 100644 frontend/src/api/plannedDowntime/createDowntimeSchedule.ts create mode 100644 frontend/src/api/plannedDowntime/deleteDowntimeSchedule.ts create mode 100644 frontend/src/api/plannedDowntime/getAllDowntimeSchedules.ts create mode 100644 frontend/src/api/plannedDowntime/updateDowntimeSchedule.ts create mode 100644 frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.styles.scss create mode 100644 frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.tsx create mode 100644 frontend/src/container/PlannedDowntime/PlannedDowntime.styles.scss create mode 100644 frontend/src/container/PlannedDowntime/PlannedDowntime.tsx create mode 100644 frontend/src/container/PlannedDowntime/PlannedDowntimeDeleteModal.tsx create mode 100644 frontend/src/container/PlannedDowntime/PlannedDowntimeForm.tsx create mode 100644 frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx create mode 100644 frontend/src/container/PlannedDowntime/PlannedDowntimeutils.ts create mode 100644 frontend/src/utils/timeZoneUtil.ts diff --git a/frontend/src/api/plannedDowntime/createDowntimeSchedule.ts b/frontend/src/api/plannedDowntime/createDowntimeSchedule.ts new file mode 100644 index 0000000000..128fb9bf69 --- /dev/null +++ b/frontend/src/api/plannedDowntime/createDowntimeSchedule.ts @@ -0,0 +1,44 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; + +import { Recurrence } from './getAllDowntimeSchedules'; + +export interface DowntimeSchedulePayload { + name: string; + description?: string; + alertIds: string[]; + schedule: { + timezone?: string; + startTime?: string; + endTime?: string; + recurrence?: Recurrence; + }; +} + +export interface PayloadProps { + status: string; + data: string; +} + +const createDowntimeSchedule = async ( + props: DowntimeSchedulePayload, +): Promise | ErrorResponse> => { + try { + const response = await axios.post('/downtime_schedules', { + ...props, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default createDowntimeSchedule; diff --git a/frontend/src/api/plannedDowntime/deleteDowntimeSchedule.ts b/frontend/src/api/plannedDowntime/deleteDowntimeSchedule.ts new file mode 100644 index 0000000000..81c3602dea --- /dev/null +++ b/frontend/src/api/plannedDowntime/deleteDowntimeSchedule.ts @@ -0,0 +1,19 @@ +import axios from 'api'; +import { useMutation, UseMutationResult } from 'react-query'; + +export interface DeleteDowntimeScheduleProps { + id?: number; +} + +export interface DeleteSchedulePayloadProps { + status: string; + data: string; +} + +export const useDeleteDowntimeSchedule = ( + props: DeleteDowntimeScheduleProps, +): UseMutationResult => + useMutation({ + mutationKey: [props.id], + mutationFn: () => axios.delete(`/downtime_schedules/${props.id}`), + }); diff --git a/frontend/src/api/plannedDowntime/getAllDowntimeSchedules.ts b/frontend/src/api/plannedDowntime/getAllDowntimeSchedules.ts new file mode 100644 index 0000000000..8e77606a3f --- /dev/null +++ b/frontend/src/api/plannedDowntime/getAllDowntimeSchedules.ts @@ -0,0 +1,50 @@ +import axios from 'api'; +import { AxiosError, AxiosResponse } from 'axios'; +import { Option } from 'container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu'; +import { useQuery, UseQueryResult } from 'react-query'; + +export type Recurrence = { + startTime?: string | null; + endTime?: string | null; + duration?: number | string | null; + repeatType?: string | Option | null; + repeatOn?: string[] | null; +}; + +type Schedule = { + timezone: string | null; + startTime: string | null; + endTime: string | null; + recurrence: Recurrence | null; +}; + +export interface DowntimeSchedules { + id: number; + name: string | null; + description: string | null; + schedule: Schedule | null; + alertIds: string[] | null; + createdAt: string | null; + createdBy: string | null; + updatedAt: string | null; + updatedBy: string | null; +} +export type PayloadProps = { data: DowntimeSchedules[] }; + +export const getAllDowntimeSchedules = async ( + props?: GetAllDowntimeSchedulesPayloadProps, +): Promise> => + axios.get('/downtime_schedules', { params: props }); + +export interface GetAllDowntimeSchedulesPayloadProps { + active?: boolean; + recurrence?: boolean; +} + +export const useGetAllDowntimeSchedules = ( + props?: GetAllDowntimeSchedulesPayloadProps, +): UseQueryResult, AxiosError> => + useQuery, AxiosError>({ + queryKey: ['getAllDowntimeSchedules', props], + queryFn: () => getAllDowntimeSchedules(props), + }); diff --git a/frontend/src/api/plannedDowntime/updateDowntimeSchedule.ts b/frontend/src/api/plannedDowntime/updateDowntimeSchedule.ts new file mode 100644 index 0000000000..3fc747ae7e --- /dev/null +++ b/frontend/src/api/plannedDowntime/updateDowntimeSchedule.ts @@ -0,0 +1,37 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; + +import { DowntimeSchedulePayload } from './createDowntimeSchedule'; + +export interface DowntimeScheduleUpdatePayload { + data: DowntimeSchedulePayload; + id?: number; +} + +export interface PayloadProps { + status: string; + data: string; +} + +const updateDowntimeSchedule = async ( + props: DowntimeScheduleUpdatePayload, +): Promise | ErrorResponse> => { + try { + const response = await axios.put(`/downtime_schedules/${props.id}`, { + ...props.data, + }); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default updateDowntimeSchedule; diff --git a/frontend/src/constants/features.ts b/frontend/src/constants/features.ts index 396843db2d..67f9fe6110 100644 --- a/frontend/src/constants/features.ts +++ b/frontend/src/constants/features.ts @@ -19,4 +19,5 @@ export enum FeatureKeys { OSS = 'OSS', ONBOARDING = 'ONBOARDING', CHAT_SUPPORT = 'CHAT_SUPPORT', + PLANNED_MAINTENANCE = 'PLANNED_MAINTENANCE', } diff --git a/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.styles.scss b/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.styles.scss new file mode 100644 index 0000000000..3ea5aeb72d --- /dev/null +++ b/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.styles.scss @@ -0,0 +1,127 @@ +.options { + width: 100%; +} + +.option { + padding: 8px 10px; + cursor: pointer; + overflow: auto; +} + +.option:hover { + background-color: var(--bg-slate-200); +} + +.submenu-container { + position: absolute; + top: 0; + right: 50%; + z-index: 1; + background-color: var(--bg-ink-400); + border: 1px solid var(--bg-slate-500); + max-height: 300px; + overflow-y: auto; + width: 160px; + display: flex; + flex-direction: column; + gap: 6px; + padding: 12px; + border-radius: 4px; + + .submenu-checkbox { + padding: 0px; + } +} + +.dropdown-submenu { + .dropdown-input { + box-sizing: border-box; + margin: 0; + padding: 4.5px 11px; + color: rgba(255, 255, 255, 0.85); + font-size: 13px; + line-height: 1.6153846153846154; + list-style: none; + font-family: Inter; + position: relative; + display: inline-block; + min-width: 0; + transition: all 0.2s; + } + + .ant-popover-inner { + padding: 0px; + } +} + +.options-container { + position: relative; + --arrow-x: 175px; + --arrow-y: 266px; + width: 350px; + box-sizing: border-box; + margin: 0; + padding: 4px; + color: var(--bg-vanilla-400); + font-size: 12px; + line-height: 1.6153846153846154; + list-style: none; + font-family: Inter; + z-index: 1050; + overflow: hidden; + font-variant: initial; + background-color: var(--bg-ink-400); + border-radius: 2px; + outline: none; + box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.3); +} + +.submenu-popover { + .ant-popover-inner { + padding: 0px; + } +} + +.save-option-btn { + height: 24px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + align-self: flex-end; + width: 60px; +} + +.submenu-header { + color: var(--bg-vanilla-200); + font-family: Inter; + font-size: 12px; + font-style: normal; + font-weight: 400; + padding-bottom: 8px; + text-transform: uppercase; +} + +.lightMode { + .option:hover { + background-color: var(--bg-vanilla-200); + } + + .submenu-container { + background-color: var(--bg-vanilla-100); + border: 1px solid var(--bg-vanilla-300); + } + + .dropdown-submenu { + .dropdown-input { + color: var(--bg-slate-100); + } + } + .options-container { + color: var(--bg-slate-100); + background-color: var(--bg-vanilla-100); + } + .submenu-header { + color: var(--bg-slate-200); + } +} diff --git a/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.tsx b/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.tsx new file mode 100644 index 0000000000..d2a3e041bf --- /dev/null +++ b/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.tsx @@ -0,0 +1,230 @@ +import './DropdownWithSubMenu.styles.scss'; + +import { CheckOutlined } from '@ant-design/icons'; +import { Button, Checkbox, Popover, Typography } from 'antd'; +import { CheckboxChangeEvent } from 'antd/es/checkbox'; +import { FormInstance } from 'antd/lib'; +import { useEffect, useState } from 'react'; +import { popupContainer } from 'utils/selectPopupContainer'; + +import { recurrenceOptions } from '../PlannedDowntimeutils'; + +interface SubOption { + label: string; + value: string; +} + +export interface Option { + label: string; + value: string; + submenu?: SubOption[]; +} + +interface DropdownProps { + options: Option[]; + form: FormInstance; + setRecurrenceOption: React.Dispatch< + React.SetStateAction + >; +} + +export function DropdownWithSubMenu(props: DropdownProps): JSX.Element { + const { options, form, setRecurrenceOption } = props; + const [selectedOption, setSelectedOption] = useState