feat: introduce notifications page
This commit is contained in:

committed by
Ömer Faruk Aydın

parent
db021c06c7
commit
d373d04cdf
@@ -7,6 +7,7 @@ import AppsIcon from '@mui/icons-material/Apps';
|
|||||||
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
|
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
|
||||||
import HistoryIcon from '@mui/icons-material/History';
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
import ExploreIcon from '@mui/icons-material/Explore';
|
import ExploreIcon from '@mui/icons-material/Explore';
|
||||||
|
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||||
|
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import AppBar from 'components/AppBar';
|
import AppBar from 'components/AppBar';
|
||||||
@@ -39,6 +40,14 @@ const drawerLinks = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const drawerBottomLinks = [
|
||||||
|
{
|
||||||
|
Icon: NotificationsIcon,
|
||||||
|
primary: 'settingsDrawer.notifications',
|
||||||
|
to: URLS.UPDATES,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
export default function PublicLayout({ children }: PublicLayoutProps): React.ReactElement {
|
export default function PublicLayout({ children }: PublicLayoutProps): React.ReactElement {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('lg'), { noSsr: true });
|
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('lg'), { noSsr: true });
|
||||||
@@ -54,6 +63,7 @@ export default function PublicLayout({ children }: PublicLayoutProps): React.Rea
|
|||||||
<Box sx={{ display: 'flex', }}>
|
<Box sx={{ display: 'flex', }}>
|
||||||
<Drawer
|
<Drawer
|
||||||
links={drawerLinks}
|
links={drawerLinks}
|
||||||
|
bottomLinks={drawerBottomLinks}
|
||||||
open={isDrawerOpen}
|
open={isDrawerOpen}
|
||||||
onOpen={openDrawer}
|
onOpen={openDrawer}
|
||||||
onClose={closeDrawer}
|
onClose={closeDrawer}
|
||||||
|
56
packages/web/src/components/NotificationCard/index.tsx
Normal file
56
packages/web/src/components/NotificationCard/index.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardHeader from '@mui/material/CardHeader';
|
||||||
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
|
||||||
|
interface NotificationCardProps {
|
||||||
|
name: string;
|
||||||
|
createdAt: string;
|
||||||
|
documentationUrl: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getHumanlyDate = (timestamp: number) => DateTime.fromMillis(timestamp).toRelative();
|
||||||
|
|
||||||
|
export default function NotificationCard(props: NotificationCardProps) {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
createdAt,
|
||||||
|
documentationUrl,
|
||||||
|
description,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
const relativeCreatedAt = getHumanlyDate((new Date(createdAt)).getTime());
|
||||||
|
const subheader = formatMessage('notification.releasedAt', { relativeDate: relativeCreatedAt });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardActionArea
|
||||||
|
component={'a'}
|
||||||
|
href={documentationUrl}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<CardHeader
|
||||||
|
title={name}
|
||||||
|
titleTypographyProps={{ variant: 'h6' }}
|
||||||
|
subheader={subheader}
|
||||||
|
sx={{ borderBottom: '1px solid', borderColor: 'divider'}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CardContent>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
color="text.secondary"
|
||||||
|
dangerouslySetInnerHTML={{ __html: description }}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,16 +1,10 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography, { TypographyProps } from '@mui/material/Typography';
|
||||||
|
|
||||||
type PageTitleProps = {
|
type PageTitleProps = TypographyProps;
|
||||||
children: React.ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function PageTitle(props: PageTitleProps): React.ReactElement {
|
export default function PageTitle(props: PageTitleProps): React.ReactElement {
|
||||||
const { children } = props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Typography variant="h3">
|
<Typography variant="h3" {...props} />
|
||||||
{children}
|
|
||||||
</Typography>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
@@ -5,6 +5,7 @@ type Config = {
|
|||||||
const config: Config = {
|
const config: Config = {
|
||||||
baseUrl: process.env.REACT_APP_BASE_URL as string,
|
baseUrl: process.env.REACT_APP_BASE_URL as string,
|
||||||
graphqlUrl: process.env.REACT_APP_GRAPHQL_URL as string,
|
graphqlUrl: process.env.REACT_APP_GRAPHQL_URL as string,
|
||||||
|
notificationsUrl: process.env.REACT_APP_NOTIFICATIONS_URL as string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
@@ -31,8 +31,8 @@ export const FLOW_PATTERN = '/flows/:flowId';
|
|||||||
|
|
||||||
export const SETTINGS = '/settings';
|
export const SETTINGS = '/settings';
|
||||||
export const SETTINGS_DASHBOARD = SETTINGS;
|
export const SETTINGS_DASHBOARD = SETTINGS;
|
||||||
export const SETTINGS_PROFILE = '/settings/profile';
|
|
||||||
export const PROFILE = 'profile';
|
export const PROFILE = 'profile';
|
||||||
|
export const UPDATES = '/updates';
|
||||||
|
export const SETTINGS_PROFILE = `${SETTINGS}/${PROFILE}`;
|
||||||
|
|
||||||
export const DASHBOARD = FLOWS;
|
export const DASHBOARD = FLOWS;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
"drawer.explore": "Explore",
|
"drawer.explore": "Explore",
|
||||||
"settingsDrawer.myProfile": "My Profile",
|
"settingsDrawer.myProfile": "My Profile",
|
||||||
"settingsDrawer.goBack": "Go to the dashboard",
|
"settingsDrawer.goBack": "Go to the dashboard",
|
||||||
|
"settingsDrawer.notifications": "Notifications",
|
||||||
"app.connectionCount": "{count} connections",
|
"app.connectionCount": "{count} connections",
|
||||||
"app.flowCount": "{count} flows",
|
"app.flowCount": "{count} flows",
|
||||||
"app.addConnection": "Add connection",
|
"app.addConnection": "Add connection",
|
||||||
@@ -49,5 +50,7 @@
|
|||||||
"profileSettings.confirmNewPassword": "Confirm new password",
|
"profileSettings.confirmNewPassword": "Confirm new password",
|
||||||
"profileSettings.updatedEmail": "Your email has been updated.",
|
"profileSettings.updatedEmail": "Your email has been updated.",
|
||||||
"profileSettings.updatedPassword": "Your password has been updated.",
|
"profileSettings.updatedPassword": "Your password has been updated.",
|
||||||
"profileSettings.updatePassword": "Update password"
|
"profileSettings.updatePassword": "Update password",
|
||||||
|
"notifications.title": "Notifications",
|
||||||
|
"notification.releasedAt": "Released {relativeDate}"
|
||||||
}
|
}
|
||||||
|
@@ -29,7 +29,7 @@ export default function CreateFlow(): React.ReactElement {
|
|||||||
});
|
});
|
||||||
const flowId = response.data?.createFlow?.id;
|
const flowId = response.data?.createFlow?.id;
|
||||||
|
|
||||||
navigate(URLS.FLOW_EDITOR(flowId));
|
navigate(URLS.FLOW_EDITOR(flowId), { replace: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
initiate();
|
initiate();
|
||||||
|
56
packages/web/src/pages/Notifications/index.tsx
Normal file
56
packages/web/src/pages/Notifications/index.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
|
||||||
|
import appConfig from 'config/app';
|
||||||
|
import Container from 'components/Container';
|
||||||
|
import NotificationCard from 'components/NotificationCard';
|
||||||
|
import PageTitle from 'components/PageTitle';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
|
||||||
|
interface IUpdate {
|
||||||
|
name: string;
|
||||||
|
createdAt: string;
|
||||||
|
documentationUrl: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Updates(): React.ReactElement {
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
const [updates, setUpdates] = React.useState<IUpdate[]>([]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetch(`${appConfig.notificationsUrl}/notifications.json`)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((updates) => {
|
||||||
|
if (Array.isArray(updates) && updates.length) {
|
||||||
|
setUpdates(updates);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ py: 3 }}>
|
||||||
|
<Container>
|
||||||
|
<PageTitle sx={{ mb: [2, 5] }}>
|
||||||
|
{formatMessage('notifications.title')}
|
||||||
|
</PageTitle>
|
||||||
|
|
||||||
|
<Stack
|
||||||
|
gap={2}
|
||||||
|
>
|
||||||
|
{updates.map((update: IUpdate) => (
|
||||||
|
<NotificationCard
|
||||||
|
key={update.name}
|
||||||
|
name={`Version ${update.name}`}
|
||||||
|
description={update.description}
|
||||||
|
createdAt={update.createdAt}
|
||||||
|
documentationUrl={update.documentationUrl}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@@ -12,6 +12,8 @@ import Login from 'pages/Login';
|
|||||||
import EditorRoutes from 'pages/Editor/routes';
|
import EditorRoutes from 'pages/Editor/routes';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import settingsRoutes from './settingsRoutes';
|
import settingsRoutes from './settingsRoutes';
|
||||||
|
import Notifications from 'pages/Notifications';
|
||||||
|
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
<Routes>
|
<Routes>
|
||||||
@@ -33,6 +35,8 @@ export default (
|
|||||||
|
|
||||||
<Route path={URLS.LOGIN} element={<PublicLayout><Login /></PublicLayout>} />
|
<Route path={URLS.LOGIN} element={<PublicLayout><Login /></PublicLayout>} />
|
||||||
|
|
||||||
|
<Route path={URLS.UPDATES} element={<Layout><Notifications /></Layout>} />
|
||||||
|
|
||||||
<Route path="/" element={<Navigate to={URLS.FLOWS} />} />
|
<Route path="/" element={<Navigate to={URLS.FLOWS} />} />
|
||||||
|
|
||||||
<Route path={`${URLS.SETTINGS}`}>
|
<Route path={`${URLS.SETTINGS}`}>
|
||||||
|
Reference in New Issue
Block a user