From cef97a62403f6d287b3b40d68b2e182460f951f8 Mon Sep 17 00:00:00 2001 From: ek-hystax <33006768+ek-hystax@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:00:37 +0400 Subject: [PATCH 1/3] OS-2794. Events layout changes 1. Add a "No events" message 2. Update the layout to remove the second scroll on the page 3. Update title font size for consistency and increase spacing between accordions 4. Added max height for events list container to ensure scrollability on large screens (in height) 5. Refactor code --- .../ui/src/components/Events/Events.styles.ts | 31 ---- ngui/ui/src/components/Events/Events.tsx | 141 ++++++++++-------- .../EventsContainer/EventsContainer.tsx | 7 +- ngui/ui/src/translations/en-US/app.json | 1 + ngui/ui/src/utils/constants.ts | 2 + ngui/ui/src/utils/layouts.ts | 2 +- 6 files changed, 90 insertions(+), 94 deletions(-) delete mode 100644 ngui/ui/src/components/Events/Events.styles.ts diff --git a/ngui/ui/src/components/Events/Events.styles.ts b/ngui/ui/src/components/Events/Events.styles.ts deleted file mode 100644 index eb5d52f66..000000000 --- a/ngui/ui/src/components/Events/Events.styles.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { makeStyles } from "tss-react/mui"; - -const useStyles = makeStyles()((theme) => ({ - eventsWrapper: { - display: "flex", - position: "relative", - height: `calc(100vh - ${theme.spacing(19.5)})` - }, - events: { - width: "100%", - overflowY: "auto" - }, - heading: { - fontSize: theme.typography.pxToRem(15) - }, - tableIcon: { - verticalAlign: "bottom", - paddingRight: "0.2rem" - }, - infoIcon: { - color: theme.palette.info.main - }, - warningIcon: { - color: theme.palette.warning.main - }, - dateBlock: { - paddingTop: "2rem" - } -})); - -export default useStyles; diff --git a/ngui/ui/src/components/Events/Events.tsx b/ngui/ui/src/components/Events/Events.tsx index eb4a8c5d8..bca1eacab 100644 --- a/ngui/ui/src/components/Events/Events.tsx +++ b/ngui/ui/src/components/Events/Events.tsx @@ -1,9 +1,9 @@ import { useEffect, useState, useMemo } from "react"; import ErrorIcon from "@mui/icons-material/Error"; import InfoIcon from "@mui/icons-material/Info"; +import { Stack } from "@mui/material"; import Box from "@mui/material/Box"; import CircularProgress from "@mui/material/CircularProgress"; -import Grid from "@mui/material/Grid"; import Typography from "@mui/material/Typography"; import { FormattedMessage } from "react-intl"; import Accordion from "components/Accordion"; @@ -15,11 +15,10 @@ import RangePickerForm from "components/RangePickerForm"; import Table from "components/Table"; import { useInitialMount } from "hooks/useInitialMount"; import { isEmpty } from "utils/arrays"; -import { EVENT_LEVEL } from "utils/constants"; -import { formatUTC } from "utils/datetime"; -import { SPACING_1 } from "utils/layouts"; +import { EVENT_LEVEL, EVENTS_LIMIT } from "utils/constants"; +import { EN_FULL_FORMAT, formatUTC } from "utils/datetime"; +import { SPACING_1, SPACING_2, SPACING_3 } from "utils/layouts"; import { getQueryParams, updateQueryParams, removeQueryParam } from "utils/network"; -import useStyles from "./Events.styles"; const actionBarDefinition = { title: { @@ -29,7 +28,7 @@ const actionBarDefinition = { }; const Loader = () => ( - + ); @@ -106,15 +105,30 @@ const EventLevelSelector = ({ eventLevel, onApply }) => { ); }; +const getEventsGroupedByTime = (events) => + events.reduce((resultObject, event) => { + const groupKey = formatUTC(event.time); + return { + ...resultObject, + [groupKey]: [...(resultObject[groupKey] || []), event] + }; + }, {}); + +const EventIcon = ({ eventLevel }) => + ({ + [EVENT_LEVEL.INFO]: , + [EVENT_LEVEL.WARNING]: , + [EVENT_LEVEL.ERROR]: + })[eventLevel]; + const Events = ({ eventLevel, onScroll, applyFilter, events, isLoading = false }) => { const [expanded, setExpanded] = useState(""); - const { classes, cx } = useStyles(); const queryParams = getQueryParams(); - const handleAccordion = (panel) => (event, isExpanded) => { - setExpanded(isExpanded ? panel : ""); + const handleAccordion = (eventId) => (_, isExpanded) => { + setExpanded(isExpanded ? eventId : ""); if (isExpanded) { - updateQueryParams({ event: panel }); + updateQueryParams({ event: eventId }); } else { removeQueryParam("event"); } @@ -130,13 +144,6 @@ const Events = ({ eventLevel, onScroll, applyFilter, events, isLoading = false } } }, [setExpanded, queryParams, isInitialMount, setIsInitialMount]); - const addIcon = (level) => - ({ - [EVENT_LEVEL.INFO]: , - [EVENT_LEVEL.WARNING]: , - [EVENT_LEVEL.ERROR]: - })[level]; - const eventTableColumns = useMemo( () => [ { @@ -164,7 +171,7 @@ const Events = ({ eventLevel, onScroll, applyFilter, events, isLoading = false } { name: , dataTestId: "lbl_date", - value: `${formatUTC(event.time, "MM/dd/yyyy hh:mm a")} UTC` + value: `${formatUTC(event.time, EN_FULL_FORMAT)} UTC` }, { name: , @@ -211,65 +218,79 @@ const Events = ({ eventLevel, onScroll, applyFilter, events, isLoading = false } eventsList.map((event, index) => ( - - {addIcon(event.level)} - {event.description} - + + + + + + {event.description} + + {getAccordionContent(event)} )); - const getGroupedEvents = (eventsList) => - eventsList.reduce((resultObject, event) => { - const groupKey = formatUTC(event.time); - return { - ...resultObject, - [groupKey]: [...(resultObject[groupKey] || []), event] - }; - }, {}); + const renderEventList = () => { + const noEvents = isEmpty(events); - const buildEventsBox = (eventsObj) => - Object.keys(eventsObj).map((groupKey, index) => ( - - {groupKey} - {renderAccordion(eventsObj[groupKey], index)} + if (noEvents) { + return isLoading ? : ; + } + + return ( + + + {Object.entries(getEventsGroupedByTime(events)).map(([groupKey, groupData], index) => ( + + {groupKey} + {renderAccordion(groupData, index)} + + ))} + + {isLoading ? : null} - )); + ); + }; return ( <> - - - + + + - - + + - - - - - {isLoading && isEmpty(events) ? ( - - - - ) : ( - - {buildEventsBox(getGroupedEvents(events))} - {isLoading ? : null} - - )} - - + + + {renderEventList()} + + ); diff --git a/ngui/ui/src/containers/EventsContainer/EventsContainer.tsx b/ngui/ui/src/containers/EventsContainer/EventsContainer.tsx index cb02a3403..d7a504bd2 100644 --- a/ngui/ui/src/containers/EventsContainer/EventsContainer.tsx +++ b/ngui/ui/src/containers/EventsContainer/EventsContainer.tsx @@ -3,7 +3,7 @@ import { useQuery } from "@apollo/client"; import Events from "components/Events"; import { GET_EVENTS } from "graphql/api/keeper/queries"; import { useOrganizationInfo } from "hooks/useOrganizationInfo"; -import { EVENT_LEVEL } from "utils/constants"; +import { EVENT_LEVEL, EVENTS_LIMIT } from "utils/constants"; import { scrolledToBottom } from "utils/layouts"; import { getQueryParams, updateQueryParams } from "utils/network"; @@ -25,7 +25,10 @@ const EventsContainer = () => { const { loading } = useQuery(GET_EVENTS, { variables: { organizationId, - requestParams + requestParams: { + ...requestParams, + limit: EVENTS_LIMIT + } }, onCompleted: (data) => { setEvents((state) => [...state, ...(data?.events ?? [])]); diff --git a/ngui/ui/src/translations/en-US/app.json b/ngui/ui/src/translations/en-US/app.json index 4941ae167..6a07cfb4b 100644 --- a/ngui/ui/src/translations/en-US/app.json +++ b/ngui/ui/src/translations/en-US/app.json @@ -1318,6 +1318,7 @@ "noDuplicateChecks": "No duplicate checks, create a new one using the \"Run check\" button.", "noEnvironments": "No Shared Environments", "noErrorOutput": "No error output", + "noEvents": "No events", "noExceededPoolLimitForecasts": "No exceeded pool limit forecasts", "noExceededPoolLimits": "No exceeded pool limits", "noExclusions": "No exclusions", diff --git a/ngui/ui/src/utils/constants.ts b/ngui/ui/src/utils/constants.ts index 19d407fe6..b6939d12b 100644 --- a/ngui/ui/src/utils/constants.ts +++ b/ngui/ui/src/utils/constants.ts @@ -173,6 +173,8 @@ export const EVENT_LEVEL = Object.freeze({ ERROR: "ERROR" }); +export const EVENTS_LIMIT = 80; + export const CLOUD_ACCOUNT_TYPES_LIST = Object.keys(CLOUD_ACCOUNT_TYPE); export const DEFAULT_MAX_INPUT_LENGTH = 255; diff --git a/ngui/ui/src/utils/layouts.ts b/ngui/ui/src/utils/layouts.ts index fa2a24f5e..60e83ec8b 100644 --- a/ngui/ui/src/utils/layouts.ts +++ b/ngui/ui/src/utils/layouts.ts @@ -7,7 +7,7 @@ export const SPACING_4 = 4; export const SPACING_5 = 5; export const SPACING_6 = 6; -export const scrolledToBottom = (target) => target.scrollTop + document.documentElement.offsetHeight > target.scrollHeight; +export const scrolledToBottom = (target) => target.scrollTop + target.clientHeight >= target.scrollHeight; /** * Calculate the approximate width of the provided text From f0c30ffcaa1feadebc66c9210441ee159a7f8c6a Mon Sep 17 00:00:00 2001 From: ek-hystax <33006768+ek-hystax@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:01:10 +0400 Subject: [PATCH 2/3] OS-7737. Update weekly_expense_report email template --- .../templates/weekly_expense_report.html | 1538 ++++++++--------- herald/send_templates.py | 10 +- .../organization_expenses.py | 6 - 3 files changed, 748 insertions(+), 806 deletions(-) diff --git a/herald/modules/email_generator/templates/weekly_expense_report.html b/herald/modules/email_generator/templates/weekly_expense_report.html index 5c5d26069..de4e85c24 100644 --- a/herald/modules/email_generator/templates/weekly_expense_report.html +++ b/herald/modules/email_generator/templates/weekly_expense_report.html @@ -1,830 +1,786 @@ - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- - - -
- -
- - - -
- - +
+ + + + + - - - - - - -
- - - - - - -
- -
- - - - - - - - - -
-
-

Dear - {{texts.user.user_display_name}},

-
-
-
-

Your weekly expense report on {{texts.organization.name}} organization is ready and available below.

-
-
-
- -
-
- -
- - - - - - -
- -
- - - - - - - - - - - - - -
-
-

Limit

-
-
-
-

Expenses up-to-date

-
-
-
-

Forecasted expenses

-
-
-
-

{{texts.organization.currency_code}}{{texts.organization.limit}}

-
-
-
-

{{texts.organization.currency_code}}{{texts.organization.total_cost}}

-
-
-
-

{{texts.organization.currency_code}}{{texts.organization.forecast}}

-
-
-
- -
-
- -
- - - - - - -
- -
- - - - - - -
-
-

Unassigned resources

-
-
-
- -
-
- -
- - - - - - -
- -
- - - - - - - - - - - - - -
-
-

Expenses up-to-date

-
-
-
-

Forecasted expenses

-
-
-
-

Number of resources

-
-
-
-

{{texts.organization.currency_code}}{{texts.unassigned.total_cost}}

-
-
-
-

{{texts.organization.currency_code}}{{texts.unassigned.forecast}}

-
-
-
-

{{texts.unassigned.resources_tracked}}

-
-
-
- -
-
- -
- - - - + + +
- -
- - - - - - -
-
-

Pools report

-
-
-
- + + + + - - - - - +
- -
- - - -
- + + -