mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-29 16:15:04 +00:00
feat: update front
This commit is contained in:
@@ -4,12 +4,11 @@ import AppBar from '@mui/material/AppBar';
|
|||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import Container from '@mui/material/Container';
|
import Container from '@mui/material/Container';
|
||||||
|
|
||||||
import Sitemark from './SitemarkIcon';
|
|
||||||
import {NavLink} from "react-router-dom";
|
import {NavLink} from "react-router-dom";
|
||||||
import ToggleColorMode from "./ToggleColorMode";
|
import ToggleColorMode from "./ToggleColorMode";
|
||||||
import {PaletteMode} from "@mui/material";
|
import {PaletteMode} from "@mui/material";
|
||||||
import Link from "@mui/material/Link";
|
import Link from "@mui/material/Link";
|
||||||
|
import {Pets} from "@mui/icons-material";
|
||||||
|
|
||||||
interface AppAppBarProps {
|
interface AppAppBarProps {
|
||||||
mode: PaletteMode;
|
mode: PaletteMode;
|
||||||
@@ -47,30 +46,9 @@ export default function AppAppBar({mode, toggleColorMode, isAuthenticated}: AppA
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Box sx={{flexGrow: 1, display: 'flex', alignItems: 'center', px: 0}}>
|
<Box sx={{flexGrow: 1, display: 'flex', alignItems: 'center', px: 0}}>
|
||||||
<Sitemark/>
|
<Pets color="secondary"/>
|
||||||
<Box sx={{display: {xs: 'none', md: 'flex'}}}>
|
|
||||||
<NavLink to='/'>
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
color="info"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
Presentation
|
|
||||||
</Button>
|
|
||||||
</NavLink>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{display: {xs: 'none', md: 'flex'}}}>
|
|
||||||
<NavLink to="/dashboard">
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
color="info"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
Dashboard
|
|
||||||
</Button>
|
|
||||||
</NavLink>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: {xs: 'none', md: 'flex'},
|
display: {xs: 'none', md: 'flex'},
|
||||||
@@ -100,5 +78,6 @@ export default function AppAppBar({mode, toggleColorMode, isAuthenticated}: AppA
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</Container>
|
</Container>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
);
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import Accordion from '@mui/material/Accordion';
|
|
||||||
import AccordionDetails from '@mui/material/AccordionDetails';
|
|
||||||
import AccordionSummary from '@mui/material/AccordionSummary';
|
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import Container from '@mui/material/Container';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
|
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|
||||||
|
|
||||||
export default function FAQ() {
|
|
||||||
const [expanded, setExpanded] = React.useState<string | false>(false);
|
|
||||||
|
|
||||||
const handleChange =
|
|
||||||
(panel: string) => (event: React.SyntheticEvent, isExpanded: boolean) => {
|
|
||||||
setExpanded(isExpanded ? panel : false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container
|
|
||||||
id="faq"
|
|
||||||
sx={{
|
|
||||||
pt: {xs: 4, sm: 12},
|
|
||||||
pb: {xs: 8, sm: 16},
|
|
||||||
position: 'relative',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: {xs: 3, sm: 6},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
component="h2"
|
|
||||||
variant="h4"
|
|
||||||
sx={{
|
|
||||||
color: 'text.primary',
|
|
||||||
width: {sm: '100%', md: '60%'},
|
|
||||||
textAlign: {sm: 'left', md: 'center'},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Frequently asked questions
|
|
||||||
</Typography>
|
|
||||||
<Box sx={{width: '100%'}}>
|
|
||||||
<Accordion
|
|
||||||
expanded={expanded === 'panel1'}
|
|
||||||
onChange={handleChange('panel1')}
|
|
||||||
>
|
|
||||||
<AccordionSummary
|
|
||||||
expandIcon={<ExpandMoreIcon/>}
|
|
||||||
aria-controls="panel1d-content"
|
|
||||||
id="panel1d-header"
|
|
||||||
>
|
|
||||||
<Typography component="h3" variant="subtitle2">
|
|
||||||
May I reuse the data obtained from Domain Watchdog?
|
|
||||||
</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
gutterBottom
|
|
||||||
sx={{maxWidth: {sm: '100%', md: '70%'}}}
|
|
||||||
>
|
|
||||||
Although the source code of this project is open source, the license does not
|
|
||||||
extend to the data collected by it.<br/>
|
|
||||||
This data is redistributed under the same conditions as when it was obtained.
|
|
||||||
This means that you must respect the reuse conditions of each of the RDAP servers used.<br/>
|
|
||||||
<br/>
|
|
||||||
For each domain, Domain Watchdog tells you which RDAP server was contacted. <b>It is your
|
|
||||||
responsibility to check the conditions of use of this server.</b>
|
|
||||||
</Typography>
|
|
||||||
</AccordionDetails>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion
|
|
||||||
expanded={expanded === 'panel2'}
|
|
||||||
onChange={handleChange('panel2')}
|
|
||||||
>
|
|
||||||
<AccordionSummary
|
|
||||||
expandIcon={<ExpandMoreIcon/>}
|
|
||||||
aria-controls="panel2d-content"
|
|
||||||
id="panel2d-header"
|
|
||||||
>
|
|
||||||
<Typography component="h3" variant="subtitle2">
|
|
||||||
What is an RDAP server?
|
|
||||||
</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
gutterBottom
|
|
||||||
sx={{maxWidth: {sm: '100%', md: '70%'}}}
|
|
||||||
>
|
|
||||||
The latest version of the WHOIS protocol was standardized in 2004 by RFC 3912 This
|
|
||||||
protocol allows anyone to retrieve key information concerning a domain name, an IP address,
|
|
||||||
or an entity registered with a registry.<br/>
|
|
||||||
<br/>
|
|
||||||
ICANN launched a global vote in 2023 to propose replacing the WHOIS protocol with RDAP. As a
|
|
||||||
result, registries and registrars will no longer be required to support WHOIS from 2025
|
|
||||||
(WHOIS Sunset Date).<br/>
|
|
||||||
<br/>
|
|
||||||
Domain Watchdog uses the RDAP protocol, which will soon be the new standard for retrieving
|
|
||||||
information concerning domain names.
|
|
||||||
</Typography>
|
|
||||||
</AccordionDetails>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion
|
|
||||||
expanded={expanded === 'panel3'}
|
|
||||||
onChange={handleChange('panel3')}
|
|
||||||
>
|
|
||||||
<AccordionSummary
|
|
||||||
expandIcon={<ExpandMoreIcon/>}
|
|
||||||
aria-controls="panel3d-content"
|
|
||||||
id="panel3d-header"
|
|
||||||
>
|
|
||||||
<Typography component="h3" variant="subtitle2">
|
|
||||||
What are Domain Watchdog's data sources?
|
|
||||||
</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
gutterBottom
|
|
||||||
sx={{maxWidth: {sm: '100%', md: '70%'}}}
|
|
||||||
>
|
|
||||||
This project relies on open access data.
|
|
||||||
Domain Watchdog uses the RDAP protocol, which will soon be the new standard for retrieving
|
|
||||||
information concerning domain names.
|
|
||||||
</Typography>
|
|
||||||
</AccordionDetails>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion
|
|
||||||
expanded={expanded === 'panel4'}
|
|
||||||
onChange={handleChange('panel4')}
|
|
||||||
>
|
|
||||||
<AccordionSummary
|
|
||||||
expandIcon={<ExpandMoreIcon/>}
|
|
||||||
aria-controls="panel4d-content"
|
|
||||||
id="panel4d-header"
|
|
||||||
>
|
|
||||||
<Typography component="h3" variant="subtitle2">
|
|
||||||
What is the added value of Domain Watchdog rather than doing RDAP queries yourself?
|
|
||||||
</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
gutterBottom
|
|
||||||
sx={{maxWidth: {sm: '100%', md: '70%'}}}
|
|
||||||
>
|
|
||||||
Although the RDAP and WHOIS protocols allow you to obtain precise information about a
|
|
||||||
domain, it is not possible to perform a reverse search to discover a list of domain names
|
|
||||||
associated with an entity. Additionally, accessing a detailed history of events (ownership
|
|
||||||
changes, renewals, etc.) is not feasible with these protocols.
|
|
||||||
</Typography>
|
|
||||||
</AccordionDetails>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion
|
|
||||||
expanded={expanded === 'panel5'}
|
|
||||||
onChange={handleChange('panel5')}
|
|
||||||
>
|
|
||||||
<AccordionSummary
|
|
||||||
expandIcon={<ExpandMoreIcon/>}
|
|
||||||
aria-controls="panel5d-content"
|
|
||||||
id="panel5d-header"
|
|
||||||
>
|
|
||||||
<Typography component="h3" variant="subtitle2">
|
|
||||||
Under what license is the source code for this project released?
|
|
||||||
</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
gutterBottom
|
|
||||||
sx={{maxWidth: {sm: '100%', md: '70%'}}}
|
|
||||||
>
|
|
||||||
This entire project is licensed under GNU Affero General Public License v3.0 or later.
|
|
||||||
The source code is published on GitHub and freely accessible.
|
|
||||||
</Typography>
|
|
||||||
</AccordionDetails>
|
|
||||||
</Accordion>
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import Container from '@mui/material/Container';
|
|
||||||
import Stack from '@mui/material/Stack';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import {styled} from '@mui/material/styles';
|
|
||||||
|
|
||||||
const StyledBox = styled('div')(({theme}) => ({
|
|
||||||
alignSelf: 'center',
|
|
||||||
width: '100%',
|
|
||||||
height: 400,
|
|
||||||
marginTop: theme.spacing(8),
|
|
||||||
borderRadius: theme.shape.borderRadius,
|
|
||||||
outline: '1px solid',
|
|
||||||
boxShadow: '0 0 12px 8px hsla(220, 25%, 80%, 0.2)',
|
|
||||||
backgroundImage: `url(${'/static/images/templates/templates-images/hero-light.png'})`,
|
|
||||||
outlineColor: 'hsla(220, 25%, 80%, 0.5)',
|
|
||||||
backgroundSize: 'cover',
|
|
||||||
[theme.breakpoints.up('sm')]: {
|
|
||||||
marginTop: theme.spacing(10),
|
|
||||||
height: 700,
|
|
||||||
},
|
|
||||||
...theme.applyStyles('dark', {
|
|
||||||
boxShadow: '0 0 24px 12px hsla(210, 100%, 25%, 0.2)',
|
|
||||||
backgroundImage: `url(${'/static/images/templates/templates-images/hero-dark.png'})`,
|
|
||||||
outlineColor: 'hsla(210, 100%, 80%, 0.1)',
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default function Hero() {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
id="hero"
|
|
||||||
sx={(theme) => ({
|
|
||||||
width: '100%',
|
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
backgroundImage:
|
|
||||||
'radial-gradient(ellipse 80% 50% at 50% -20%, hsl(210, 100%, 90%), transparent)',
|
|
||||||
...theme.applyStyles('dark', {
|
|
||||||
backgroundImage:
|
|
||||||
'radial-gradient(ellipse 80% 50% at 50% -20%, hsl(210, 100%, 16%), transparent)',
|
|
||||||
}),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Container
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
pt: {xs: 14, sm: 20},
|
|
||||||
pb: {xs: 8, sm: 12},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
spacing={2}
|
|
||||||
useFlexGap
|
|
||||||
sx={{alignItems: 'center', width: {xs: '100%', sm: '70%'}}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
variant="h1"
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: {xs: 'column', sm: 'row'},
|
|
||||||
alignItems: 'center',
|
|
||||||
fontSize: 'clamp(3rem, 10vw, 3.5rem)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Domain
|
|
||||||
<Typography
|
|
||||||
component="span"
|
|
||||||
variant="h1"
|
|
||||||
sx={(theme) => ({
|
|
||||||
fontSize: 'inherit',
|
|
||||||
color: 'primary.main',
|
|
||||||
...theme.applyStyles('dark', {
|
|
||||||
color: 'primary.light',
|
|
||||||
}),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
Watchdog
|
|
||||||
</Typography>
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
textAlign: 'center',
|
|
||||||
color: 'text.secondary',
|
|
||||||
width: {sm: '100%', md: '80%'},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Explore the fascinating history of domain names with Domain Watchdog.
|
|
||||||
This service collects open access information about domain names, helping track changes.
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import Card from '@mui/material/Card';
|
|
||||||
import Container from '@mui/material/Container';
|
|
||||||
import Grid from '@mui/material/Grid';
|
|
||||||
import Stack from '@mui/material/Stack';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import AutoFixHighRoundedIcon from '@mui/icons-material/AutoFixHighRounded';
|
|
||||||
import QueryStatsRoundedIcon from '@mui/icons-material/QueryStatsRounded';
|
|
||||||
import SettingsSuggestRoundedIcon from '@mui/icons-material/SettingsSuggestRounded';
|
|
||||||
import ThumbUpAltRoundedIcon from '@mui/icons-material/ThumbUpAltRounded';
|
|
||||||
|
|
||||||
const items = [
|
|
||||||
{
|
|
||||||
icon: <SettingsSuggestRoundedIcon/>,
|
|
||||||
title: 'Virtuous RDAP requests',
|
|
||||||
description:
|
|
||||||
'Domain Watchdog is designed to make as few RDAP requests as possible so as not to overload them.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <ThumbUpAltRoundedIcon/>,
|
|
||||||
title: 'Open access API',
|
|
||||||
description:
|
|
||||||
'The Domain Watchdog API is accessible to all its users.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <AutoFixHighRoundedIcon/>,
|
|
||||||
title: 'Open Source',
|
|
||||||
description:
|
|
||||||
'The project is licensed under AGPL-3.0. The source code is freely available on GitHub.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <QueryStatsRoundedIcon/>,
|
|
||||||
title: 'Data quality',
|
|
||||||
description:
|
|
||||||
'The data is retrieved from official top-level domain name registries. Once collected, this data is made available to users of this service.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function Highlights() {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
id="highlights"
|
|
||||||
sx={{
|
|
||||||
pt: {xs: 4, sm: 12},
|
|
||||||
pb: {xs: 8, sm: 16},
|
|
||||||
color: 'white',
|
|
||||||
bgcolor: 'hsl(220, 30%, 2%)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Container
|
|
||||||
sx={{
|
|
||||||
position: 'relative',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: {xs: 3, sm: 6},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: {sm: '100%', md: '60%'},
|
|
||||||
textAlign: {sm: 'left', md: 'center'},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography component="h2" variant="h4">
|
|
||||||
Highlights
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body1" sx={{color: 'grey.400'}}>
|
|
||||||
Here are the reasons why Domain Watchdog is the solution for domain name tracking.
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Grid container spacing={2.5}>
|
|
||||||
{items.map((item, index) => (
|
|
||||||
<Grid item xs={6} sm={6} md={6} key={index}>
|
|
||||||
<Stack
|
|
||||||
direction="column"
|
|
||||||
component={Card}
|
|
||||||
spacing={1}
|
|
||||||
useFlexGap
|
|
||||||
sx={{
|
|
||||||
color: 'inherit',
|
|
||||||
p: 3,
|
|
||||||
height: '100%',
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor: 'hsla(220, 25%, 25%, .3)',
|
|
||||||
background: 'transparent',
|
|
||||||
backgroundColor: 'grey.900',
|
|
||||||
boxShadow: 'none',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{opacity: '50%'}}>{item.icon}</Box>
|
|
||||||
<div>
|
|
||||||
<Typography gutterBottom sx={{fontWeight: 'medium'}}>
|
|
||||||
{item.title}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2" sx={{color: 'grey.400'}}>
|
|
||||||
{item.description}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import SvgIcon from '@mui/material/SvgIcon';
|
|
||||||
|
|
||||||
export default function SitemarkIcon() {
|
|
||||||
return (
|
|
||||||
<SvgIcon sx={{height: 21, width: 100, mr: 2}}>
|
|
||||||
<svg
|
|
||||||
width={86}
|
|
||||||
height={19}
|
|
||||||
viewBox="0 0 86 19"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
</svg>
|
|
||||||
</SvgIcon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
|
|
||||||
import LandingPage from "./pages/LandingPage";
|
|
||||||
import TextPage from "./pages/TextPage";
|
import TextPage from "./pages/TextPage";
|
||||||
import {HashRouter, Route, Routes} from "react-router-dom";
|
import {HashRouter, Route, Routes} from "react-router-dom";
|
||||||
|
|
||||||
@@ -36,15 +34,14 @@ function App() {
|
|||||||
<CssBaseline/>
|
<CssBaseline/>
|
||||||
<AppAppBar mode={mode} toggleColorMode={toggleColorMode} isAuthenticated={isAuthenticated}/>
|
<AppAppBar mode={mode} toggleColorMode={toggleColorMode} isAuthenticated={isAuthenticated}/>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<LandingPage/>}/>
|
{isAuthenticated ?
|
||||||
|
<Route path="/" element={<DashboardPage/>}/>
|
||||||
|
:
|
||||||
|
<Route path="*" element={<LoginPage setIsAuthenticated={setIsAuthenticated}/>}/>
|
||||||
|
}
|
||||||
<Route path="/tos" element={<TextPage content={tosContent}/>}/>
|
<Route path="/tos" element={<TextPage content={tosContent}/>}/>
|
||||||
<Route path="/privacy" element={<TextPage content={privacyContent}/>}/>
|
<Route path="/privacy" element={<TextPage content={privacyContent}/>}/>
|
||||||
{isAuthenticated ?
|
|
||||||
<Route path="/dashboard" element={<DashboardPage/>}/>
|
|
||||||
:
|
|
||||||
<Route path="*" element={<LoginPage isAuthenticated={isAuthenticated}
|
|
||||||
setIsAuthenticated={setIsAuthenticated}/>}/>
|
|
||||||
}
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
|
||||||
const DashboardPage = () => {
|
export default function DashboardPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>Dashboard</p>
|
<p>Dashboard</p>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardPage;
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import Divider from '@mui/material/Divider';
|
|
||||||
import Hero from '../components/Hero';
|
|
||||||
import Highlights from '../components/Highlights';
|
|
||||||
import FAQ from '../components/FAQ';
|
|
||||||
import Footer from '../components/Footer';
|
|
||||||
|
|
||||||
export default function Index() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Hero/>
|
|
||||||
<Box sx={{bgcolor: 'background.default'}}>
|
|
||||||
<Divider/>
|
|
||||||
<Highlights/>
|
|
||||||
<Divider/>
|
|
||||||
<FAQ/>
|
|
||||||
<Divider/>
|
|
||||||
<Footer/>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import {useState} from 'react';
|
||||||
import Avatar from '@mui/material/Avatar';
|
import Avatar from '@mui/material/Avatar';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
@@ -10,28 +11,28 @@ import Footer from "../components/Footer";
|
|||||||
import Link from "@mui/material/Link";
|
import Link from "@mui/material/Link";
|
||||||
import {login} from "../utils/api";
|
import {login} from "../utils/api";
|
||||||
import {useNavigate} from "react-router-dom";
|
import {useNavigate} from "react-router-dom";
|
||||||
|
import {Alert} from "@mui/material";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setIsAuthenticated: (val: boolean) => void
|
setIsAuthenticated: (val: boolean) => void
|
||||||
isAuthenticated: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LoginPage({setIsAuthenticated, isAuthenticated}: Props) {
|
export default function LoginPage({setIsAuthenticated}: Props) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
const [error, setError] = useState<string>('')
|
||||||
|
const [credentials, setCredentials] = useState({email: "", password: ""})
|
||||||
|
|
||||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
const data = new FormData(event.currentTarget);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await login(data.get('email') as string, data.get('password') as string);
|
await login(credentials.email, credentials.password);
|
||||||
setIsAuthenticated(true)
|
setIsAuthenticated(true)
|
||||||
|
navigate('/');
|
||||||
|
|
||||||
navigate('/dashboard');
|
} catch (e: any) {
|
||||||
|
setCredentials({email: "", password: ""})
|
||||||
} catch (e) {
|
setError(e.response.data.message)
|
||||||
//TODO: handle error
|
|
||||||
setIsAuthenticated(false)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,6 +54,9 @@ export default function LoginPage({setIsAuthenticated, isAuthenticated}: Props)
|
|||||||
Sign in
|
Sign in
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box component="form" onSubmit={handleSubmit} noValidate sx={{mt: 1}}>
|
<Box component="form" onSubmit={handleSubmit} noValidate sx={{mt: 1}}>
|
||||||
|
{
|
||||||
|
error !== "" && <Alert variant="outlined" severity="error">{error}</Alert>
|
||||||
|
}
|
||||||
<TextField
|
<TextField
|
||||||
margin="normal"
|
margin="normal"
|
||||||
required
|
required
|
||||||
@@ -62,6 +66,8 @@ export default function LoginPage({setIsAuthenticated, isAuthenticated}: Props)
|
|||||||
name="email"
|
name="email"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
autoFocus
|
autoFocus
|
||||||
|
value={credentials.email}
|
||||||
|
onChange={(e) => setCredentials({...credentials, email: e.currentTarget.value})}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
margin="normal"
|
margin="normal"
|
||||||
@@ -71,7 +77,9 @@ export default function LoginPage({setIsAuthenticated, isAuthenticated}: Props)
|
|||||||
label="Password"
|
label="Password"
|
||||||
type="password"
|
type="password"
|
||||||
id="password"
|
id="password"
|
||||||
|
value={credentials.password}
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
|
onChange={(e) => setCredentials({...credentials, password: e.currentTarget.value})}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
@@ -8,31 +8,29 @@ interface Props {
|
|||||||
|
|
||||||
export default function Index({content}: Props) {
|
export default function Index({content}: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<Container
|
||||||
<Container
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: {xs: 4, sm: 8},
|
||||||
|
py: {xs: 8, sm: 10},
|
||||||
|
textAlign: {sm: 'center', md: 'left'},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
pt: {xs: 4, sm: 8},
|
||||||
gap: {xs: 4, sm: 8},
|
width: '100%',
|
||||||
py: {xs: 8, sm: 10},
|
borderTop: '1px solid',
|
||||||
textAlign: {sm: 'center', md: 'left'},
|
borderColor: 'divider',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<div dangerouslySetInnerHTML={{__html: content}}></div>
|
||||||
sx={{
|
</Box>
|
||||||
display: 'flex',
|
</Container>
|
||||||
justifyContent: 'space-between',
|
|
||||||
pt: {xs: 4, sm: 8},
|
|
||||||
width: '100%',
|
|
||||||
borderTop: '1px solid',
|
|
||||||
borderColor: 'divider',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div dangerouslySetInnerHTML={{__html: content}}></div>
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
"html-loader": "^5.0.0",
|
"html-loader": "^5.0.0",
|
||||||
"jsonld": "^8.3.2",
|
"jsonld": "^8.3.2",
|
||||||
"markdown-loader": "^8.0.0",
|
"markdown-loader": "^8.0.0",
|
||||||
"raw-loader": "^4.0.2",
|
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.25.1",
|
"react-router-dom": "^6.25.1",
|
||||||
|
|||||||
@@ -53,14 +53,26 @@ Encore
|
|||||||
config.corejs = '3.23';
|
config.corejs = '3.23';
|
||||||
})
|
})
|
||||||
|
|
||||||
// enables Sass/SCSS support
|
// enables Sass/SCSS support
|
||||||
//.enableSassLoader()
|
//.enableSassLoader()
|
||||||
|
|
||||||
// uncomment if you use TypeScript
|
// uncomment if you use TypeScript
|
||||||
.enableTypeScriptLoader()
|
.enableTypeScriptLoader()
|
||||||
|
|
||||||
// uncomment if you use React
|
// uncomment if you use React
|
||||||
.enableReactPreset()
|
.enableReactPreset()
|
||||||
|
|
||||||
|
.addLoader({
|
||||||
|
test: /\.md$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "html-loader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "markdown-loader"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
// uncomment to get integrity="..." attributes on your script & link tags
|
// uncomment to get integrity="..." attributes on your script & link tags
|
||||||
// requires WebpackEncoreBundle 1.4 or higher
|
// requires WebpackEncoreBundle 1.4 or higher
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@@ -4307,14 +4307,6 @@ raw-body@2.5.2:
|
|||||||
iconv-lite "0.4.24"
|
iconv-lite "0.4.24"
|
||||||
unpipe "1.0.0"
|
unpipe "1.0.0"
|
||||||
|
|
||||||
raw-loader@^4.0.2:
|
|
||||||
version "4.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6"
|
|
||||||
integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==
|
|
||||||
dependencies:
|
|
||||||
loader-utils "^2.0.0"
|
|
||||||
schema-utils "^3.0.0"
|
|
||||||
|
|
||||||
rdf-canonize@^3.4.0:
|
rdf-canonize@^3.4.0:
|
||||||
version "3.4.0"
|
version "3.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-3.4.0.tgz#87f88342b173cc371d812a07de350f0c1aa9f058"
|
resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-3.4.0.tgz#87f88342b173cc371d812a07de350f0c1aa9f058"
|
||||||
@@ -4565,7 +4557,7 @@ scheduler@^0.23.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0:
|
schema-utils@^3.1.1, schema-utils@^3.2.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
|
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
|
||||||
integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
|
integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
|
||||||
|
|||||||
Reference in New Issue
Block a user