feat: update front

This commit is contained in:
Maël Gangloff
2024-07-23 20:34:27 +02:00
parent 827ca478c6
commit d564e200ed
13 changed files with 70 additions and 515 deletions

View File

@@ -4,12 +4,11 @@ import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Button from '@mui/material/Button';
import Container from '@mui/material/Container';
import Sitemark from './SitemarkIcon';
import {NavLink} from "react-router-dom";
import ToggleColorMode from "./ToggleColorMode";
import {PaletteMode} from "@mui/material";
import Link from "@mui/material/Link";
import {Pets} from "@mui/icons-material";
interface AppAppBarProps {
mode: PaletteMode;
@@ -47,30 +46,9 @@ export default function AppAppBar({mode, toggleColorMode, isAuthenticated}: AppA
})}
>
<Box sx={{flexGrow: 1, display: 'flex', alignItems: 'center', px: 0}}>
<Sitemark/>
<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>
<Pets color="secondary"/>
</Box>
<Box
sx={{
display: {xs: 'none', md: 'flex'},
@@ -100,5 +78,6 @@ export default function AppAppBar({mode, toggleColorMode, isAuthenticated}: AppA
</Toolbar>
</Container>
</AppBar>
);
)
}

View File

@@ -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>
);
}

View File

@@ -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&nbsp;
<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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -1,7 +1,5 @@
import React, {useEffect, useState} from "react";
import ReactDOM from "react-dom/client";
import LandingPage from "./pages/LandingPage";
import TextPage from "./pages/TextPage";
import {HashRouter, Route, Routes} from "react-router-dom";
@@ -36,15 +34,14 @@ function App() {
<CssBaseline/>
<AppAppBar mode={mode} toggleColorMode={toggleColorMode} isAuthenticated={isAuthenticated}/>
<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="/privacy" element={<TextPage content={privacyContent}/>}/>
{isAuthenticated ?
<Route path="/dashboard" element={<DashboardPage/>}/>
:
<Route path="*" element={<LoginPage isAuthenticated={isAuthenticated}
setIsAuthenticated={setIsAuthenticated}/>}/>
}
</Routes>
</HashRouter>
</ThemeProvider>

View File

@@ -1,12 +1,10 @@
import React from 'react';
const DashboardPage = () => {
export default function DashboardPage() {
return (
<>
<p>Dashboard</p>
</>
);
};
export default DashboardPage;

View File

@@ -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>
</>
);
}

View File

@@ -1,4 +1,5 @@
import * as React from 'react';
import {useState} from 'react';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
@@ -10,28 +11,28 @@ import Footer from "../components/Footer";
import Link from "@mui/material/Link";
import {login} from "../utils/api";
import {useNavigate} from "react-router-dom";
import {Alert} from "@mui/material";
interface Props {
setIsAuthenticated: (val: boolean) => void
isAuthenticated: boolean
}
export default function LoginPage({setIsAuthenticated, isAuthenticated}: Props) {
const navigate = useNavigate();
export default function LoginPage({setIsAuthenticated}: Props) {
const navigate = useNavigate()
const [error, setError] = useState<string>('')
const [credentials, setCredentials] = useState({email: "", password: ""})
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
event.preventDefault()
try {
await login(data.get('email') as string, data.get('password') as string);
await login(credentials.email, credentials.password);
setIsAuthenticated(true)
navigate('/');
navigate('/dashboard');
} catch (e) {
//TODO: handle error
setIsAuthenticated(false)
} catch (e: any) {
setCredentials({email: "", password: ""})
setError(e.response.data.message)
}
};
@@ -53,6 +54,9 @@ export default function LoginPage({setIsAuthenticated, isAuthenticated}: Props)
Sign in
</Typography>
<Box component="form" onSubmit={handleSubmit} noValidate sx={{mt: 1}}>
{
error !== "" && <Alert variant="outlined" severity="error">{error}</Alert>
}
<TextField
margin="normal"
required
@@ -62,6 +66,8 @@ export default function LoginPage({setIsAuthenticated, isAuthenticated}: Props)
name="email"
autoComplete="email"
autoFocus
value={credentials.email}
onChange={(e) => setCredentials({...credentials, email: e.currentTarget.value})}
/>
<TextField
margin="normal"
@@ -71,7 +77,9 @@ export default function LoginPage({setIsAuthenticated, isAuthenticated}: Props)
label="Password"
type="password"
id="password"
value={credentials.password}
autoComplete="current-password"
onChange={(e) => setCredentials({...credentials, password: e.currentTarget.value})}
/>
<Button
type="submit"

View File

@@ -8,31 +8,29 @@ interface Props {
export default function Index({content}: Props) {
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={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: {xs: 4, sm: 8},
py: {xs: 8, sm: 10},
textAlign: {sm: 'center', md: 'left'},
justifyContent: 'space-between',
pt: {xs: 4, sm: 8},
width: '100%',
borderTop: '1px solid',
borderColor: 'divider',
}}
>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
pt: {xs: 4, sm: 8},
width: '100%',
borderTop: '1px solid',
borderColor: 'divider',
}}
>
<div dangerouslySetInnerHTML={{__html: content}}></div>
</Box>
</Container>
</>
<div dangerouslySetInnerHTML={{__html: content}}></div>
</Box>
</Container>
)
}

View File

@@ -32,7 +32,6 @@
"html-loader": "^5.0.0",
"jsonld": "^8.3.2",
"markdown-loader": "^8.0.0",
"raw-loader": "^4.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.25.1",

View File

@@ -53,14 +53,26 @@ Encore
config.corejs = '3.23';
})
// enables Sass/SCSS support
//.enableSassLoader()
// enables Sass/SCSS support
//.enableSassLoader()
// uncomment if you use TypeScript
.enableTypeScriptLoader()
// uncomment if you use TypeScript
.enableTypeScriptLoader()
// uncomment if you use React
.enableReactPreset()
// uncomment if you use React
.enableReactPreset()
.addLoader({
test: /\.md$/,
use: [
{
loader: "html-loader",
},
{
loader: "markdown-loader"
},
],
})
// uncomment to get integrity="..." attributes on your script & link tags
// requires WebpackEncoreBundle 1.4 or higher

View File

@@ -4307,14 +4307,6 @@ raw-body@2.5.2:
iconv-lite "0.4.24"
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:
version "3.4.0"
resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-3.4.0.tgz#87f88342b173cc371d812a07de350f0c1aa9f058"
@@ -4565,7 +4557,7 @@ scheduler@^0.23.2:
dependencies:
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"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==