diff --git a/ngui/ui/src/components/CleanExpensesTable/CleanExpensesTable.tsx b/ngui/ui/src/components/CleanExpensesTable/CleanExpensesTable.tsx index 399a984de..011a49f44 100644 --- a/ngui/ui/src/components/CleanExpensesTable/CleanExpensesTable.tsx +++ b/ngui/ui/src/components/CleanExpensesTable/CleanExpensesTable.tsx @@ -137,7 +137,7 @@ const CleanExpensesTable = ({ }, cell: ({ row: { original } }) => { const metadataTags = MetadataNodes(original).getTags(); - return ; + return ; } }, resourcePoolOwner({ @@ -284,33 +284,34 @@ const CleanExpensesTable = ({ } ]; - if (typeof downloadResources === "function") { - actionBarItems.push({ - key: "download", - startIcon: , - messageId: "download", - type: "dropdown", - action: downloadResources, - isLoading: isDownloadingResources, - menu: { - items: [ - { - key: "xlsx", - messageId: "xlsxFile", - action: () => downloadResources(DOWNLOAD_FILE_FORMATS.XLSX), - dataTestId: "btn_download_xlsx" - }, - { - key: "json", - messageId: "jsonFile", - action: () => downloadResources(DOWNLOAD_FILE_FORMATS.JSON), - dataTestId: "btn_download_json" - } - ] - }, - dataTestId: "btn_download" - }); - } + // KU_TODO: Disabled download + // if (typeof downloadResources === "function") { + // actionBarItems.push({ + // key: "download", + // startIcon: , + // messageId: "download", + // type: "dropdown", + // action: downloadResources, + // isLoading: isDownloadingResources, + // menu: { + // items: [ + // { + // key: "xlsx", + // messageId: "xlsxFile", + // action: () => downloadResources(DOWNLOAD_FILE_FORMATS.XLSX), + // dataTestId: "btn_download_xlsx" + // }, + // { + // key: "json", + // messageId: "jsonFile", + // action: () => downloadResources(DOWNLOAD_FILE_FORMATS.JSON), + // dataTestId: "btn_download_json" + // } + // ] + // }, + // dataTestId: " btn_download" + // }); + // } if (assignmentRuleCreationLinkParameters) { actionBarItems.push({ diff --git a/ngui/ui/src/components/Dashboard/Dashboard.tsx b/ngui/ui/src/components/Dashboard/Dashboard.tsx index e510604ee..e4d1d9315 100644 --- a/ngui/ui/src/components/Dashboard/Dashboard.tsx +++ b/ngui/ui/src/components/Dashboard/Dashboard.tsx @@ -1,12 +1,12 @@ import Link from "@mui/material/Link"; -import { FormattedMessage } from "react-intl"; -import { GET_DATA_SOURCES } from "api/restapi/actionTypes"; +import {FormattedMessage} from "react-intl"; +import {GET_DATA_SOURCES} from "api/restapi/actionTypes"; import AlertDialog from "components/AlertDialog"; import DashboardGridLayout from "components/DashboardGridLayout"; import MailTo from "components/MailTo"; -import Mocked, { MESSAGE_TYPES } from "components/Mocked"; +import Mocked, {MESSAGE_TYPES} from "components/Mocked"; import PageContentWrapper from "components/PageContentWrapper"; -import { PRODUCT_TOUR, useProductTour, useStartTour } from "components/Tour"; +import {PRODUCT_TOUR, useProductTour, useStartTour} from "components/Tour"; import OrganizationConstraintsCardContainer from "containers/OrganizationConstraintsCardContainer"; import OrganizationExpensesContainer from "containers/OrganizationExpensesContainer"; import PoolsRequiringAttentionCardContainer from "containers/PoolsRequiringAttentionCardContainer"; @@ -14,11 +14,11 @@ import RecentModelsCardContainer from "containers/RecentModelsCardContainer"; import RecentTasksCardContainer from "containers/RecentTasksCardContainer"; import RecommendationsCardContainer from "containers/RecommendationsCardContainer"; import TopResourcesExpensesCardContainer from "containers/TopResourcesExpensesCardContainer"; -import { useApiData } from "hooks/useApiData"; -import { useIsUpMediaQuery } from "hooks/useMediaQueries"; -import { EMAIL_SUPPORT, DOCS_HYSTAX_OPTSCALE, SHOW_POLICY_QUERY_PARAM } from "urls"; -import { ENVIRONMENT } from "utils/constants"; -import { getQueryParams, removeQueryParam } from "utils/network"; +import {useApiData} from "hooks/useApiData"; +import {useIsUpMediaQuery} from "hooks/useMediaQueries"; +import {EMAIL_SUPPORT, DOCS_HYSTAX_OPTSCALE, SHOW_POLICY_QUERY_PARAM} from "urls"; +import {ENVIRONMENT} from "utils/constants"; +import {getQueryParams, removeQueryParam} from "utils/network"; import DashboardMocked from "./DashboardMocked"; const Dashboard = () => { @@ -27,14 +27,14 @@ const Dashboard = () => { const startTour = useStartTour(); const { - apiData: { cloudAccounts = [] } + apiData: {cloudAccounts = []} } = useApiData(GET_DATA_SOURCES); - const thereAreOnlyEnvironmentDataSources = cloudAccounts.every(({ type }) => type === ENVIRONMENT); + const thereAreOnlyEnvironmentDataSources = cloudAccounts.every(({type}) => type === ENVIRONMENT); - const { isFinished } = useProductTour(PRODUCT_TOUR); + const {isFinished} = useProductTour(PRODUCT_TOUR); - const { showPolicy } = getQueryParams(); + const {showPolicy} = getQueryParams(); const firstTimeOpen = !!showPolicy; const closeAlert = (queryParams) => { @@ -46,18 +46,18 @@ const Dashboard = () => { }; const dashboardGridItems = { - topResourcesExpensesCard: thereAreOnlyEnvironmentDataSources ? null : , - policiesCard: , - organizationExpenses: thereAreOnlyEnvironmentDataSources ? null : , - recommendationsCard: , - // poolsRequiringAttentionCard: , - recentTasksCard: , - recentModelsCard: + topResourcesExpensesCard: thereAreOnlyEnvironmentDataSources ? null : , + policiesCard: , + organizationExpenses: thereAreOnlyEnvironmentDataSources ? null : , + recommendationsCard: , + poolsRequiringAttentionCard: , + recentTasksCard: , + recentModelsCard: }; return ( <> - } backdropMessageType={MESSAGE_TYPES.DASHBOARD}> + } backdropMessageType={MESSAGE_TYPES.DASHBOARD}> @@ -69,22 +69,22 @@ const Dashboard = () => { paper: "window_privacy_policy", button: "btn_proceed" }} - header={} + header={} message={ , + email: , docs: (chunks) => ( {chunks} ), p: (chunks) =>

{chunks}

, - ul: (chunks) =>
    {chunks}
, + ul: (chunks) =>
    {chunks}
, li: (chunks) =>
  • {chunks}
  • , strong: (chunks) => {chunks}, - br:
    + br:
    }} /> } diff --git a/ngui/ui/src/components/HeaderButtons/HeaderButtons.tsx b/ngui/ui/src/components/HeaderButtons/HeaderButtons.tsx index 681113aa0..452d038a7 100644 --- a/ngui/ui/src/components/HeaderButtons/HeaderButtons.tsx +++ b/ngui/ui/src/components/HeaderButtons/HeaderButtons.tsx @@ -66,15 +66,16 @@ const HeaderButtons = () => { value: }} /> - : } - onClick={setIsCommunityDocsOpened} - color="info" - tooltip={{ - show: true, - value: - }} - /> + { /* TODO_KU: disabled because it doesn't not exist in documentation */ } + {/* : }*/} + {/* onClick={setIsCommunityDocsOpened}*/} + {/* color="info"*/} + {/* tooltip={{*/} + {/* show: true,*/} + {/* value: */} + {/* }}*/} + {/*/>*/} { flexWrap: "wrap", margin: `-${gap} -${halfGap} 0 -${halfGap}`, "& > *": { - margin: `${gap} ${halfGap} 0 ${halfGap}` + margin: `${halfGap}` }, minHeight: "40px" }, diff --git a/ngui/ui/src/components/LinearSelector/LinearSelector.tsx b/ngui/ui/src/components/LinearSelector/LinearSelector.tsx index 35e169942..d41f2f780 100644 --- a/ngui/ui/src/components/LinearSelector/LinearSelector.tsx +++ b/ngui/ui/src/components/LinearSelector/LinearSelector.tsx @@ -1,6 +1,10 @@ import { useState } from "react"; -import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; -import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp"; + +// KU_TODO: Changed icons +// import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; +// import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp"; +import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined'; +import KeyboardArrowUpOutlined from '@mui/icons-material/KeyboardArrowUpOutlined'; import DeleteOutlinedIcon from "@mui/icons-material/DeleteOutlined"; import Box from "@mui/material/Box"; import Checkbox from "@mui/material/Checkbox"; @@ -147,7 +151,7 @@ const MultiPopoverItem = ({ name, items, label, handleApply, values }) => { const PopoverLabelExpandIcon = ({ isOpen }) => { const { classes } = useStyles(); - const Icon = isOpen ? ArrowDropUpIcon : ArrowDropDownIcon; + const Icon = isOpen ? KeyboardArrowUpOutlined : KeyboardArrowDownOutlinedIcon; return ; }; @@ -324,13 +328,13 @@ const LinearSelector = ({ value, label, items, onClear, onClearAll, onChange, on return ( {label && ( - + {label} {": "} )} {valuesArray.length === 0 ? ( - + ) : ( diff --git a/ngui/ui/src/components/Resources/Resources.tsx b/ngui/ui/src/components/Resources/Resources.tsx index 822f17266..fad81c847 100644 --- a/ngui/ui/src/components/Resources/Resources.tsx +++ b/ngui/ui/src/components/Resources/Resources.tsx @@ -1,26 +1,27 @@ -import { useEffect } from "react"; +import {useEffect, useState} from "react"; import AddchartOutlinedIcon from "@mui/icons-material/AddchartOutlined"; import AssessmentOutlinedIcon from "@mui/icons-material/AssessmentOutlined"; import GroupWorkOutlinedIcon from "@mui/icons-material/GroupWorkOutlined"; import LinkOutlinedIcon from "@mui/icons-material/LinkOutlined"; +import {Badge, Box} from "@mui/material"; import Grid from "@mui/material/Grid"; -import { FormattedMessage, useIntl } from "react-intl"; +import {FormattedMessage, useIntl} from "react-intl"; import ActionBar from "components/ActionBar"; import CopyText from "components/CopyText"; -import { getBasicRangesSet } from "components/DateRangePicker/defaults"; +import {getBasicRangesSet} from "components/DateRangePicker/defaults"; import ExpensesFilters from "components/ExpensesFilters"; import LinearSelector from "components/LinearSelector"; import PageContentWrapper from "components/PageContentWrapper"; -import { ApplyResourcePerspectiveModal, CreateResourcePerspectiveModal } from "components/SideModalManager/SideModals"; +import {ApplyResourcePerspectiveModal, CreateResourcePerspectiveModal} from "components/SideModalManager/SideModals"; import TypographyLoader from "components/TypographyLoader"; import CleanExpensesBreakdownContainer from "containers/CleanExpensesBreakdownContainer"; import ExpensesSummaryContainer from "containers/ExpensesSummaryContainer"; import RangePickerFormContainer from "containers/RangePickerFormContainer"; import ResourceCountBreakdownContainer from "containers/ResourceCountBreakdownContainer"; import TagsBreakdownContainer from "containers/TagsBreakdownContainer"; -import { useOpenSideModal } from "hooks/useOpenSideModal"; -import { useOrganizationInfo } from "hooks/useOrganizationInfo"; -import { useResourceFilters } from "hooks/useResourceFilters"; +import {useOpenSideModal} from "hooks/useOpenSideModal"; +import {useOrganizationInfo} from "hooks/useOrganizationInfo"; +import {useResourceFilters} from "hooks/useResourceFilters"; import { CLUSTER_TYPES, DAILY_EXPENSES_BREAKDOWN_BY_PARAMETER_NAME, @@ -29,30 +30,33 @@ import { RESOURCES_BREAKDOWN_BY_QUERY_PARAMETER_NAME, RESOURCES_PERSPECTIVE_PARAMETER_NAME } from "urls"; -import { BREAKDOWN_LINEAR_SELECTOR_ITEMS, CLEAN_EXPENSES_BREAKDOWN_TYPES, DATE_RANGE_TYPE } from "utils/constants"; -import { SPACING_2 } from "utils/layouts"; -import { getQueryParams, updateQueryParams } from "utils/network"; -import { isEmpty as isEmptyObject } from "utils/objects"; +import {BREAKDOWN_LINEAR_SELECTOR_ITEMS, CLEAN_EXPENSES_BREAKDOWN_TYPES, DATE_RANGE_TYPE} from "utils/constants"; +import {KU_SPACING_2} from "utils/layouts"; +import {getQueryParams, updateQueryParams} from "utils/network"; +import {isEmpty as isEmptyObject} from "utils/objects"; +import Accordion from "../Accordion"; +import Typography from "@mui/material/Typography"; +import Divider from "../Selector/components/Divider"; -const BreakdownLinearSelector = ({ value, onChange }) => { +const BreakdownLinearSelector = ({value, onChange}) => { useEffect(() => { - updateQueryParams({ [RESOURCES_BREAKDOWN_BY_QUERY_PARAMETER_NAME]: value.name }); + updateQueryParams({[RESOURCES_BREAKDOWN_BY_QUERY_PARAMETER_NAME]: value.name}); }, [value.name]); return ( } + label={} onChange={onChange} items={BREAKDOWN_LINEAR_SELECTOR_ITEMS} /> ); }; -const SelectedPerspectiveTitle = ({ perspectiveName }) => { +const SelectedPerspectiveTitle = ({perspectiveName}) => { const intl = useIntl(); - const { organizationId } = useOrganizationInfo(); + const {organizationId} = useOrganizationInfo(); const copyUrl = [ window.location.origin, @@ -65,31 +69,32 @@ const SelectedPerspectiveTitle = ({ perspectiveName }) => { return ( {intl.formatMessage( - { id: "value - value" }, - { value1: intl.formatMessage({ id: "resources" }), value2: perspectiveName } + {id: "value - value"}, + {value1: intl.formatMessage({id: "resources"}), value2: perspectiveName} )} ); }; const Resources = ({ - startDateTimestamp, - endDateTimestamp, - filters, - filterValues, - onApply, - onFilterAdd, - onFilterDelete, - onFiltersDelete, - requestParams, - activeBreakdown, - selectedPerspectiveName, - perspectives, - onBreakdownChange, - onPerspectiveApply, - isFilterValuesLoading = false -}) => { + startDateTimestamp, + endDateTimestamp, + filters, + filterValues, + onApply, + onFilterAdd, + onFilterDelete, + onFiltersDelete, + requestParams, + activeBreakdown, + selectedPerspectiveName, + perspectives, + onBreakdownChange, + onPerspectiveApply, + isFilterValuesLoading = false + }) => { const openSideModal = useOpenSideModal(); + const [selectedFiltersCount, setSelectedFiltersCount] = useState(0); const intl = useIntl(); @@ -100,12 +105,16 @@ const Resources = ({ const items = resourceFilters.getFilterSelectors(); const appliedValues = resourceFilters.getAppliedValues(); + useEffect(() => { + setSelectedFiltersCount(appliedValues.length); // Update count based on applied values length + }, [appliedValues]); + const actionBarDefinition = { title: { text: selectedPerspectiveName ? ( - + ) : ( - intl.formatMessage({ id: "resources" }) + intl.formatMessage({id: "resources"}) ), dataTestId: "lbl_resources" }, @@ -113,23 +122,23 @@ const Resources = ({ ...(isEmptyObject(perspectives) ? [] : [ - { - key: "perspectives", - icon: , - messageId: "perspectivesTitle", - type: "button", - action: () => { - openSideModal(ApplyResourcePerspectiveModal, { - perspectives, - appliedPerspectiveName: selectedPerspectiveName, - onApply: onPerspectiveApply - }); - } + { + key: "perspectives", + icon: , + messageId: "perspectivesTitle", + type: "button", + action: () => { + openSideModal(ApplyResourcePerspectiveModal, { + perspectives, + appliedPerspectiveName: selectedPerspectiveName, + onApply: onPerspectiveApply + }); } - ]), + } + ]), { key: "savePerspectiveTitle", - icon: , + icon: , messageId: "savePerspectiveTitle", disabled: isPerspectiveSelected, type: "button", @@ -165,7 +174,7 @@ const Resources = ({ }, { key: "configureClusterTypes", - icon: , + icon: , messageId: "configureClusterTypes", type: "button", link: CLUSTER_TYPES, @@ -174,11 +183,11 @@ const Resources = ({ ] }; - const renderExpensesBreakdown = () => ; + const renderExpensesBreakdown = () => ; - const renderResourcesCountBreakdown = () => ; + const renderResourcesCountBreakdown = () => ; - const renderTagsBreakdown = () => ; + const renderTagsBreakdown = () => ; const renderContent = { [CLEAN_EXPENSES_BREAKDOWN_TYPES.EXPENSES]: renderExpensesBreakdown, @@ -188,39 +197,69 @@ const Resources = ({ return ( <> - + - - - - - - onApply(dateRange)} - initialStartDateValue={startDateTimestamp} - initialEndDateValue={endDateTimestamp} - rangeType={DATE_RANGE_TYPE.RESOURCES} - definedRanges={getBasicRangesSet()} - /> - - - {isFilterValuesLoading ? ( - - ) : ( - - )} + + + + + {isFilterValuesLoading ? ( + + ) : ( + <> + {/**/} + {/*
    */} + {/* */} + {/* */} + {/* */} + {/* */} + {/*
    */} + {/* */} + {/*
    */} + + + + + )} +
    + +
    + + + onApply(dateRange)} + initialStartDateValue={startDateTimestamp} + initialEndDateValue={endDateTimestamp} + rangeType={DATE_RANGE_TYPE.RESOURCES} + definedRanges={getBasicRangesSet()} + /> + +
    +
    - + - - {typeof renderContent === "function" ? renderContent() : null} + + + {typeof renderContent === "function" ? renderContent() : null} +
    diff --git a/ngui/ui/src/components/Selector/components/Divider.tsx b/ngui/ui/src/components/Selector/components/Divider.tsx index 44de2b961..1ab1d04cd 100644 --- a/ngui/ui/src/components/Selector/components/Divider.tsx +++ b/ngui/ui/src/components/Selector/components/Divider.tsx @@ -1,5 +1,5 @@ import { Divider as MuiDivider } from "@mui/material"; -const Divider = () => ; +const Divider = (props) => ; export default Divider; diff --git a/ngui/ui/src/components/SummaryGrid/SummaryGrid.styles.tsx b/ngui/ui/src/components/SummaryGrid/SummaryGrid.styles.tsx new file mode 100644 index 000000000..9301c90fe --- /dev/null +++ b/ngui/ui/src/components/SummaryGrid/SummaryGrid.styles.tsx @@ -0,0 +1,22 @@ +import {makeStyles} from "tss-react/mui"; +import {KU_BOX_SHADOW, KU_SPACING_1, KU_SPACING_2} from "../../utils/layouts"; + +const useStyles = makeStyles()(() => ({ + customBox: { + margin: 0, + marginBottom: KU_SPACING_2, + '.MuiPaper-rounded': { + borderRadius: KU_SPACING_2, + textAlign: 'center', + padding: KU_SPACING_1, + background: '#CAE4FF', + boxShadow: KU_BOX_SHADOW + }, + '.MuiBox-root': { + justifyContent: 'center', + color: 'black' + } + } +})); + +export default useStyles; diff --git a/ngui/ui/src/components/SummaryGrid/SummaryGrid.tsx b/ngui/ui/src/components/SummaryGrid/SummaryGrid.tsx index d22ecaa50..ea068c6f1 100644 --- a/ngui/ui/src/components/SummaryGrid/SummaryGrid.tsx +++ b/ngui/ui/src/components/SummaryGrid/SummaryGrid.tsx @@ -1,25 +1,26 @@ import Grid from "@mui/material/Grid"; -import { FormattedNumber, FormattedMessage } from "react-intl"; -import { v4 as uuidv4 } from "uuid"; +import {FormattedNumber, FormattedMessage} from "react-intl"; +import {v4 as uuidv4} from "uuid"; import FormattedDigitalUnit from "components/FormattedDigitalUnit"; import FormattedMoney from "components/FormattedMoney"; import SummaryCard from "components/SummaryCard"; import SummaryCardExtended from "components/SummaryCardExtended"; import Tooltip from "components/Tooltip"; -import { useOrganizationInfo } from "hooks/useOrganizationInfo"; -import { SUMMARY_CARD_TYPES, SUMMARY_VALUE_COMPONENT_TYPES, FORMATTED_MONEY_TYPES } from "utils/constants"; -import { SPACING_2 } from "utils/layouts"; +import {useOrganizationInfo} from "hooks/useOrganizationInfo"; +import {SUMMARY_CARD_TYPES, SUMMARY_VALUE_COMPONENT_TYPES, FORMATTED_MONEY_TYPES} from "utils/constants"; +import {SPACING_3} from "utils/layouts"; +import useStyles from "./SummaryGrid.styles"; const getValueComponentSettings = (type, CustomComponent) => ({ component: { [SUMMARY_VALUE_COMPONENT_TYPES.FormattedNumber]: FormattedNumber, - [SUMMARY_VALUE_COMPONENT_TYPES.FormattedMoney]: ({ value, ...rest }) => { - const { currency } = useOrganizationInfo(); + [SUMMARY_VALUE_COMPONENT_TYPES.FormattedMoney]: ({value, ...rest}) => { + const {currency} = useOrganizationInfo(); return ( - }> + }> - + ); @@ -34,30 +35,30 @@ const getValueComponentSettings = (type, CustomComponent) => ({ }); const renderSummaryCard = ({ - value, - valueComponentType, - valueComponentProps, - captionMessageId, - caption, - rawCaption, - CustomValueComponent = null, - isLoading, - color, - help, - button, - dataTestIds, - icon, - pdfId, - backdrop -}) => { - const { component: ValueComponent } = getValueComponentSettings(valueComponentType, CustomValueComponent); + value, + valueComponentType, + valueComponentProps, + captionMessageId, + caption, + rawCaption, + CustomValueComponent = null, + isLoading, + color, + help, + button, + dataTestIds, + icon, + pdfId, + backdrop + }) => { + const {component: ValueComponent} = getValueComponentSettings(valueComponentType, CustomValueComponent); return ( : value} icon={icon} rawValue={valueComponentProps?.value} - caption={caption ?? } + caption={caption ?? } rawCaption={rawCaption ?? captionMessageId} isLoading={isLoading} color={color} @@ -71,31 +72,31 @@ const renderSummaryCard = ({ }; const renderExtendedSummaryCard = ({ - value, - valueComponentType, - valueComponentProps, - CustomValueComponent = null, - captionMessageId, - caption, - relativeValue, - relativeValueComponentType, - relativeValueComponentProps, - CustomRelativeValueComponent = null, - relativeValueCaptionMessageId, - isLoading, - color, - help, - button, - dataTestIds, - icon, - backdrop -}) => { - const { component: ValueComponent, computedProps: computedValueComponentProps = {} } = getValueComponentSettings( + value, + valueComponentType, + valueComponentProps, + CustomValueComponent = null, + captionMessageId, + caption, + relativeValue, + relativeValueComponentType, + relativeValueComponentProps, + CustomRelativeValueComponent = null, + relativeValueCaptionMessageId, + isLoading, + color, + help, + button, + dataTestIds, + icon, + backdrop + }) => { + const {component: ValueComponent, computedProps: computedValueComponentProps = {}} = getValueComponentSettings( valueComponentType, CustomValueComponent ); - const { component: RelativeValueComponent, computedProps: computedRelativeValueComponentProps = {} } = + const {component: RelativeValueComponent, computedProps: computedRelativeValueComponentProps = {}} = getValueComponentSettings(relativeValueComponentType, CustomRelativeValueComponent); return ( @@ -108,8 +109,8 @@ const renderExtendedSummaryCard = ({ relativeValue ) } - caption={caption ?? } - relativeValueCaption={} + caption={caption ?? } + relativeValueCaption={} icon={icon} isLoading={isLoading} color={color} @@ -127,20 +128,21 @@ const getCardRenderer = (cardType) => [SUMMARY_CARD_TYPES.EXTENDED]: renderExtendedSummaryCard })[cardType]; -const SummaryGrid = ({ summaryData }) => { +const SummaryGrid = ({summaryData, summaryStyle}) => { + const {classes} = useStyles(); const renderSummary = () => - summaryData.map(({ key, renderCondition, type = SUMMARY_CARD_TYPES.BASIC, isLoading, ...rest }) => { + summaryData.map(({key, renderCondition, type = SUMMARY_CARD_TYPES.BASIC, isLoading, ...rest}) => { const shouldRender = renderCondition ? renderCondition() || isLoading : true; const renderCard = getCardRenderer(type); return renderCard && shouldRender ? ( - - {renderCard({ isLoading, ...rest })} + + {renderCard({isLoading, ...rest})} ) : null; }); return ( - + {renderSummary()} ); diff --git a/ngui/ui/src/components/Table/Table.tsx b/ngui/ui/src/components/Table/Table.tsx index 6c3dde61e..479bfac8e 100644 --- a/ngui/ui/src/components/Table/Table.tsx +++ b/ngui/ui/src/components/Table/Table.tsx @@ -1,15 +1,15 @@ -import { useRef } from "react"; -import { Box, TableBody, TableCell, TableFooter, TableHead, TableRow } from "@mui/material"; +import {useRef} from "react"; +import {Box, TableBody, TableCell, TableFooter, TableHead, TableRow} from "@mui/material"; import MuiTable from "@mui/material/Table"; -import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; -import { FormattedMessage } from "react-intl"; +import {getCoreRowModel, useReactTable} from "@tanstack/react-table"; +import {FormattedMessage} from "react-intl"; import TableLoader from "components/TableLoader"; -import { isEmpty as isEmptyArray } from "utils/arrays"; -import { SPACING_1 } from "utils/layouts"; +import {isEmpty as isEmptyArray} from "utils/arrays"; +import {SPACING_1} from "utils/layouts"; import InfoArea from "./components/InfoArea"; import Pagination from "./components/Pagination"; import TableActions from "./components/TableActions"; -import { MemoTableBodyCell, TableBodyCell } from "./components/TableBodyCell"; +import {MemoTableBodyCell, TableBodyCell} from "./components/TableBodyCell"; import TableFooterCell from "./components/TableFooterCell"; import TableHeaderCell from "./components/TableHeaderCell"; import { @@ -25,77 +25,77 @@ import { useSticky } from "./hooks"; import useStyles from "./Table.styles"; -import { getRowsCount } from "./utils"; -import { SELECTION_COLUMN_ID } from "./utils/constants"; +import {getRowsCount} from "./utils"; +import {SELECTION_COLUMN_ID} from "./utils/constants"; const DEFAULT_EMPTY_MESSAGE_ID = "noDataToDisplay"; const Table = ({ - data, - columns: columnsProperty, - /** - * TODO: Remove isLoading from this component - * Motivation: This component manages a local state, so if we render it when data is not fully - * ready (isLoading = false) then we will need to update the local state that depends on a dynamic (api) data - * when it is ready. - * We could make **THIS** component isLoading-independent in order to initialize the state when the data is fully loaded - * In order to do so, we could create another **Table** component and define the following logic there: - * ``` - * const Table = ({ isLoading, ...rest }) => { - * if(isLoading) { return } - * - * return - * } - * ``` - */ - isLoading, - getRowStyle, - withHeader = true, - withFooter = false, - withSearch, - rangeFilter, - dataTestIds = {}, - queryParamPrefix, - localization = {}, - pageSize, - counters = {}, - /** - * TODO: The name is confusing, it looks like it is a boolean, but it is a string - * @ekirillov would suggest refactoring it to something more generic line "link" or "header link" - */ - showAllLink, - withSelection, - rowSelection, - onRowSelectionChange, - withExpanded, - getSubRows = (row) => row.children, - getRowId, - expanded, - onExpandedChange, - actionBar, - columnsSelectorUID, - columnSetsSelectorId, - columnOrder, - onColumnOrderChange, - stickySettings = {}, - memoBodyCells = false, - getRowCellClassName, - getHeaderCellClassName, - onRowClick, - isSelectedRow, - overflowX = "auto", - disableBottomBorderForLastRow = false, - tableLayout = "auto", - enableSearchQueryParam, - enablePaginationQueryParam, - manualPagination, - manualGlobalFiltering -}) => { + data, + columns: columnsProperty, + /** + * TODO: Remove isLoading from this component + * Motivation: This component manages a local state, so if we render it when data is not fully + * ready (isLoading = false) then we will need to update the local state that depends on a dynamic (api) data + * when it is ready. + * We could make **THIS** component isLoading-independent in order to initialize the state when the data is fully loaded + * In order to do so, we could create another **Table** component and define the following logic there: + * ``` + * const Table = ({ isLoading, ...rest }) => { + * if(isLoading) { return } + * + * return + * } + * ``` + */ + isLoading, + getRowStyle, + withHeader = true, + withFooter = false, + withSearch, + rangeFilter, + dataTestIds = {}, + queryParamPrefix, + localization = {}, + pageSize, + counters = {}, + /** + * TODO: The name is confusing, it looks like it is a boolean, but it is a string + * @ekirillov would suggest refactoring it to something more generic line "link" or "header link" + */ + showAllLink, + withSelection, + rowSelection, + onRowSelectionChange, + withExpanded, + getSubRows = (row) => row.children, + getRowId, + expanded, + onExpandedChange, + actionBar, + columnsSelectorUID, + columnSetsSelectorId, + columnOrder, + onColumnOrderChange, + stickySettings = {}, + memoBodyCells = false, + getRowCellClassName, + getHeaderCellClassName, + onRowClick, + isSelectedRow, + overflowX = "auto", + disableBottomBorderForLastRow = false, + tableLayout = "auto", + enableSearchQueryParam, + enablePaginationQueryParam, + manualPagination, + manualGlobalFiltering + }) => { const headerRef = useRef(); - const { classes } = useStyles(); + const {classes} = useStyles(); - const { stickyHeaderCellStyles, stickyTableStyles } = useSticky({ + const {stickyHeaderCellStyles, stickyTableStyles} = useSticky({ headerRef, stickySettings }); @@ -112,7 +112,7 @@ const Table = ({ }; }; - const { tableOptions: sortingTableOptions } = useSortingTableSettings(); + const {tableOptions: sortingTableOptions} = useSortingTableSettings(); const { state: globalFilterState, @@ -127,7 +127,7 @@ const Table = ({ columns: columnsProperty }); - const { state: columnsVisibilityState, tableOptions: columnsVisibilityTableOptions } = + const {state: columnsVisibilityState, tableOptions: columnsVisibilityTableOptions} = useColumnsVisibility(columnsSelectorUID); const totalRowsCount = getRowsCount(data, { @@ -135,7 +135,7 @@ const Table = ({ getSubRows }); - const { state: paginationState, tableOptions: paginationTableOptions } = usePaginationTableSettings({ + const {state: paginationState, tableOptions: paginationTableOptions} = usePaginationTableSettings({ pageSize, rowsCount: totalRowsCount, queryParamPrefix, @@ -146,19 +146,19 @@ const Table = ({ withSelection }); - const { state: expandedState, tableOptions: expandedTableOptions } = useExpandedTableSettings({ + const {state: expandedState, tableOptions: expandedTableOptions} = useExpandedTableSettings({ withExpanded, getSubRows, expanded, onExpandedChange }); - const { state: columnOrderState, tableOptions: columnOrderTableOptions } = useColumnOrderTableSettings({ + const {state: columnOrderState, tableOptions: columnOrderTableOptions} = useColumnOrderTableSettings({ columnOrder, onColumnOrderChange }); - const { state: rowSelectionState, tableOptions: rowSelectionTableOptions } = useRowSelectionTableSettings({ + const {state: rowSelectionState, tableOptions: rowSelectionTableOptions} = useRowSelectionTableSettings({ withSelection, rowSelection, onRowSelectionChange @@ -209,7 +209,7 @@ const Table = ({ const selectedRowsCount = withSelection ? table.getSelectedRowModel().flatRows.length : 0; - const { rows } = table.getRowModel(); + const {rows} = table.getRowModel(); const getPaginationSettings = () => { if (isManualPagination) { @@ -248,10 +248,10 @@ const Table = ({ return { withSearch, searchValue: withSearch ? globalFilterState.globalFilter.search : undefined, - onSearchChange: withSearch ? (newSearchValue) => onSearchChange(newSearchValue, { tableContext: table }) : null, + onSearchChange: withSearch ? (newSearchValue) => onSearchChange(newSearchValue, {tableContext: table}) : null, rangeFilter, rangeValue: rangeFilter ? globalFilterState.globalFilter.range : undefined, - onRangeChange: rangeFilter ? (newRangeValue) => onRangeChange(newRangeValue, { tableContext: table }) : null + onRangeChange: rangeFilter ? (newRangeValue) => onRangeChange(newRangeValue, {tableContext: table}) : null }; }; @@ -280,7 +280,7 @@ const Table = ({ }} > {isLoading ? ( - + ) : ( {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map((header) => ( - + ( - ))} + />) + )} ))} @@ -312,15 +313,15 @@ const Table = ({ sx={ disableBottomBorderForLastRow ? { - "&:last-child > td": { - borderBottom: "none" - } + "&:last-child > td": { + borderBottom: "none" } + } : {} } > - + ) : ( @@ -336,16 +337,16 @@ const Table = ({ sx={ disableBottomBorderForLastRow ? { - "&:last-child > td": { - borderBottom: "none" - } + "&:last-child > td": { + borderBottom: "none" } + } : {} } > {row.getVisibleCells().map((cell) => { if (cell.column.id === SELECTION_COLUMN_ID) { - return ; + return ; } const Cell = memoBodyCells ? MemoTableBodyCell : TableBodyCell; @@ -369,7 +370,7 @@ const Table = ({ {table.getFooterGroups().map((footerGroup) => ( {footerGroup.headers.map((footerContext) => ( - + ))} ))} @@ -384,8 +385,8 @@ const Table = ({ alignItems: "center", paddingTop: SPACING_1, flexWrap: "wrap", - flexDirection: { xs: "column-reverse", md: "row" }, - ":empty": { display: "none" } + flexDirection: {xs: "column-reverse", md: "row"}, + ":empty": {display: "none"} }} > ( ); -const TableHeaderCell = ({ headerContext, stickyStyles = {}, getHeaderCellClassName }) => { +const TableHeaderCell = ({ headerContext, stickyStyles = {}, getHeaderCellClassName, isSelectionColumn = false }) => { const { style: cellStyle = {}, headerStyle: headerCellStyle = {} } = headerContext.column.columnDef; const getCellContent = () => { @@ -54,7 +54,9 @@ const TableHeaderCell = ({ headerContext, stickyStyles = {}, getHeaderCellClassN }} colSpan={headerContext.colSpan} rowSpan={headerContext.rowSpan} - className={typeof getHeaderCellClassName === "function" ? getHeaderCellClassName(headerContext.getContext()) : undefined} + className={ + `${isSelectionColumn ? 'tableRowSelection' : ''} ${typeof getHeaderCellClassName === "function" ? getHeaderCellClassName(headerContext.getContext()) : ''}`.trim() + } > {getCellContent()} diff --git a/ngui/ui/src/components/WrapperCard/WrapperCard.styles.ts b/ngui/ui/src/components/WrapperCard/WrapperCard.styles.ts index 992b101e0..e55a1dffa 100644 --- a/ngui/ui/src/components/WrapperCard/WrapperCard.styles.ts +++ b/ngui/ui/src/components/WrapperCard/WrapperCard.styles.ts @@ -1,5 +1,5 @@ import { makeStyles } from "tss-react/mui"; -import {KU_SPACING_2, KU_SPACING_3} from "../../utils/layouts"; +import {KU_BOX_SHADOW, KU_SPACING_2, KU_SPACING_3} from "../../utils/layouts"; const useStyles = makeStyles()((theme) => ({ spacer: { @@ -17,7 +17,7 @@ const useStyles = makeStyles()((theme) => ({ }, shadow: { borderRadius: KU_SPACING_2, - boxShadow: '0 0 8px rgba(0,0,0,0.1)' + boxShadow: KU_BOX_SHADOW }, buttonLink: { "&:hover": { diff --git a/ngui/ui/src/containers/ExpensesSummaryContainer/ExpensesSummaryContainer.tsx b/ngui/ui/src/containers/ExpensesSummaryContainer/ExpensesSummaryContainer.tsx index 6b54a9e5f..35e96604e 100644 --- a/ngui/ui/src/containers/ExpensesSummaryContainer/ExpensesSummaryContainer.tsx +++ b/ngui/ui/src/containers/ExpensesSummaryContainer/ExpensesSummaryContainer.tsx @@ -63,7 +63,7 @@ const ExpensesSummaryContainer = ({ requestParams }) => { } ]; - return ; + return ; }; export default ExpensesSummaryContainer; diff --git a/ngui/ui/src/theme.ts b/ngui/ui/src/theme.ts index f85860b73..558fd3c05 100644 --- a/ngui/ui/src/theme.ts +++ b/ngui/ui/src/theme.ts @@ -1,12 +1,15 @@ // Hystax palette: https://cdn.hystax.com/Hystax/Hystax-Guideline-2020.pdf // Material design color tool: https://material.io/resources/color/ +import KeyboardArrowUpOutlinedIcon from '@mui/icons-material/KeyboardArrowUpOutlined'; import {common} from "@mui/material/colors"; import {createTheme, alpha, darken, lighten} from "@mui/material/styles"; import {isEmpty as isEmptyArray} from "utils/arrays"; import {customResponsiveFontSizes} from "utils/fonts"; import {isEmpty as isEmptyObject} from "utils/objects"; -import {BRAND_PRIMARY, KU_SPACING_1, KU_SPACING_2, KU_SPACING_3} from "./utils/layouts"; +import {BRAND_GRAY_1, BRAND_PRIMARY, KU_BOX_SHADOW, KU_SPACING_1, KU_SPACING_2, KU_SPACING_3} from "./utils/layouts"; +import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined'; + const getLighten = (color, lightenAlpha = 0.2) => lighten(color, lightenAlpha); const getDarken = (color, darkenAlpha = 0.3) => darken(color, darkenAlpha); @@ -14,674 +17,748 @@ const getDarken = (color, darkenAlpha = 0.3) => darken(color, darkenAlpha); export const isMedia = (property) => property.startsWith("@media"); const applyPaletteSettings = (settings) => { - const isEmptySetting = (name) => isEmptyObject(settings.palette?.[name] ?? {}); - - const mergeIfSettingIsNotEmpty = (target, settingName) => ({ - ...target, - ...(isEmptySetting(settingName) ? {} : settings.palette[settingName]) - }); - - const primary = mergeIfSettingIsNotEmpty( - { - main: BRAND_PRIMARY - }, - "primary" - ); - - const info = mergeIfSettingIsNotEmpty( - { - main: "#5E6A7F", - header: getLighten("#5E6A7F", 0.93) - }, - "info" - ); - - const secondary = mergeIfSettingIsNotEmpty( - { - main: "#472AFF", - contrastText: 'white' - }, - "secondary" - ); - - const success = mergeIfSettingIsNotEmpty( - { - main: "#007E00" - }, - "success" - ); - - const error = mergeIfSettingIsNotEmpty( - { - main: "#B00020" - }, - "error" - ); - - const warning = mergeIfSettingIsNotEmpty( - { - main: "#906B00" - }, - "warning" - ); + const isEmptySetting = (name) => isEmptyObject(settings.palette?.[name] ?? {}); - const text = mergeIfSettingIsNotEmpty( - { - primary: getDarken(info.main), - secondary: primary.main - }, - "text" - ); - - return { - primary, - secondary, - info, - success, - error, - warning, - text - }; -}; -const applyChartPaletteSettings = (settings) => { - const isEmptySetting = (name) => isEmptyArray(settings.chartPalette?.[name] ?? []); - - const chart = isEmptySetting("chart") - ? [ - "#4AB4EE", - "#FFC348", - "#30D5C8", - "#9950B1", - "#4A63EE", - "#FF6648", - "#30D575", - "#B19950", - "#834AEE", - "#48E1FF", - "#D53090", - "#99B150" - ] - : settings.chartPalette.chart; - - const monoChart = isEmptySetting("monoChart") ? ["#4AB4EE"] : settings.chartPalette.monoChart; + const mergeIfSettingIsNotEmpty = (target, settingName) => ({ + ...target, + ...(isEmptySetting(settingName) ? {} : settings.palette[settingName]) + }); - return { - chart, - monoChart - }; -}; - -const applyGoogleMapPaletteSettings = (basicColorsPalette) => [ - { - elementType: "geometry", - stylers: [ - { - color: "#ffffff" - } - ] - }, + const primary = mergeIfSettingIsNotEmpty( { - elementType: "labels.icon", - stylers: [ - { - visibility: "off" - } - ] - }, - { - elementType: "labels.text.fill", - stylers: [ - { - color: basicColorsPalette.info.main - } - ] - }, - { - elementType: "labels.text.stroke", - stylers: [ - { - visibility: "off" - } - ] - }, - { - featureType: "administrative", - elementType: "geometry", - stylers: [ - { - visibility: "off" - } - ] + main: BRAND_PRIMARY }, + "primary" + ); + + const info = mergeIfSettingIsNotEmpty( { - featureType: "administrative.land_parcel", - stylers: [ - { - visibility: "off" - } - ] - }, - { - featureType: "administrative.neighborhood", - stylers: [ - { - visibility: "off" - } - ] + main: "#5E6A7F", + header: getLighten("#5E6A7F", 0.93) }, + "info" + ); + + const secondary = mergeIfSettingIsNotEmpty( { - featureType: "poi", - stylers: [ - { - visibility: "off" - } - ] + main: "#472AFF", + contrastText: 'white' }, + "secondary" + ); + + const success = mergeIfSettingIsNotEmpty( { - featureType: "poi", - elementType: "labels.text", - stylers: [ - { - visibility: "off" - } - ] + main: "#007E00" }, + "success" + ); + + const error = mergeIfSettingIsNotEmpty( { - featureType: "road", - stylers: [ - { - visibility: "off" - } - ] + main: "#B00020" }, + "error" + ); + + const warning = mergeIfSettingIsNotEmpty( { - featureType: "transit", - stylers: [ - { - visibility: "off" - } - ] + main: "#906B00" }, + "warning" + ); + + const text = mergeIfSettingIsNotEmpty( { - featureType: "water", - elementType: "geometry", - stylers: [ - { - color: "#DEE1E5" - } - ] + primary: getDarken(info.main), + secondary: primary.main }, - { - featureType: "water", - elementType: "labels.text", - stylers: [ - { - visibility: "off" - } - ] - } + "text" + ); + + return { + primary, + secondary, + info, + success, + error, + warning, + text + }; +}; +const applyChartPaletteSettings = (settings) => { + const isEmptySetting = (name) => isEmptyArray(settings.chartPalette?.[name] ?? []); + + const chart = isEmptySetting("chart") + ? [ + "#4AB4EE", + "#FFC348", + "#30D5C8", + "#9950B1", + "#4A63EE", + "#FF6648", + "#30D575", + "#B19950", + "#834AEE", + "#48E1FF", + "#D53090", + "#99B150" + ] + : settings.chartPalette.chart; + + const monoChart = isEmptySetting("monoChart") ? ["#4AB4EE"] : settings.chartPalette.monoChart; + + return { + chart, + monoChart + }; +}; + +const applyGoogleMapPaletteSettings = (basicColorsPalette) => [ + { + elementType: "geometry", + stylers: [ + { + color: "#ffffff" + } + ] + }, + { + elementType: "labels.icon", + stylers: [ + { + visibility: "off" + } + ] + }, + { + elementType: "labels.text.fill", + stylers: [ + { + color: basicColorsPalette.info.main + } + ] + }, + { + elementType: "labels.text.stroke", + stylers: [ + { + visibility: "off" + } + ] + }, + { + featureType: "administrative", + elementType: "geometry", + stylers: [ + { + visibility: "off" + } + ] + }, + { + featureType: "administrative.land_parcel", + stylers: [ + { + visibility: "off" + } + ] + }, + { + featureType: "administrative.neighborhood", + stylers: [ + { + visibility: "off" + } + ] + }, + { + featureType: "poi", + stylers: [ + { + visibility: "off" + } + ] + }, + { + featureType: "poi", + elementType: "labels.text", + stylers: [ + { + visibility: "off" + } + ] + }, + { + featureType: "road", + stylers: [ + { + visibility: "off" + } + ] + }, + { + featureType: "transit", + stylers: [ + { + visibility: "off" + } + ] + }, + { + featureType: "water", + elementType: "geometry", + stylers: [ + { + color: "#DEE1E5" + } + ] + }, + { + featureType: "water", + elementType: "labels.text", + stylers: [ + { + visibility: "off" + } + ] + } ]; export const getThemeSpacingCoefficient = (theme) => { - const coefficient = theme.spacing(1).match(/[-]?[0-9]+\.?[0-9]*/g); + const coefficient = theme.spacing(1).match(/[-]?[0-9]+\.?[0-9]*/g); - if (coefficient !== null) { - return Number(coefficient[0]); - } + if (coefficient !== null) { + return Number(coefficient[0]); + } - return 0; + return 0; }; // Main theme config const getThemeConfig = (settings = {}) => { - const baseColorsPalette = applyPaletteSettings(settings); - const {primary, secondary, info, success, error, warning, text} = baseColorsPalette; + const baseColorsPalette = applyPaletteSettings(settings); + const {primary, secondary, info, success, error, warning, text} = baseColorsPalette; - const {chart, monoChart} = applyChartPaletteSettings(settings); + const {chart, monoChart} = applyChartPaletteSettings(settings); - const googleMapPalette = applyGoogleMapPaletteSettings(baseColorsPalette); + const googleMapPalette = applyGoogleMapPaletteSettings(baseColorsPalette); - // Actions - const ACTION_HOVER = getLighten(info.main, 0.9); - const ACTION_ACTIVE = primary.main; - const ACTION_SELECTED = secondary.main; + // Actions + const ACTION_HOVER = getLighten(info.main, 0.9); + const ACTION_ACTIVE = primary.main; + const ACTION_SELECTED = secondary.main; - // Misc - const BACKGROUND = getLighten(info.main, 0.95); - const SKELETON_COLOR = getLighten(info.main, 0.8); + // Misc + const BACKGROUND = getLighten(info.main, 0.95); + const SKELETON_COLOR = getLighten(info.main, 0.8); - return Object.freeze({ - typography: { - fontFamily: "'Ubuntu', sans-serif", - mono: { - fontFamily: "'Ubuntu Mono', monospace" + return Object.freeze({ + typography: { + fontFamily: "'Ubuntu', sans-serif", + mono: { + fontFamily: "'Ubuntu Mono', monospace" + } + }, + components: { + MuiAccordion: { + styleOverrides: { + root: { + "&:before": { + display: "none" } - }, - components: { - MuiAccordion: { - styleOverrides: { - root: { - "&:before": { - display: "none" - } - } - } - }, - MuiAccordionSummary: { - styleOverrides: { - content: { - maxWidth: "100%", - margin: 0, - "&.Mui-expanded": { - margin: 0 - } - }, - root: { - "&.Mui-expanded": { - minHeight: "48px", - color: secondary.contrastText, - backgroundColor: ACTION_SELECTED - } - }, - expandIconWrapper: { - "&.Mui-expanded": { - color: secondary.contrastText - } - } - } - }, - MuiSelect: { - styleOverrides: { - select: { - fontSize: '14px', - color: 'black', - paddingLeft: KU_SPACING_2, - paddingRight: KU_SPACING_3, - paddingTop: '6px', - paddingBottom: '6px' - }, - - root: { - "& .MuiSvgIcon-fontSizeSmall": { - fontSize: KU_SPACING_2 - }, - }, - iconOutlined: { - fontSize: '16px' // Set font size for MuiSelect-iconOutlined - } - } - }, - MuiOutlinedInput: { - styleOverrides: { - root: { - backgroundColor: 'white', - '& .MuiOutlinedInput-notchedOutline': { - borderColor: 'black', - color: 'black' - }, - '&:hover .MuiOutlinedInput-notchedOutline': { - borderColor: BRAND_PRIMARY - }, - '&.Mui-focused .MuiOutlinedInput-notchedOutline': { - borderColor: BRAND_PRIMARY - } - } - } - }, - MuiAutocomplete: { - styleOverrides: { - option: ({theme}) => ({ - "&.MuiAutocomplete-option": { - /* - Make option font styles similar to the MuiMenuItem - */ - ...theme.typography.body2, - /* - When options are selected, Autocomplete does not add any Mui classes, - so we need to rely on the aria-selected element property instead. - */ - "&[aria-selected='true']": { - backgroundColor: ACTION_SELECTED, - color: secondary.contrastText, - "&.Mui-focused": { - backgroundColor: ACTION_SELECTED - } - } - } - }) - } - }, - MuiButton: { - defaultProps: { - size: "small", - color: "info" - }, - variants: [ - { - props: {variant: "contained", color: "lightYellow"}, - style: ({theme}) => ({ - color: theme.palette.lightYellow.contrastText, - "&:hover": { - backgroundColor: lighten(theme.palette.lightYellow.main, 0.08) - } - }) - }, - { - props: {variant: "contained", color: "lightBlue"}, - style: ({theme}) => ({ - color: theme.palette.lightBlue.contrastText, - "&:hover": { - backgroundColor: lighten(theme.palette.lightBlue.main, 0.08) - } - }) - }, - { - props: {variant: "text", color: "info"}, - style: ({theme}) => ({ - color: theme.palette.text.primary - }) - } - ] - }, - MuiButtonGroup: { - defaultProps: { - color: "info" - } - }, - MuiCardHeader: { - styleOverrides: { - content: { - overflow: "hidden" - } - } - }, - MuiCheckbox: { - defaultProps: { - color: "secondary" - }, - styleOverrides: { - colorSecondary: { - color: secondary.main - } - } - }, - MuiCssBaseline: { - styleOverrides: { - "#root": {display: "flex", flexDirection: "column", minHeight: "100vh"} - } - }, - MuiDialogActions: { - styleOverrides: { - root: { - justifyContent: "center" - } - } - }, - MuiDialogTitle: { - styleOverrides: { - root: { - textAlign: "center" - } - } - }, - MuiFormControl: { - defaultProps: { - margin: "dense" - } - }, - MuiFormHelperText: { - defaultProps: { - margin: "dense" - } - }, - MuiIconButton: { - defaultProps: { - size: "small" - }, - styleOverrides: { - root: { - marginLeft: 0, - marginRight: 0, - padding: 4, - "&:hover": { - backgroundColor: alpha(ACTION_HOVER, 0.5) - } - }, - sizeSmall: { - padding: "10px", // Adjust padding to control button size - "& .MuiSvgIcon-root": { - fontSize: '20px', - }, - }, - } - }, - MuiInputLabel: { - styleOverrides: { - root: { - color: 'black' - } - }, - defaultProps: { - size: "small" - } - }, - MuiInputBase: { - defaultProps: { - size: "small" - } - }, - MuiLink: { - defaultProps: { - underline: "hover" - }, - }, - MuiSwitch: { - defaultProps: { - color: "secondary" - } - }, - MuiListItem: { - defaultProps: { - dense: true - }, - styleOverrides: { - dense: { - paddingTop: "0.375rem", - paddingBottom: "0.375rem" - }, - root: { - "&.Mui-selected": { - color: secondary.contrastText - }, - "&.Mui-focusVisible": { - backgroundColor: getLighten(secondary.main), - color: secondary.contrastText - } - } - } - }, - MuiListItemSecondaryAction: { - styleOverrides: { - root: { - right: "0.2rem" - } - } - }, - MuiMenuItem: { - defaultProps: { - dense: true - }, - styleOverrides: { - root: { - // https://github.com/mui-org/material-ui/issues/29842 - "&.Mui-selected": { - backgroundColor: ACTION_SELECTED, - color: secondary.contrastText, - "&.Mui-focusVisible": {background: ACTION_SELECTED}, - "&:hover": { - backgroundColor: ACTION_SELECTED - } - } - } - } - }, - MuiSkeleton: { - styleOverrides: { - root: { - backgroundColor: SKELETON_COLOR - } - } - }, - MuiStepLabel: { - styleOverrides: { - label: { - color: text.primary - }, - labelContainer: { - color: text.primary - } - } - }, - MuiTab: { - styleOverrides: { - root: { - minHeight: "3rem" - } - } - }, - MuiTable: { - defaultProps: { - size: "small" - } - }, - MuiTableSortLabel: { - styleOverrides: { - root: { - "&:hover": { - color: text.primary, - // Apply color only to the sort arrow icon - "> svg:last-child": { - color: primary.main - } - }, - "&.Mui-active": { - color: primary.main - } - } - } - }, - MuiAlert: { - styleOverrides: { - action: { - paddingTop: 0 - } - } - }, - MuiTabs: { - styleOverrides: { - root: { - minHeight: "2rem" - } - } - }, - MuiTextField: { - defaultProps: { - size: "small" - } - }, - MuiToolbar: { - defaultProps: { - variant: "dense" - }, - styleOverrides: { - dense: { - paddingRight: 0 - } - } - }, - MuiTypography: { - defaultProps: { - variant: "body2" - }, - styleOverrides: { - h6: { - color: 'black', - fontSize: '24px' - }, - } - }, - MuiUseMediaQuery: { - defaultProps: { - noSsr: true + } + } + }, + MuiAccordionSummary: { + styleOverrides: { + content: { + maxWidth: "100%", + margin: 0, + "&.Mui-expanded": { + margin: 0 + } + }, + root: { + "&.Mui-expanded": { + minHeight: "48px", + color: secondary.contrastText, + backgroundColor: ACTION_SELECTED + } + }, + expandIconWrapper: { + "&.Mui-expanded": { + color: secondary.contrastText + } + } + } + }, + MuiSelect: { + styleOverrides: { + select: { + fontSize: '14px', + color: 'black', + paddingLeft: KU_SPACING_2, + paddingRight: KU_SPACING_3, + paddingTop: '6px', + paddingBottom: '6px' + }, + + root: { + "& .MuiSvgIcon-fontSizeSmall": { + fontSize: KU_SPACING_2 + }, + }, + iconOutlined: { + fontSize: '16px' // Set font size for MuiSelect-iconOutlined + } + } + }, + MuiOutlinedInput: { + styleOverrides: { + root: { + backgroundColor: 'white', + '& .MuiOutlinedInput-notchedOutline': { + borderColor: 'black', + color: 'black' + }, + '&:hover .MuiOutlinedInput-notchedOutline': { + borderColor: BRAND_PRIMARY + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: BRAND_PRIMARY + } + } + } + }, + MuiAutocomplete: { + styleOverrides: { + option: ({theme}) => ({ + "&.MuiAutocomplete-option": { + /* + Make option font styles similar to the MuiMenuItem + */ + ...theme.typography.body2, + /* + When options are selected, Autocomplete does not add any Mui classes, + so we need to rely on the aria-selected element property instead. + */ + "&[aria-selected='true']": { + backgroundColor: ACTION_SELECTED, + color: secondary.contrastText, + "&.Mui-focused": { + backgroundColor: ACTION_SELECTED } + } } + }) + } + }, + MuiButton: { + defaultProps: { + size: "small", + color: "info" }, - palette: { - action: { - hover: ACTION_HOVER, - active: ACTION_ACTIVE, - selected: ACTION_SELECTED - }, - primary, - secondary, - info, - success, - error, - warning, - common, - background: { - default: BACKGROUND - }, - text, - lightYellow: { - main: "#FFC348" - }, - lightBlue: { - main: "#4AB4EE", - contrastText: common.white - }, - gold: { - main: "#FFD700" - }, - silver: { - main: "#C0C0C0" - }, - bronze: { - main: "#CD7F32" + variants: [ + { + props: {variant: "contained", color: "lightYellow"}, + style: ({theme}) => ({ + color: theme.palette.lightYellow.contrastText, + "&:hover": { + backgroundColor: lighten(theme.palette.lightYellow.main, 0.08) + } + }) + }, + { + props: {variant: "contained", color: "lightBlue"}, + style: ({theme}) => ({ + color: theme.palette.lightBlue.contrastText, + "&:hover": { + backgroundColor: lighten(theme.palette.lightBlue.main, 0.08) + } + }) + }, + { + props: {variant: "text", color: "info"}, + style: ({theme}) => ({ + color: theme.palette.text.primary + }) + } + ] + }, + MuiButtonGroup: { + defaultProps: { + color: "info" + } + }, + MuiCardHeader: { + styleOverrides: { + content: { + overflow: "hidden" + } + } + }, + MuiCard: { + styleOverrides: { + root: { + borderRadius: '16px', // Set your desired border-radius value + }, + }, + }, + MuiCheckbox: { + defaultProps: { + color: "secondary" + }, + styleOverrides: { + colorSecondary: { + color: secondary.main + }, + root: { + '& .MuiTableRowCheckbox-root': { // Only apply to table row checkboxes + color: 'gray', + '&.Mui-checked': { + color: '#000000' + }, + '& .MuiSvgIcon-root': { + fontSize: '16px' + } + } + } + } + }, + MuiCssBaseline: { + styleOverrides: { + "#root": { + display: "flex", flexDirection: "column", minHeight: "100vh" + }, + '.KuBoxShadowRoot': { + '> .MuiBox-root': { + boxShadow: KU_BOX_SHADOW, + background: 'white', + padding: KU_SPACING_2, + borderRadius: KU_SPACING_1 + } + + } + } + }, + MuiDialogActions: { + styleOverrides: { + root: { + justifyContent: "center" + } + } + }, + MuiDialogTitle: { + styleOverrides: { + root: { + textAlign: "center" + } + } + }, + MuiFormControl: { + defaultProps: { + margin: "dense" + } + }, + MuiFormHelperText: { + defaultProps: { + margin: "dense" + } + }, + MuiIconButton: { + defaultProps: { + size: "small" + }, + styleOverrides: { + root: { + marginLeft: 0, + marginRight: 0, + padding: 4, + "&:hover": { + backgroundColor: alpha(ACTION_HOVER, 0.5), + borderRadius: KU_SPACING_2 + } + }, + sizeSmall: { + padding: "10px", // Adjust padding to control button size + "& .MuiSvgIcon-root": { + fontSize: '20px', }, - chart, - monoChart, - googleMap: googleMapPalette, - json: { - default: text.primary, - error: error.main, - background: BACKGROUND, - background_warning: BACKGROUND, - string: text.primary, - number: text.primary, - colon: text.primary, - keys: text.primary, - keys_whiteSpace: text.primary, - primitive: text.primary + }, + } + }, + MuiInputLabel: { + styleOverrides: { + root: { + color: 'black' + } + }, + defaultProps: { + size: "small" + } + }, + MuiInputBase: { + defaultProps: { + size: "small" + } + }, + MuiLink: { + defaultProps: { + underline: "hover" + }, + }, + MuiSwitch: { + defaultProps: { + color: "secondary" + } + }, + MuiListItem: { + defaultProps: { + dense: true + }, + styleOverrides: { + dense: { + paddingTop: "0.375rem", + paddingBottom: "0.375rem" + }, + root: { + "&.Mui-selected": { + color: secondary.contrastText + }, + "&.Mui-focusVisible": { + backgroundColor: getLighten(secondary.main), + color: secondary.contrastText + } + } + } + }, + MuiListItemSecondaryAction: { + styleOverrides: { + root: { + right: "0.2rem" + } + } + }, + MuiMenuItem: { + defaultProps: { + dense: true + }, + styleOverrides: { + root: { + // https://github.com/mui-org/material-ui/issues/29842 + "&.Mui-selected": { + backgroundColor: ACTION_SELECTED, + color: secondary.contrastText, + "&.Mui-focusVisible": {background: ACTION_SELECTED}, + "&:hover": { + backgroundColor: ACTION_SELECTED + } + } + } + } + }, + MuiSkeleton: { + styleOverrides: { + root: { + backgroundColor: SKELETON_COLOR + } + } + }, + MuiStepLabel: { + styleOverrides: { + label: { + color: text.primary + }, + labelContainer: { + color: text.primary + } + } + }, + MuiTab: { + styleOverrides: { + root: { + minHeight: "3rem" + } + } + }, + MuiTable: { + defaultProps: { + size: "small" + }, + styleOverrides: { + root: { + // Add any additional styles for the table root here + } + } + }, + MuiTableHead: { + styleOverrides: { + root: {} + } + }, + MuiTableCell: { + styleOverrides: { + root: { + "&.MuiTableCell-head": { + borderLeft: `1px solid ${BRAND_GRAY_1}`, + borderBottom: "1px solid black" + }, + "& .MuiIconButton-root": { + padding: 0 + }, + "&.tableRowSelection svg": { + fill: 'black', + width: '19px', + height: '19px' + } + } + } + }, + MuiTableSortLabel: { + defaultProps: { + IconComponent: KeyboardArrowDownOutlinedIcon, // Default icon for descending + }, + styleOverrides: { + root: { + "&.Mui-active": { + color: "black", // Ensure active label color is black + "&[aria-sort='asc'] .MuiTableSortLabel-icon": { + transform: "rotate(0deg)", // Rotate icon for ascending + content: "''" // Clear any default content + }, + "&[aria-sort='desc'] .MuiTableSortLabel-icon": { + transform: "rotate(180deg)", // Rotate icon for descending + content: "''" // Clear any default content + } + } + } + }, + slotProps: { + icon: ({direction}) => { + if (direction === "asc") { + return { + component: KeyboardArrowUpOutlinedIcon + }; } + return { + component: KeyboardArrowDownOutlinedIcon + }; + } + } + }, + MuiAlert: { + styleOverrides: { + action: { + paddingTop: 0 + } + } + }, + MuiTabs: { + styleOverrides: { + root: { + minHeight: "2rem" + } + } + }, + MuiTextField: { + defaultProps: { + size: "small" } - }); + }, + MuiToolbar: { + defaultProps: { + variant: "dense" + }, + styleOverrides: { + dense: { + paddingRight: 0 + } + } + }, + MuiTypography: { + defaultProps: { + variant: "body2" + }, + styleOverrides: { + h6: { + color: 'black', + fontSize: '24px' + }, + } + }, + MuiUseMediaQuery: { + defaultProps: { + noSsr: true + } + } + }, + palette: { + action: { + hover: ACTION_HOVER, + active: ACTION_ACTIVE, + selected: ACTION_SELECTED + }, + primary, + secondary, + info, + success, + error, + warning, + common, + background: { + default: BACKGROUND + }, + text, + lightYellow: { + main: "#FFC348" + }, + lightBlue: { + main: "#4AB4EE", + contrastText: common.white + }, + gold: { + main: "#FFD700" + }, + silver: { + main: "#C0C0C0" + }, + bronze: { + main: "#CD7F32" + }, + chart, + monoChart, + googleMap: googleMapPalette, + json: { + default: text.primary, + error: error.main, + background: BACKGROUND, + background_warning: BACKGROUND, + string: text.primary, + number: text.primary, + colon: text.primary, + keys: text.primary, + keys_whiteSpace: text.primary, + primitive: text.primary + } + } + }); }; const PDF_THEME = { - fontSizes: { - summaryBig: 20, - summarySmall: 10, - h1: 17, - h2: 16, - text: 12, - footerNote: 10 - }, - colors: { - // TODO: Make this color configurable, get PRIMARY (`getLighten(PRIMARY)`) from `theme` - link: getLighten("#004C74") - }, - logoWidth: 120 + fontSizes: { + summaryBig: 20, + summarySmall: 10, + h1: 17, + h2: 16, + text: 12, + footerNote: 10 + }, + colors: { + // TODO: Make this color configurable, get PRIMARY (`getLighten(PRIMARY)`) from `theme` + link: getLighten("#004C74") + }, + logoWidth: 120 }; // TODO: applyChartPaletteSettings needs to be rewritten, adding one one chart palette affects multiple files, diff --git a/ngui/ui/src/utils/columns/tags.tsx b/ngui/ui/src/utils/columns/tags.tsx index c92b59c54..41dbe8a1c 100644 --- a/ngui/ui/src/utils/columns/tags.tsx +++ b/ngui/ui/src/utils/columns/tags.tsx @@ -33,7 +33,7 @@ const tags = ({ return CELL_EMPTY_VALUE; } - return ; + return ; } }); diff --git a/ngui/ui/src/utils/fonts.ts b/ngui/ui/src/utils/fonts.ts index f6f8ccad3..5bf9a0f6d 100644 --- a/ngui/ui/src/utils/fonts.ts +++ b/ngui/ui/src/utils/fonts.ts @@ -36,6 +36,7 @@ const applyTypographySettings = (themeInput, settings) => { theme.typography.body2 = mergeIfSettingIsNotEmpty(theme.typography.body2, "body2"); theme.typography.subtitle1 = mergeIfSettingIsNotEmpty(theme.typography.subtitle1, "subtitle1"); theme.typography.subtitle2 = mergeIfSettingIsNotEmpty(theme.typography.subtitle2, "subtitle2"); + theme.typography.fontWeightBold = mergeIfSettingIsNotEmpty(theme.typography.fontWeightBold, "fontWeightBold"); theme.typography.h1 = mergeIfSettingIsNotEmpty(theme.typography.h1, "h1"); theme.typography.h2 = mergeIfSettingIsNotEmpty(theme.typography.h2, "h2"); theme.typography.h3 = mergeIfSettingIsNotEmpty(theme.typography.h3, "h3"); @@ -86,8 +87,24 @@ const generateResponsiveFontSizes = (themeInput) => { } }; + theme.typography.fontWeightBold = { + ...theme.typography.fontWeightBold, + fontWeight: 'bold', + color: 'black', + [upXsBreakpoint]: { + fontSize: "0.75rem" + }, + [upLgBreakpoint]: { + fontSize: "0.8125rem" + }, + [upXlBreakpoint]: { + fontSize: "0.875rem" + } + }; + theme.typography.body1 = { ...theme.typography.body1, + color: 'black', [upXsBreakpoint]: { fontSize: "0.85rem" }, @@ -101,6 +118,7 @@ const generateResponsiveFontSizes = (themeInput) => { theme.typography.body2 = { ...theme.typography.body2, + color: 'black', [upXsBreakpoint]: { fontSize: "0.75rem" }, diff --git a/ngui/ui/src/utils/layouts.ts b/ngui/ui/src/utils/layouts.ts index e172d5a37..49f8748ca 100644 --- a/ngui/ui/src/utils/layouts.ts +++ b/ngui/ui/src/utils/layouts.ts @@ -15,6 +15,7 @@ export const KU_SPACING_3 = "24px"; export const KU_SPACING_4 = "32px"; export const KU_SPACING_5 = "40px"; export const KU_SPACING_6 = "48px"; +export const KU_BOX_SHADOW = `0 1px ${KU_SPACING_1} #6b718057` export const scrolledToBottom = (target) => target.scrollTop + target.clientHeight >= target.scrollHeight;