init a react-app

Signed-off-by: Augustin Husson <augustin.husson@amadeus.com>
This commit is contained in:
Augustin Husson 2023-02-27 12:30:09 +01:00
parent eea788a968
commit c86c6131ae
14 changed files with 14547 additions and 0 deletions

83
ui/react-app/.eslintrc.js Normal file
View File

@ -0,0 +1,83 @@
module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
plugins: ['import'],
env: {
commonjs: true,
es6: true,
jest: true,
node: true,
browser: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: 'detect',
},
},
rules: {
'prettier/prettier': 'error',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/array-type': [
'warn',
{
default: 'array-simple',
},
],
'import/order': 'warn',
// you must disable the base rule as it can report incorrect errors
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
'react/prop-types': 'off',
'react-hooks/exhaustive-deps': 'warn',
// Not necessary in React 17
'react/react-in-jsx-scope': 'off',
'no-restricted-imports': [
'error',
{
patterns: [
{
/**
* This library is gigantic and named imports end up slowing down builds/blowing out bundle sizes,
* so this prevents that style of import.
*/
group: ['mdi-material-ui', '!mdi-material-ui/'],
message: `
Please use the default import from the icon file directly rather than using a named import.
Good:
import IconName from 'mdi-material-ui/IconName';
Bad:
import { IconName } from 'mdi-material-ui';
`,
},
],
},
],
},
ignorePatterns: ['**/dist'],
};

1
ui/react-app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

View File

@ -0,0 +1,2 @@
# Build output
dist/

View File

@ -0,0 +1,5 @@
{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "es5"
}

14090
ui/react-app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

49
ui/react-app/package.json Normal file
View File

@ -0,0 +1,49 @@
{
"name": "@prometheus-io/alertmanager",
"version": "0.0.0",
"private": true,
"scripts": {
"clean": "rimraf dist/",
"start": "webpack serve --config webpack.dev.ts",
"build": "webpack --config webpack.prod.ts",
"lint": "eslint src --ext .ts,.tsx",
"lint:fix": "eslint --fix src --ext .ts,.tsx"
},
"dependencies": {
"@emotion/react": "^11.9.3",
"@emotion/styled": "^11.9.3",
"@mui/material": "^5.10.14",
"mdi-material-ui": "^7.4.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-router-dom": "^6.3.0"
},
"devDependencies": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.30.7",
"@typescript-eslint/parser": "^5.30.7",
"css-loader": "^6.7.1",
"dotenv-defaults": "^5.0.2",
"esbuild-loader": "^2.20.0",
"eslint": "^8.20.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-webpack-plugin": "^3.2.0",
"fork-ts-checker-webpack-plugin": "^7.3.0",
"html-webpack-plugin": "^5.5.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.3.1",
"ts-node": "^10.9.1",
"typescript": "^4.7.4",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.7.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"webpack-merge": "^5.8.0"
}
}

25
ui/react-app/src/App.tsx Normal file
View File

@ -0,0 +1,25 @@
import { Box } from '@mui/material';
import Navbar from './components/navbar';
function App() {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
}}
>
<Navbar />
<Box
sx={{
paddingBottom: (theme) => theme.spacing(1),
flex: 1,
}}
>
</Box>
</Box>
);
}
export default App;

View File

@ -0,0 +1,30 @@
import { AppBar, Box, Button, Toolbar, Typography } from '@mui/material';
import { useNavigate } from 'react-router-dom';
export default function Navbar(): JSX.Element {
const navigate = useNavigate();
return (
<AppBar position='relative'>
<Toolbar>
<Box sx={{ display: 'flex', flexDirection: 'row' }} flexGrow={1}>
<Button
onClick={() => {
navigate('/');
}}
>
<Typography
variant='h1'
sx={(theme) => ({
marginRight: '1rem',
color: theme.palette.common.white,
})}
>
AlertManager
</Typography>
</Button>
</Box>
</Toolbar>
</AppBar>
);
}

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<meta name="description" content="AlertManager"/>
<title>Perses</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

View File

@ -0,0 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
function renderApp(container: Element| null) {
if (container === null) {
return;
}
const root = ReactDOM.createRoot(container);
root.render(
<React.StrictMode>
<BrowserRouter>
<App/>
</BrowserRouter>
</React.StrictMode>
);
}
renderApp(document.getElementById('root'));

View File

@ -0,0 +1,31 @@
// Base config for all typescript packages
{
"compilerOptions": {
"target": "ES2018",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"jsx": "react-jsx",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noUncheckedIndexedAccess": true,
"declaration": true,
"declarationMap": true,
"pretty": true,
"sourceMap": true
},
"include": ["src"],
// For builds that use ts-node to compile the configs (e.g. webpack, jest)
"ts-node": {
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"esModuleInterop": true
}
}
}

View File

@ -0,0 +1,89 @@
// Copyright 2023 The Perses Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { Configuration } from 'webpack';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import ESLintWebpackPlugin from 'eslint-webpack-plugin';
export const commonConfig: Configuration = {
entry: path.resolve(__dirname, './src/index.tsx'),
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/',
},
resolve: {
extensions: ['.ts', '.tsx', '.js', 'jsx', '.json'],
},
plugins: [
// Generates HTML index page with bundle injected
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './src/index.html'),
templateParameters: {},
}),
// Does TS type-checking in a separate process
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: path.resolve(__dirname, './tsconfig.json'),
},
}),
new ESLintWebpackPlugin({
threads: true,
files: '../*/src/**/*.{ts,tsx,js,jsx}',
}),
],
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
// Type-checking happens in separate plugin process
transpileOnly: true,
projectReferences: true,
},
},
],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(ttf|eot|woff|woff2)$/,
type: 'asset/resource',
},
{
test: /\.(png|jpg|gif)$/,
type: 'asset',
},
// SVG as React components
{
test: /\.svg$/,
use: [
{
loader: '@svgr/webpack',
options: {
// Generated React components will support a 'title' prop to render
// a <title> inside the <svg>
titleProp: true,
},
},
],
},
],
},
};

View File

@ -0,0 +1,78 @@
// Copyright 2023 The Perses Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import fs from 'fs';
import { Configuration } from 'webpack';
import { Configuration as DevServerConfig, ServerConfiguration } from 'webpack-dev-server';
import { merge } from 'webpack-merge';
import { commonConfig } from './webpack.common';
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('dotenv-defaults').config();
declare module 'webpack' {
interface Configuration {
devServer?: DevServerConfig | undefined;
}
}
// Get dev server HTTP options (note: HTTP2 is not currently supported by webpack since we're on Node 16)
function getServerConfig(): ServerConfiguration | undefined {
// Just use regular HTTP by default
if (process.env.HTTPS !== 'true') {
return undefined;
}
// Support the same HTTPS options as Creact React App if HTTPS is set
if (process.env.SSL_KEY_FILE === undefined || process.env.SSL_CRT_FILE === undefined) {
// Use the default self-signed cert
return { type: 'https' };
}
// Use a custom cert
return {
type: 'https',
options: {
key: fs.readFileSync(process.env.SSL_KEY_FILE),
cert: fs.readFileSync(process.env.SSL_CRT_FILE),
},
};
}
// Webpack configuration in dev
const devConfig: Configuration = {
mode: 'development',
devtool: 'cheap-module-source-map',
output: {
pathinfo: true,
},
watchOptions: {
aggregateTimeout: 300,
},
devServer: {
port: parseInt(process.env.PORT ?? '3000'),
open: true,
server: getServerConfig(),
historyApiFallback: true,
allowedHosts: 'all',
proxy: {
'/api': 'http://localhost:8080',
},
},
cache: true,
};
const merged = merge(commonConfig, devConfig);
export default merged;

View File

@ -0,0 +1,30 @@
// Copyright 2023 The Perses Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Configuration } from 'webpack';
import { merge } from 'webpack-merge';
import { ESBuildMinifyPlugin } from 'esbuild-loader';
import { commonConfig } from './webpack.common';
const prodConfig: Configuration = {
mode: 'production',
bail: true,
devtool: 'source-map',
optimization: {
// TODO: Could this also be replaced with swc minifier?
minimizer: [new ESBuildMinifyPlugin({ target: 'es2018' })],
},
};
const merged = merge(commonConfig, prodConfig);
export default merged;