diff --git a/assets/components/AppAppBar.tsx b/assets/components/AppAppBar.tsx new file mode 100644 index 0000000..f321a93 --- /dev/null +++ b/assets/components/AppAppBar.tsx @@ -0,0 +1,148 @@ +import * as React from 'react'; +import {PaletteMode} from '@mui/material'; +import Box from '@mui/material/Box'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import Button from '@mui/material/Button'; +import IconButton from '@mui/material/IconButton'; +import Container from '@mui/material/Container'; +import Divider from '@mui/material/Divider'; +import MenuItem from '@mui/material/MenuItem'; +import Drawer from '@mui/material/Drawer'; +import MenuIcon from '@mui/icons-material/Menu'; +import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; +import ToggleColorMode from './ToggleColorMode'; + +import Sitemark from './SitemarkIcon'; +import {NavLink} from "react-router-dom"; + +interface AppAppBarProps { + mode: PaletteMode; + toggleColorMode: () => void; +} + +export default function AppAppBar({mode, toggleColorMode}: AppAppBarProps) { + const [open, setOpen] = React.useState(false); + + const toggleDrawer = (newOpen: boolean) => () => { + setOpen(newOpen); + }; + + const scrollToSection = (sectionId: string) => { + const sectionElement = document.getElementById(sectionId); + const offset = 128; + if (sectionElement) { + const targetScroll = sectionElement.offsetTop - offset; + sectionElement.scrollIntoView({behavior: 'smooth'}); + window.scrollTo({ + top: targetScroll, + behavior: 'smooth', + }); + setOpen(false); + } + }; + + return ( + + + ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + flexShrink: 0, + borderRadius: '999px', + backdropFilter: 'blur(24px)', + maxHeight: 40, + border: '1px solid', + borderColor: 'divider', + bgcolor: 'hsla(220, 60%, 99%, 0.6)', + boxShadow: + '0 1px 2px hsla(210, 0%, 0%, 0.05), 0 2px 12px hsla(210, 100%, 80%, 0.5)', + ...theme.applyStyles('dark', { + bgcolor: 'hsla(220, 0%, 0%, 0.7)', + boxShadow: + '0 1px 2px hsla(210, 0%, 0%, 0.5), 0 2px 12px hsla(210, 100%, 25%, 0.3)', + }), + })} + > + + + + + + + + + + + + + + + + + + + + + + + + + + + scrollToSection('highlights')}> + Highlights + + scrollToSection('faq')}>FAQ + + + + + + + + + + ); +} diff --git a/assets/components/FAQ.tsx b/assets/components/FAQ.tsx new file mode 100644 index 0000000..4e86b62 --- /dev/null +++ b/assets/components/FAQ.tsx @@ -0,0 +1,183 @@ +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(false); + + const handleChange = + (panel: string) => (event: React.SyntheticEvent, isExpanded: boolean) => { + setExpanded(isExpanded ? panel : false); + }; + + return ( + + + Frequently asked questions + + + + } + aria-controls="panel1d-content" + id="panel1d-header" + > + + May I reuse the data obtained from Domain Watchdog? + + + + + Although the source code of this project is open source, the license does not + extend to the data collected by it.
+ 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.
+
+ For each domain, Domain Watchdog tells you which RDAP server was contacted. It is your + responsibility to check the conditions of use of this server. +
+
+
+ + } + aria-controls="panel2d-content" + id="panel2d-header" + > + + What is an RDAP server? + + + + + 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.
+
+ 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).
+
+ Domain Watchdog uses the RDAP protocol, which will soon be the new standard for retrieving + information concerning domain names. +
+
+
+ + } + aria-controls="panel3d-content" + id="panel3d-header" + > + + What are Domain Watchdog's data sources? + + + + + 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. + + + + + } + aria-controls="panel4d-content" + id="panel4d-header" + > + + What is the added value of Domain Watchdog rather than doing RDAP queries yourself? + + + + + 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. + + + + + } + aria-controls="panel5d-content" + id="panel5d-header" + > + + Under what license is the source code for this project released? + + + + + 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. + + + +
+
+ ); +} diff --git a/assets/components/Footer.tsx b/assets/components/Footer.tsx new file mode 100644 index 0000000..19b1ab0 --- /dev/null +++ b/assets/components/Footer.tsx @@ -0,0 +1,73 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Container from '@mui/material/Container'; +import IconButton from '@mui/material/IconButton'; +import Link from '@mui/material/Link'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; + +import FacebookIcon from '@mui/icons-material/GitHub'; + +function Copyright() { + return ( + + {'Copyright © '} + Domain Watchdog  + {new Date().getFullYear()} + + ); +} + +export default function Footer() { + return ( + + +
+ + Privacy Policy + + +  •  + + + Terms of Service + + +
+ + + + + +
+
+ ); +} diff --git a/assets/components/Hero.tsx b/assets/components/Hero.tsx new file mode 100644 index 0000000..b325fc4 --- /dev/null +++ b/assets/components/Hero.tsx @@ -0,0 +1,97 @@ +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 ( + ({ + 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)', + }), + })} + > + + + + Domain  + ({ + fontSize: 'inherit', + color: 'primary.main', + ...theme.applyStyles('dark', { + color: 'primary.light', + }), + })} + > + Watchdog + + + + Explore the fascinating history of domain names with Domain Watchdog. + This service collects open access information about domain names, helping track changes. + + + + + ); +} diff --git a/assets/components/Highlights.tsx b/assets/components/Highlights.tsx new file mode 100644 index 0000000..8e95fe1 --- /dev/null +++ b/assets/components/Highlights.tsx @@ -0,0 +1,108 @@ +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: , + title: 'Virtuous RDAP requests', + description: + 'Domain Watchdog is designed to make as few RDAP requests as possible so as not to overload them.', + }, + { + icon: , + title: 'Open access API', + description: + 'The Domain Watchdog API is accessible to all its users.', + }, + { + icon: , + title: 'Open Source', + description: + 'The project is licensed under AGPL-3.0. The source code is freely available on GitHub.', + }, + { + icon: , + 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 ( + + + + + Highlights + + + Here are the reasons why Domain Watchdog is the solution for domain name tracking. + + + + {items.map((item, index) => ( + + + {item.icon} +
+ + {item.title} + + + {item.description} + +
+
+
+ ))} +
+
+
+ ); +} diff --git a/assets/components/SitemarkIcon.tsx b/assets/components/SitemarkIcon.tsx new file mode 100644 index 0000000..2ba55c3 --- /dev/null +++ b/assets/components/SitemarkIcon.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import SvgIcon from '@mui/material/SvgIcon'; + +export default function SitemarkIcon() { + return ( + + + + + ); +} diff --git a/assets/components/ToggleColorMode.tsx b/assets/components/ToggleColorMode.tsx new file mode 100644 index 0000000..9e0c04c --- /dev/null +++ b/assets/components/ToggleColorMode.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import {PaletteMode} from '@mui/material'; +import IconButton, {IconButtonProps} from '@mui/material/IconButton'; + +import WbSunnyRoundedIcon from '@mui/icons-material/WbSunnyRounded'; +import ModeNightRoundedIcon from '@mui/icons-material/ModeNightRounded'; + +interface ToggleColorModeProps extends IconButtonProps { + mode: PaletteMode; + toggleColorMode: () => void; +} + +export default function ToggleColorMode({ + mode, + toggleColorMode, + ...props + }: ToggleColorModeProps) { + return ( + + {mode === 'dark' ? ( + + ) : ( + + )} + + ); +} diff --git a/frontend/.gitignore b/assets/controllers/.gitkeep similarity index 100% rename from frontend/.gitignore rename to assets/controllers/.gitkeep diff --git a/assets/index.tsx b/assets/index.tsx new file mode 100644 index 0000000..ad2810c --- /dev/null +++ b/assets/index.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; + +import LandingPage from "./pages/LandingPage"; +import {Route, Routes, HashRouter} from "react-router-dom"; +import FAQ from "./components/FAQ"; + + +const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); +root.render( + + + + }/> + + + +); diff --git a/assets/pages/LandingPage.tsx b/assets/pages/LandingPage.tsx new file mode 100644 index 0000000..0b6bea6 --- /dev/null +++ b/assets/pages/LandingPage.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import {PaletteMode} from '@mui/material'; +import CssBaseline from '@mui/material/CssBaseline'; +import Box from '@mui/material/Box'; +import Divider from '@mui/material/Divider'; +import {createTheme, ThemeProvider} from '@mui/material/styles'; +import AppAppBar from '../components/AppAppBar'; +import Hero from '../components/Hero'; +import Highlights from '../components/Highlights'; +import FAQ from '../components/FAQ'; +import Footer from '../components/Footer'; + +interface ToggleCustomThemeProps { + showCustomTheme: Boolean; + toggleCustomTheme: () => void; +} + +export default function Index() { + const [mode, setMode] = React.useState('light'); + const defaultTheme = createTheme({palette: {mode}}); + + const toggleColorMode = () => { + setMode((prev) => (prev === 'dark' ? 'light' : 'dark')); + }; + + return ( + + + + + + + + + + +