feat: update front layout

This commit is contained in:
Maël Gangloff 2024-07-26 18:31:47 +02:00
parent a15c1b2c2f
commit 467d0efa1c
No known key found for this signature in database
GPG Key ID: 11FDC81C24A7F629
13 changed files with 272 additions and 177 deletions

View File

@ -7,12 +7,14 @@ import {
FileSearchOutlined,
InfoCircleOutlined,
LineChartOutlined,
LoginOutlined,
LogoutOutlined,
QuestionCircleOutlined,
SearchOutlined,
TeamOutlined,
UserOutlined
} from "@ant-design/icons";
import {Navigate, Route, Routes, useNavigate} from "react-router-dom";
import {Navigate, Route, Routes, useLocation, useNavigate} from "react-router-dom";
import TextPage from "./pages/TextPage";
import tos from "./content/tos.md";
import privacy from "./content/privacy.md";
@ -23,171 +25,204 @@ import TldPage from "./pages/info/TldPage";
import StatisticsPage from "./pages/info/StatisticsPage";
import WatchlistsPage from "./pages/tracking/WatchlistsPage";
import UserPage from "./pages/UserPage";
import LoginPage from "./pages/LoginPage";
import React, {useEffect, useState} from "react";
import React, {useCallback, useEffect, useMemo, useState} from "react";
import {getUser} from "./utils/api";
import FAQPage from "./pages/FAQPage";
import LoginPage, {AuthenticatedContext} from "./pages/LoginPage";
import ConnectorsPage from "./pages/tracking/ConnectorsPage";
import NotFoundPage from "./pages/NotFoundPage";
export default function App() {
const {
token: {colorBgContainer, borderRadiusLG},
} = theme.useToken()
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
const navigate = useNavigate()
const navigate = useNavigate()
const [isAuthenticated, setIsAuthenticated] = useState(false)
const location = useLocation()
const authenticated = useCallback((authenticated: boolean) => {
setIsAuthenticated(authenticated)
}, []);
const contextValue = useMemo(() => ({
authenticated,
setIsAuthenticated
}), [authenticated, setIsAuthenticated]);
useEffect(() => {
getUser().then(() => setIsAuthenticated(true)).catch(() => setIsAuthenticated(false))
getUser().then(() => {
setIsAuthenticated(true)
if (location.pathname === '/login') navigate('/search/domain')
}).catch(() => {
setIsAuthenticated(false)
if (location.pathname !== '/login') navigate('/login')
})
}, []);
return <Layout hasSider style={{minHeight: '100vh'}}>
<Layout.Sider>
<Menu
defaultSelectedKeys={['1-1']}
defaultOpenKeys={['1', '2', '3']}
mode="inline"
theme="dark"
items={[
{
key: '1',
label: 'Search',
children: [
{
key: '1-1',
icon: <SearchOutlined/>,
label: 'Domain',
title: 'Domain Finder',
disabled: !isAuthenticated,
onClick: () => navigate('/search/domain')
},
{
key: '1-2',
icon: <TeamOutlined/>,
label: 'Entity',
title: 'Entity Finder',
disabled: !isAuthenticated,
onClick: () => navigate('/search/entity')
},
{
key: '1-3',
icon: <CloudServerOutlined/>,
label: 'Nameserver',
title: 'Nameserver Finder',
disabled: !isAuthenticated,
onClick: () => navigate('/search/nameserver')
}
]
},
{
key: '2',
label: 'Information',
children: [
{
key: '2-1',
icon: <BankOutlined/>,
label: 'TLD',
disabled: !isAuthenticated,
onClick: () => navigate('/info/tld')
},
{
key: '2-2',
icon: <LineChartOutlined/>,
label: 'Statistics',
disabled: !isAuthenticated,
onClick: () => navigate('/info/stats')
}
]
},
{
key: '3',
label: 'Tracking',
children: [
{
key: '3-1',
icon: <Badge count={0} size="small"><FileSearchOutlined
shape="square"/></Badge>,
label: 'My Watchlists',
disabled: !isAuthenticated,
onClick: () => navigate('/tracking/watchlist')
},
{
key: '3-2',
icon: <ApiOutlined/>,
label: 'My connectors',
disabled: !isAuthenticated,
onClick: () => navigate('/tracking/connectors')
}
]
},
{
key: '4',
icon: <UserOutlined/>,
label: 'My Account',
disabled: !isAuthenticated,
onClick: () => navigate('/user')
},
{
key: '5',
icon: <QuestionCircleOutlined/>,
label: 'FAQ',
onClick: () => navigate('/faq')
},
{
key: '6',
icon: <InfoCircleOutlined/>,
label: 'TOS',
onClick: () => navigate('/tos')
},
{
key: '7',
icon: <FileProtectOutlined/>,
label: 'Privacy Policy',
onClick: () => navigate('/privacy')
}
]}
/>
<div className="demo-logo-vertical"></div>
</Layout.Sider>
<Layout>
<Layout.Header style={{padding: 0, background: colorBgContainer}}/>
<Layout.Content style={{margin: '24px 16px 0'}}>
<div style={{
padding: 24,
minHeight: 360,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}>
const menuItems = [
{
key: '1',
label: 'Search',
children: [
{
key: '1-1',
icon: <SearchOutlined/>,
label: 'Domain',
title: 'Domain Finder',
disabled: !isAuthenticated,
onClick: () => navigate('/search/domain')
},
{
key: '1-2',
icon: <TeamOutlined/>,
label: 'Entity',
title: 'Entity Finder',
disabled: !isAuthenticated,
onClick: () => navigate('/search/entity')
},
{
key: '1-3',
icon: <CloudServerOutlined/>,
label: 'Nameserver',
title: 'Nameserver Finder',
disabled: !isAuthenticated,
onClick: () => navigate('/search/nameserver')
}
]
},
{
key: '2',
label: 'Information',
children: [
{
key: '2-1',
icon: <BankOutlined/>,
label: 'TLD',
disabled: !isAuthenticated,
onClick: () => navigate('/info/tld')
},
{
key: '2-2',
icon: <LineChartOutlined/>,
label: 'Statistics',
disabled: !isAuthenticated,
onClick: () => navigate('/info/stats')
}
]
},
{
key: '3',
label: 'Tracking',
children: [
{
key: '3-1',
icon: <Badge count={0} size="small"><FileSearchOutlined
shape="square"/></Badge>,
label: 'My Watchlists',
disabled: !isAuthenticated,
onClick: () => navigate('/tracking/watchlist')
},
{
key: '3-2',
icon: <ApiOutlined/>,
label: 'My connectors',
disabled: !isAuthenticated,
onClick: () => navigate('/tracking/connectors')
}
]
},
{
key: '4',
icon: <UserOutlined/>,
label: 'My Account',
disabled: !isAuthenticated,
onClick: () => navigate('/user')
},
{
key: '5',
icon: <QuestionCircleOutlined/>,
label: 'FAQ',
onClick: () => navigate('/faq')
},
{
key: '6',
icon: <InfoCircleOutlined/>,
label: 'TOS',
onClick: () => navigate('/tos')
},
{
key: '7',
icon: <FileProtectOutlined/>,
label: 'Privacy Policy',
onClick: () => navigate('/privacy')
}
]
<Routes>
<Route path="/tos" element={<TextPage markdown={tos}/>}/>
<Route path="/privacy" element={<TextPage markdown={privacy}/>}/>
{isAuthenticated ?
<>
<Route path="/" element={<Navigate to="/search/domain"/>}/>
return <AuthenticatedContext.Provider value={contextValue}>
<Layout hasSider style={{minHeight: '100vh'}}>
<Layout.Sider>
<Menu
defaultSelectedKeys={['1-1']}
defaultOpenKeys={['1', '2', '3']}
mode="inline"
theme="dark"
items={[...menuItems, isAuthenticated ? {
key: '8',
icon: <LogoutOutlined/>,
label: 'Log out',
danger: true,
onClick: () => window.location.replace("/logout")
} : {
key: '8',
icon: <LoginOutlined/>,
label: 'Log in',
onClick: () => navigate('/login')
}]}
/>
<div className="demo-logo-vertical"></div>
</Layout.Sider>
<Layout>
<Layout.Header style={{padding: 0, background: colorBgContainer}}/>
<Layout.Content style={{margin: '24px 16px 0'}}>
<div style={{
padding: 24,
minHeight: 360,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}>
<Route path="/search/domain" element={<DomainSearchPage/>}/>
<Route path="/search/entity" element={<EntitySearchPage/>}/>
<Route path="/search/nameserver" element={<NameserverSearchPage/>}/>
<Routes>
<Route path="/" element={<Navigate to="/search/domain"/>}/>
<Route path="/info/tld" element={<TldPage/>}/>
<Route path="/info/stats" element={<StatisticsPage/>}/>
<Route path="/search/domain" element={<DomainSearchPage/>}/>
<Route path="/search/entity" element={<EntitySearchPage/>}/>
<Route path="/search/nameserver" element={<NameserverSearchPage/>}/>
<Route path="/tracking/watchlist" element={<WatchlistsPage/>}/>
<Route path="/info/tld" element={<TldPage/>}/>
<Route path="/info/stats" element={<StatisticsPage/>}/>
<Route path="/user" element={<UserPage/>}/>
</>
:
<Route path="*" element={<LoginPage/>}/>
}
<Route path="/tracking/watchlist" element={<WatchlistsPage/>}/>
<Route path="/tracking/connectors" element={<ConnectorsPage/>}/>
</Routes>
</div>
</Layout.Content>
<Layout.Footer style={{textAlign: 'center'}}>
Domain Watchdog ©{new Date().getFullYear()} Created by Maël Gangloff
</Layout.Footer>
<Route path="/user" element={<UserPage/>}/>
<Route path="/faq" element={<FAQPage/>}/>
<Route path="/tos" element={<TextPage markdown={tos}/>}/>
<Route path="/privacy" element={<TextPage markdown={privacy}/>}/>
<Route path="/login" element={<LoginPage/>}/>
<Route path="*" element={<NotFoundPage/>}/>
</Routes>
</div>
</Layout.Content>
<Layout.Footer style={{textAlign: 'center'}}>
Domain Watchdog ©{new Date().getFullYear()} Created by Maël Gangloff
</Layout.Footer>
</Layout>
</Layout>
</Layout>
</AuthenticatedContext.Provider>
}

View File

@ -14,7 +14,6 @@ function Index() {
<HashRouter>
<App/>
</HashRouter>
);
}

8
assets/pages/FAQPage.tsx Normal file
View File

@ -0,0 +1,8 @@
import React from "react";
export default function FAQPage() {
return <p>
FAQ Page
</p>
}

View File

@ -1,7 +1,74 @@
import React from "react";
import React, {createContext, useContext, useEffect, useState} from "react";
import {Alert, Button, Card, Flex, Form, Input} from "antd";
import {login} from "../utils/api";
import {useNavigate} from "react-router-dom";
type FieldType = {
username: string;
password: string;
};
export const AuthenticatedContext = createContext<any>(null)
export default function Page() {
return <p>
Login Page
</p>
const [error, setError] = useState()
const navigate = useNavigate()
const {isAuthenticated, setIsAuthenticated} = useContext(AuthenticatedContext)
const onFinish = (data: FieldType) => {
login(data.username, data.password).then(() => {
setIsAuthenticated(true)
navigate('/search/domain')
}).catch((e) => {
setIsAuthenticated(false)
setError(e.response.data.message)
})
}
return <Flex gap="middle" align="center" justify="center" vertical><Card
title="Log in"
style={{width: 500}}
>
{error &&
<Alert
type='error'
message='Error'
banner={true}
role='role'
description={error}
style={{marginBottom: '1em'}}
/>}
<Form
name="basic"
labelCol={{span: 8}}
wrapperCol={{span: 16}}
style={{maxWidth: 600}}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item
label="Username"
name="username"
rules={[{required: true, message: 'Required'}]}
>
<Input/>
</Form.Item>
<Form.Item<FieldType>
label="Password"
name="password"
rules={[{required: true, message: 'Required'}]}
>
<Input.Password/>
</Form.Item>
<Form.Item wrapperCol={{offset: 8, span: 16}}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</Card>
</Flex>
}

View File

@ -1,4 +1,4 @@
import {Button, Result} from "antd";
import {Result} from "antd";
import React from "react";
@ -7,6 +7,5 @@ export default function NotFoundPage() {
status="404"
title="404"
subTitle="Sorry, the page you visited does not exist."
extra={<Button type="primary">Back Home</Button>}
/>
}

View File

@ -2,6 +2,6 @@ import React from "react";
export default function StatisticsPage() {
return <p>
Tld
Statistics Page
</p>
}

View File

@ -2,6 +2,6 @@ import React from "react";
export default function TldPage() {
return <p>
Tld
Tld Page
</p>
}

View File

@ -2,6 +2,6 @@ import React from "react";
export default function DomainSearchPage() {
return <p>
Domain Search Page
</p>
}

View File

@ -2,6 +2,6 @@ import React from "react";
export default function EntitySearchPage() {
return <p>
Entity Search Page
</p>
}

View File

@ -2,6 +2,6 @@ import React from "react";
export default function NameserverSearchPage() {
return <p>
NS Finder
NS Search Page
</p>
}

View File

@ -2,6 +2,6 @@ import React from "react";
export default function ConnectorsPage() {
return <p>
Connectors Page
</p>
}

View File

@ -2,6 +2,6 @@ import React from "react";
export default function WatchlistsPage() {
return <p>
Watchlists Page
</p>
}

View File

@ -8,21 +8,8 @@ interface Tld {
}
export async function getTldList(params: object): Promise<Tld[]> {
let page = 1
let response = (await request<Tld[]>({
return (await request<Tld[]>({
url: 'tld',
params: {...params, page},
params,
})).data
const tldList: Tld[] = response;
while (response.length !== 0) {
page++
response = (await request<Tld[]>({
url: 'tld',
params: {...params, page},
})).data
tldList.push(...response)
}
return tldList
}