Skip to content

add announcement page #103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
73 changes: 73 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
"@mui/icons-material": "^5.16.14",
"@mui/lab": "5.0.0-alpha.175",
"@mui/material": "^5.16.14",
"@mui/x-date-pickers": "^7.28.3",
"@mui/x-tree-view": "^7.28.1",
"@reduxjs/toolkit": "^2.5.1",
"ag-grid-community": "^33.1.0",
"ag-grid-react": "^33.1.0",
"core-js": "^3.40.0",
"dayjs": "^1.11.13",
"notistack": "^3.0.2",
"oidc-client": "^1.11.5",
"react": "^18.3.1",
Expand Down
22 changes: 20 additions & 2 deletions src/components/App/app-top-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
useState,
} from 'react';
import { capitalize, Tab, Tabs, useTheme } from '@mui/material';
import { Groups, ManageAccounts, PeopleAlt } from '@mui/icons-material';
import { fetchAppsMetadata, logout, Metadata, TopBar } from '@gridsuite/commons-ui';
import { Groups, ManageAccounts, NotificationImportant, PeopleAlt } from '@mui/icons-material';
import { fetchAppsMetadata, logout, Metadata, TopBar, useGlobalAnnouncement } from '@gridsuite/commons-ui';

Check failure on line 19 in src/components/App/app-top-bar.tsx

View workflow job for this annotation

GitHub Actions / build / build

Module '"@gridsuite/commons-ui"' has no exported member 'useGlobalAnnouncement'.
import { useParameterState } from '../parameters';
import { APP_NAME, PARAM_LANGUAGE, PARAM_THEME } from '../../utils/config-params';
import { NavLink, type To, useMatches, useNavigate } from 'react-router';
Expand Down Expand Up @@ -73,6 +73,20 @@
))}
/>,
],
[
MainPaths.banners,
<Tab
icon={<NotificationImportant />}
label={<FormattedMessage id="appBar.tabs.warningBanner" />}
href={`/${MainPaths.banners}`}
value={MainPaths.banners}
key={`tab-${MainPaths.banners}`}
iconPosition="start"
LinkComponent={forwardRef<HTMLAnchorElement, AnchorHTMLAttributes<HTMLAnchorElement>>((props, ref) => (
<NavLink ref={ref} to={props.href as To} {...props} />
))}
/>,
],
]);

const AppTopBar: FunctionComponent = () => {
Expand All @@ -96,6 +110,9 @@
const [languageLocal, handleChangeLanguage] = useParameterState(PARAM_LANGUAGE);

const [appsAndUrls, setAppsAndUrls] = useState<Metadata[]>([]);

const announcementInfos = useGlobalAnnouncement(user);

useEffect(() => {
if (user !== null) {
fetchAppsMetadata().then((res) => {
Expand All @@ -122,6 +139,7 @@
onLanguageClick={handleChangeLanguage}
language={languageLocal}
developerMode={false} // TODO: set as optional in commons-ui
announcementInfos={announcementInfos}

Check failure on line 142 in src/components/App/app-top-bar.tsx

View workflow job for this annotation

GitHub Actions / build / build

Type '{ children: Element; appName: "Admin"; appColor: string; appLogo: Element; appVersion: string; appLicense: string; onLogoutClick: () => Promise<void> | undefined; ... 10 more ...; announcementInfos: any; }' is not assignable to type 'IntrinsicAttributes & Omit<GridLogoProps, "onClick"> & Omit<LogoutProps, "disabled"> & Omit<AboutDialogProps, "onClose" | "open"> & { ...; } & { ...; }'.
>
<Tabs
component="nav"
Expand Down
10 changes: 7 additions & 3 deletions src/components/App/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
import { FunctionComponent, PropsWithChildren, useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Grid } from '@mui/material';
import { CardErrorBoundary, useNotificationsListener, useSnackMessage } from '@gridsuite/commons-ui';
import {
CardErrorBoundary,
NotificationsUrlKeys,

Check failure on line 13 in src/components/App/app.tsx

View workflow job for this annotation

GitHub Actions / build / build

Module '"@gridsuite/commons-ui"' has no exported member 'NotificationsUrlKeys'.
useNotificationsListener,
useSnackMessage,
} from '@gridsuite/commons-ui';
import { selectComputedLanguage, selectLanguage, selectTheme } from '../../redux/actions';
import { AppState } from '../../redux/reducer';
import { ConfigParameters, ConfigSrv } from '../../services';
Expand All @@ -17,7 +22,6 @@
import AppTopBar from './app-top-bar';
import { useDebugRender } from '../../utils/hooks';
import { AppDispatch } from '../../redux/store';
import { NOTIFICATIONS_URL_KEYS } from '../../utils/notifications-provider';

const App: FunctionComponent<PropsWithChildren<{}>> = (props, context) => {
useDebugRender('app');
Expand Down Expand Up @@ -59,7 +63,7 @@
[updateParams, snackError]
);

useNotificationsListener(NOTIFICATIONS_URL_KEYS.CONFIG, { listenerCallbackMessage: updateConfig });
useNotificationsListener(NotificationsUrlKeys.CONFIG, { listenerCallbackMessage: updateConfig });

useEffect(() => {
if (user !== null) {
Expand Down
151 changes: 151 additions & 0 deletions src/pages/banners/add-announcement-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright (c) 2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { CustomFormProvider, SubmitButton, useSnackMessage } from '@gridsuite/commons-ui';
import Grid from '@mui/material/Grid';
import { FormattedMessage, useIntl } from 'react-intl';
import React, { FunctionComponent, useCallback } from 'react';
import { FormControl, InputLabel, MenuItem, Select, TextField } from '@mui/material';
import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { UserAdminSrv, Announcement } from '../../services';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import yup from '../../utils/yup-config';
import 'dayjs/locale/fr';
import 'dayjs/locale/en';
import { useParameterState } from '../../components/parameters';
import { PARAM_LANGUAGE } from '../../utils/config-params';
import { getErrorMessage, handleAnnouncementCreationErrors } from '../../utils/error';

export const MESSAGE = 'message';
export const START_DATE = 'startDate';
export const END_DATE = 'endDate';
export const SEVERITY = 'severity';

interface AddAnnouncementProps {
onAnnouncementCreated: () => void;
}

const AddAnnouncementForm: FunctionComponent<AddAnnouncementProps> = ({ onAnnouncementCreated }) => {
const intl = useIntl();
const [languageLocal] = useParameterState(PARAM_LANGUAGE);
const { snackError } = useSnackMessage();

const formSchema = yup
.object()
.shape({
[MESSAGE]: yup.string().trim().required(),
[START_DATE]: yup.string().required(),
[END_DATE]: yup.string().required(),
[SEVERITY]: yup.string().required(),
})
.required();

const formMethods = useForm({
resolver: yupResolver(formSchema),
});

const { register, setValue, handleSubmit, formState } = formMethods;

const onSubmit = useCallback(
(params: any) => {
let startDate = new Date(params.startDate).toISOString();
let endDate = new Date(params.endDate).toISOString();
const newAnnouncement = {
id: crypto.randomUUID(),
message: params.message,
startDate: startDate,
endDate: endDate,
severity: params.severity,
} as Announcement;
UserAdminSrv.addAnnouncement(newAnnouncement)
.then(() => onAnnouncementCreated())
.catch((error) => {
let errorMessage = getErrorMessage(error) ?? '';
if (!handleAnnouncementCreationErrors(errorMessage, snackError)) {
snackError({
headerId: 'errCreateAnnouncement',
messageTxt: errorMessage,
});
}
});
},
[onAnnouncementCreated, snackError]
);

return (
<CustomFormProvider validationSchema={formSchema} {...formMethods}>
<Grid container spacing={1}>
<Grid item xs={4}>
<TextField
{...register('message')}
id="message-input"
label={intl.formatMessage({ id: 'banners.form.message' })}
multiline
rows={4}
fullWidth
inputProps={{ maxLength: 200 }}
/>
</Grid>
<Grid item xs={2}>
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={languageLocal}>
<DateTimePicker
{...register('startDate')}
name={START_DATE}
label={intl.formatMessage({ id: 'banners.table.startDate' })}
onChange={(newValue) => setValue('startDate', newValue?.toISOString() ?? '')}
/>
</LocalizationProvider>
</Grid>
<Grid item xs={2}>
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={languageLocal}>
<DateTimePicker
{...register('endDate')}
name={END_DATE}
label={intl.formatMessage({ id: 'banners.table.endDate' })}
onChange={(newValue) => setValue('endDate', newValue?.toISOString() ?? '')}
/>
</LocalizationProvider>
</Grid>

<Grid item xs={2}>
<FormControl fullWidth>
<InputLabel id="severity-input-label">
<FormattedMessage id="banners.table.severity" />
</InputLabel>
<Select
{...register('severity')}
name={SEVERITY}
label={intl.formatMessage({ id: 'banners.table.severity' })}
fullWidth={true}
defaultValue={''}
>
<MenuItem value={UserAdminSrv.AnnouncementSeverity.INFO}>
{intl.formatMessage({ id: 'banners.table.info' })}
</MenuItem>
<MenuItem value={UserAdminSrv.AnnouncementSeverity.WARN}>
{intl.formatMessage({ id: 'banners.table.warn' })}
</MenuItem>
</Select>
</FormControl>
</Grid>

<Grid item xs={2}>
<SubmitButton
variant="outlined"
onClick={handleSubmit(onSubmit)}
fullWidth={true}
disabled={!formState.isValid || formState.isValidating}
/>
</Grid>
</Grid>
</CustomFormProvider>
);
};

export default AddAnnouncementForm;
Loading
Loading