Skip to content

Commit

Permalink
feat: frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
visualDust committed Nov 24, 2023
1 parent 61b9f96 commit 44de0e2
Show file tree
Hide file tree
Showing 33 changed files with 3,559 additions and 1 deletion.
7 changes: 6 additions & 1 deletion neetbox/daemon/server/_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Dict, Tuple

import setproctitle
from flask import Flask, abort, json, request
from flask import Flask, abort, json, request, send_from_directory
from flask_socketio import SocketIO
from flask_socketio import emit as ws_emit
from flask_socketio import send as ws_send
Expand Down Expand Up @@ -186,6 +186,11 @@ def handle_ws_json_message(data):

# ======================== HTTP SERVER ===========================

@app.route(f"/<path:path>", methods=["GET"])
def handle_frontend_static_resources(path): # forward frontend http json to client ws
package_root = os.path.dirname(neetbox.__file__)
return send_from_directory(package_root + '/frontend/dist/', path)

@app.route(f"{FRONTEND_API_ROOT}/wsforward/<name>", methods=["POST"])
def handle_json_forward_to_client_ws(name): # forward frontend http json to client ws
data = request.json
Expand Down
21 changes: 21 additions & 0 deletions neetbox/frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'import/order': 'warn'
},
}
24 changes: 24 additions & 0 deletions neetbox/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
30 changes: 30 additions & 0 deletions neetbox/frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```

- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
13 changes: 13 additions & 0 deletions neetbox/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Neetbox</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
37 changes: 37 additions & 0 deletions neetbox/frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "neetcenter",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build-with-tsc": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@douyinfe/semi-ui": "^2.47.0",
"@semi-bot/semi-theme-nyx-c": "^1.0.8",
"echarts": "^5.4.3",
"jotai": "^2.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.19.0",
"swr": "^2.2.4",
"vite-plugin-semi-theme": "^0.5.0"
},
"devDependencies": {
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"@vitejs/plugin-react": "^4.2.0",
"eslint": "^8.53.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
"typescript": "^5.2.2",
"vite": "^5.0.0"
}
}
1 change: 1 addition & 0 deletions neetbox/frontend/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions neetbox/frontend/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions neetbox/frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
Layout,
Button,
Space,
Toast,
Typography,
Nav,
Avatar,
Form,
Checkbox,
} from "@douyinfe/semi-ui";
import React from "react";
import SwitchColorMode from "./components/themeSwitcher";
import { Link, Outlet } from "react-router-dom";
import "./styles/global.css";
import FooterContent from "./components/Footer";

const { Header, Footer, Content } = Layout;

export default function AppLayout() {
return (
<Layout className="components-layout-demo" style={{ minHeight: "100vh" }}>
<Header
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "0 20px",
}}
>
<Typography.Title>
<Link to="/">NEET Center</Link>
</Typography.Title>
<div>
<Space>
<SwitchColorMode></SwitchColorMode>
<Link to="/login">
<Button>Login</Button>
</Link>
</Space>
</div>
</Header>
<Layout>
<Content style={{ flex: "1" }}>
<Outlet />
</Content>
</Layout>
<Footer children={<FooterContent/>}/>
</Layout>
);
}

export function Home() {
return (
<div>
<Link to="/console">
<Button>console</Button>
</Link>
</div>
);
}
14 changes: 14 additions & 0 deletions neetbox/frontend/src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React, { Component } from "react";
import Logo from "./logo";

export default function FooterContent(): React.JSX.Element {
return (
<div style={{ textAlign: "center" }}>
<Logo styles={{ width: 50 }} withGlow={true} withLink={true} />
<br />
<div style={{ fontSize: 20 }}>
© 2023 - 2023 Neet Design. All rights reserved.
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";
import { Toast, Button, Card, CardGroup, Typography } from "@douyinfe/semi-ui";
import { IconCopy } from "@douyinfe/semi-icons";

function PropCard({ propName, propValue }): React.JSX.Element {
const { Text } = Typography;
const content = Array.isArray(propValue) ? propValue.join(" ") : propValue;
const nameMapping = {
username: "Launched by",
machine: "Machine type",
processor: "Processor name",
os_name: "System/OS name",
os_release: "Sys release ver",
architecture: "Python arch.",
python_build: "Python build",
python_version: "Python version",
};
return (
<Card
shadows="hover"
title={nameMapping[propName] ?? propName}
headerLine={true}
headerStyle={{ padding: "10px" }}
bodyStyle={{
padding: "10px",
minHeight: "50px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
textAlign: "center",
}}
style={{ width: 260 }}
headerExtraContent={
<Button
icon={<IconCopy />}
style={{ marginRight: 10 }}
size="small"
onClick={() => {
navigator.clipboard.writeText(propValue).then(
() => {
// copy success
Toast.info("Copied to clipboard");
},
() => {
// copy failed
Toast.error("Failed to copy");
}
);
}}
></Button>
}
>
<Text>{content}</Text>
</Card>
);
}

export default function PlatformProps({ data }): React.JSX.Element {
return (
<div>
<CardGroup spacing={10}>
{Object.entries(data.value).map(([key, value]) => (
<PropCard propName={key} propValue={value} />
))}
</CardGroup>
</div>
);
}
30 changes: 30 additions & 0 deletions neetbox/frontend/src/components/echarts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useRef, useEffect, HTMLAttributes } from "react";
import * as echarts from "echarts";

export interface EChartsProps {
initialOption: () => echarts.EChartsOption;
updatingOption: echarts.EChartsOption;
style?: HTMLAttributes<HTMLElement>['style'];
}

export const ECharts = (props: EChartsProps) => {
const chartContainerRef = useRef(null);
const chartRef = useRef<echarts.ECharts>(null!);

useEffect(() => {
const chart = echarts.init(chartContainerRef.current);

chart.setOption(props.initialOption());
chartRef.current = chart;

return () => {
chart.dispose();
};
}, []);

useEffect(() => {
chartRef.current.setOption(props.updatingOption);
}, [props.updatingOption]);

return <div ref={chartContainerRef} style={props.style} />;
};
15 changes: 15 additions & 0 deletions neetbox/frontend/src/components/logo.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
body {
--logo-glow-color: rgb(215, 76, 76);
}

body[theme-mode="dark"] {
--logo-glow-color: rgb(20, 76, 255);
}

.neet-logo-glow {
transition: filter 0.3s ease-in-out;
}

.neet-logo-glow:hover {
filter: drop-shadow(0 0 0.55rem var(--logo-glow-color));
}
30 changes: 30 additions & 0 deletions neetbox/frontend/src/components/logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { CSSProperties } from "react";
import styles from "./logo.module.css";

type LogoProps = {
styles?: CSSProperties;
className?: string;
withGlow?: boolean;
withLink?: boolean;
withTitle?: boolean;
};

export default function Logo(props: LogoProps) {
const { withLink = false, withTitle = false, withGlow = false } = props;
const url = withLink ? "https://neetbox.550w.host" : undefined;
const glowStyleClassName = withGlow ? styles["neet-logo-glow"] : undefined;
const combinedStyles: CSSProperties = {
...{}, // add default style here
...props.styles,
};
const imageComponent = <img className={glowStyleClassName} src="/logo.svg" />;
const titleComponent = withTitle ? <span>NEETBOX</span> : null;
return (
<div className={props.className} style={combinedStyles}>
<a href={url} target="_blank">
{imageComponent}
{titleComponent}
</a>
</div>
);
}
16 changes: 16 additions & 0 deletions neetbox/frontend/src/components/themeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";
import { Button } from "@douyinfe/semi-ui";


export default function SwitchColorMode(): React.JSX.Element {
const switchMode = () => {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.removeAttribute("theme-mode");
} else {
body.setAttribute("theme-mode", "dark");
}
};

return <Button onClick={switchMode}>Switch Mode</Button>;
}
Loading

0 comments on commit 44de0e2

Please sign in to comment.