feat(web-client): implement a basic web client
This commit is contained in:
parent
991b82d750
commit
8b8f84ede1
13 changed files with 12506 additions and 9 deletions
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
24
web-client/.gitignore
vendored
Normal file
24
web-client/.gitignore
vendored
Normal file
|
|
@ -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?
|
||||
17
web-client/README.md
Normal file
17
web-client/README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# csv-to-json web client
|
||||
|
||||
A simple Preact SPA designed to showcase the csv-to-json server in action.
|
||||
|
||||
## Usage
|
||||
|
||||
Have a recent version of node (>=v14) and npm installed, and install any dependencies needed:
|
||||
|
||||
```sh
|
||||
$> npm i
|
||||
```
|
||||
|
||||
Ensure that you have `csv-to-json` running and listening on port 8000, then fire up the dev server and visit the url indicated in your browser (defaults to http://localhost:3000):
|
||||
|
||||
```sh
|
||||
$> npm run dev
|
||||
```
|
||||
12
web-client/index.html
Normal file
12
web-client/index.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>CSV to JSON</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
12205
web-client/package-lock.json
generated
Normal file
12205
web-client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,25 @@
|
|||
{
|
||||
"name": "web-client",
|
||||
"version": "0.1.0",
|
||||
"description": "A web client for csv-to-json.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"author": "M. George Hansen",
|
||||
"license": "UNLICENSED"
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"dev": "vite"
|
||||
},
|
||||
"dependencies": {
|
||||
"preact": "^10.7.2",
|
||||
"react-query": "^3.39.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preact/preset-vite": "^2.1.7",
|
||||
"@testing-library/preact": "^2.0.1",
|
||||
"@types/jest": "^27.4.1",
|
||||
"jest": "^27.5.1",
|
||||
"ts-node": "^10.6.0",
|
||||
"typescript": "^4.5.5",
|
||||
"vite": "^2.8.4",
|
||||
"vite-tsconfig-paths": "^3.4.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
94
web-client/src/app.tsx
Normal file
94
web-client/src/app.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
export { App };
|
||||
|
||||
import { QueryClient, QueryClientProvider, useMutation } from "react-query";
|
||||
import { FunctionComponent } from "preact";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const App: FunctionComponent = () => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<FileLoader />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
const FileLoader: FunctionComponent = () => (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<form
|
||||
action="http://localhost:8000"
|
||||
method="POST"
|
||||
encType="multipart/form-data"
|
||||
>
|
||||
<h1>Convert Your CSV to JSON:</h1>
|
||||
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid #fff",
|
||||
borderRadius: 4,
|
||||
padding: "20px 10px",
|
||||
}}
|
||||
>
|
||||
<label>
|
||||
Upload CSV{" "}
|
||||
<input
|
||||
style={{ cursor: "pointer" }}
|
||||
type="file"
|
||||
name="file"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<button
|
||||
style={{
|
||||
border: "1px solid #fff",
|
||||
background: "none",
|
||||
color: "inherit",
|
||||
padding: "5px 10px",
|
||||
borderRadius: 4,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Convert
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Motto />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
|
||||
const MOTTOS = [
|
||||
"It's fun AND educational!",
|
||||
"Everyone's doing it, don't get left behind!",
|
||||
"Do iiittt!",
|
||||
];
|
||||
|
||||
const UPDATE_INTERVAL = 5_000;
|
||||
|
||||
const Motto: FunctionComponent = () => {
|
||||
const [motto, setMotto] = useState(0);
|
||||
useEffect(() => {
|
||||
const handle = setInterval(
|
||||
() =>
|
||||
setMotto((i) => {
|
||||
const newIndex = i + 1;
|
||||
if (newIndex >= MOTTOS.length) {
|
||||
return 0;
|
||||
} else {
|
||||
return newIndex;
|
||||
}
|
||||
}),
|
||||
UPDATE_INTERVAL
|
||||
);
|
||||
return () => {
|
||||
clearInterval(handle);
|
||||
};
|
||||
}, []);
|
||||
return <div>{MOTTOS[motto]}</div>;
|
||||
};
|
||||
5
web-client/src/index.tsx
Normal file
5
web-client/src/index.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { render } from "preact";
|
||||
import { App } from "./app";
|
||||
import "./reset.css";
|
||||
|
||||
render(<App />, document.body);
|
||||
58
web-client/src/reset.css
Normal file
58
web-client/src/reset.css
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* Based on https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
img,
|
||||
picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
text-decoration: unset;
|
||||
color: unset;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
p,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
25
web-client/src/tsconfig.json
Normal file
25
web-client/src/tsconfig.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"useDefineForClassFields": true,
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact",
|
||||
"rootDir": "./",
|
||||
"types": ["vite/client"],
|
||||
"typeRoots": ["./types"],
|
||||
"paths": {
|
||||
"#/*": ["./*"],
|
||||
"react": ["../node_modules/preact/compat"],
|
||||
"react-dom": ["../node_modules/preact/compat"]
|
||||
}
|
||||
}
|
||||
}
|
||||
9
web-client/tsconfig.base.json
Normal file
9
web-client/tsconfig.base.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}
|
||||
9
web-client/tsconfig.json
Normal file
9
web-client/tsconfig.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
16
web-client/vite.config.ts
Normal file
16
web-client/vite.config.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { defineConfig } from "vite";
|
||||
import preact from "@preact/preset-vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [preact(), tsconfigPaths({ projects: ["./src/tsconfig.json"] })],
|
||||
resolve: {
|
||||
alias: {
|
||||
react: "preact/compat",
|
||||
"react-dom": "preact/compat",
|
||||
"react-dom/test-utils": "preact/test-utils",
|
||||
"react/jsx-runtime": "preact/jsx-runtime",
|
||||
},
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue