add website
This commit is contained in:
parent
1bf3728022
commit
171c22c338
50
prefs_page/README.md
Normal file
50
prefs_page/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# 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 tseslint.config({
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
||||
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import react from 'eslint-plugin-react'
|
||||
|
||||
export default tseslint.config({
|
||||
// Set the react version
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
// Add the react plugin
|
||||
react,
|
||||
},
|
||||
rules: {
|
||||
// other rules...
|
||||
// Enable its recommended rules
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
},
|
||||
})
|
||||
```
|
13
prefs_page/index.html
Normal file
13
prefs_page/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Team preference</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
33
prefs_page/package.json
Normal file
33
prefs_page/package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "prefs",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@mui/icons-material": "^6.1.5",
|
||||
"@mui/material": "^6.1.5",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.13.0",
|
||||
"@types/react": "^18.3.11",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vitejs/plugin-react": "^4.3.3",
|
||||
"eslint": "^9.13.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.13",
|
||||
"globals": "^15.11.0",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.10.0",
|
||||
"vite": "^5.4.9"
|
||||
}
|
||||
}
|
55
prefs_page/public/logo.svg
Normal file
55
prefs_page/public/logo.svg
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 12.7 12.7"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="logo.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="10.575773"
|
||||
inkscape:cx="49.499926"
|
||||
inkscape:cy="20.896817"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1800"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
style="fill:#f4d7d7;stroke:#ff0000;stroke-width:1.94357"
|
||||
id="path2"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="6.3500028"
|
||||
sodipodi:cy="6.3500109"
|
||||
sodipodi:rx="4.5089426"
|
||||
sodipodi:ry="4.5918198"
|
||||
sodipodi:start="2.4256468"
|
||||
sodipodi:end="2.4204584"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="M 2.9481283,9.363766 A 4.5089426,4.5918198 0 0 1 3.3862324,2.8895199 4.5089426,4.5918198 0 0 1 9.7441886,3.3272785 4.5089426,4.5918198 0 0 1 9.3225783,9.8026603 4.5089426,4.5918198 0 0 1 2.9635283,9.3817001" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
14
prefs_page/src/App.css
Normal file
14
prefs_page/src/App.css
Normal file
@ -0,0 +1,14 @@
|
||||
#root {
|
||||
max-width: 60vw;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
204
prefs_page/src/App.tsx
Normal file
204
prefs_page/src/App.tsx
Normal file
@ -0,0 +1,204 @@
|
||||
import "./App.css";
|
||||
import names from "./players.json";
|
||||
import * as React from "react";
|
||||
import { Theme, useTheme } from "@mui/material/styles";
|
||||
import Box from "@mui/material/Box";
|
||||
import OutlinedInput from "@mui/material/OutlinedInput";
|
||||
import InputLabel from "@mui/material/InputLabel";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import FormControl from "@mui/material/FormControl";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import Chip from "@mui/material/Chip";
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
FormHelperText,
|
||||
Stack,
|
||||
} from "@mui/material";
|
||||
|
||||
function getStyles(name: string, players: readonly string[], theme: Theme) {
|
||||
return {
|
||||
fontWeight: players.includes(name)
|
||||
? theme.typography.fontWeightMedium
|
||||
: theme.typography.fontWeightRegular,
|
||||
};
|
||||
}
|
||||
|
||||
async function submit(
|
||||
person: string,
|
||||
players: string[],
|
||||
setResponseStatus: (value: number) => void
|
||||
) {
|
||||
// console.log(JSON.stringify({ person: person, players: players }));
|
||||
const response = await fetch("https://0124816.xyz/team/submit/", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ person: person, players: players }),
|
||||
});
|
||||
setResponseStatus(response.status);
|
||||
}
|
||||
|
||||
type SubmitButtonProps = {
|
||||
person: string;
|
||||
players: string[];
|
||||
};
|
||||
|
||||
function SubmitButton(props: SubmitButtonProps) {
|
||||
const [responseStatus, setResponseStatus] = React.useState(0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color={responseStatus === 200 ? "success" : "primary"}
|
||||
onClick={() => submit(props.person, props.players, setResponseStatus)}
|
||||
>
|
||||
submit
|
||||
</Button>
|
||||
<Dialog open={responseStatus === 200}>
|
||||
<DialogTitle>thank you. please leave now.</DialogTitle>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type MultiSelectProps = {
|
||||
person: string;
|
||||
setPerson: (value: string) => void;
|
||||
players: string[];
|
||||
setPlayers: (value: string[]) => void;
|
||||
};
|
||||
|
||||
function MultipleSelectChip(props: MultiSelectProps) {
|
||||
const ITEM_HEIGHT = 48;
|
||||
const ITEM_PADDING_TOP = 8;
|
||||
const MenuProps = {
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: ITEM_HEIGHT * 6.5 + ITEM_PADDING_TOP,
|
||||
width: 250,
|
||||
},
|
||||
},
|
||||
};
|
||||
const [alert, setAlert] = React.useState<string>("none");
|
||||
const maxLimit = 9;
|
||||
const theme = useTheme();
|
||||
|
||||
const handlePersonChange = (
|
||||
event: SelectChangeEvent<typeof props.person>
|
||||
) => {
|
||||
const {
|
||||
target: { value },
|
||||
} = event;
|
||||
props.setPerson(value);
|
||||
const index = props.players.indexOf(value);
|
||||
if (index > -1) {
|
||||
props.players.splice(index, 1);
|
||||
}
|
||||
props.setPlayers(props.players);
|
||||
};
|
||||
|
||||
const handleChange = (event: SelectChangeEvent<typeof props.players>) => {
|
||||
const {
|
||||
target: { value },
|
||||
} = event;
|
||||
if (value.length <= maxLimit) {
|
||||
setAlert("none");
|
||||
props.setPlayers(
|
||||
// On autofill we get a stringified value.
|
||||
typeof value === "string" ? value.split(",") : value
|
||||
);
|
||||
} else {
|
||||
setAlert("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ maxWidth: "60vw" }}>
|
||||
<Stack direction="column">
|
||||
<FormControl sx={{ m: "8px auto", width: 200 }}>
|
||||
<InputLabel id="demo-multiple-chip-label">who are you?</InputLabel>
|
||||
<Select
|
||||
labelId="demo-chip-label"
|
||||
id="demo-multiple-chip"
|
||||
value={props.person}
|
||||
onChange={handlePersonChange}
|
||||
input={<OutlinedInput id="select-chip" label="who are you?" />}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{names.map((name) => (
|
||||
<MenuItem key={name} value={name}>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Alert
|
||||
severity="warning"
|
||||
sx={{ display: alert, width: 200, m: "8px auto" }}
|
||||
>
|
||||
do not select more than {maxLimit} players
|
||||
</Alert>
|
||||
|
||||
<FormControl sx={{ m: "8px auto", width: 200 }}>
|
||||
<InputLabel id="demo-multiple-chip-label">players</InputLabel>
|
||||
<Select
|
||||
labelId="demo-multiple-chip-label"
|
||||
id="demo-multiple-chip"
|
||||
multiple
|
||||
value={props.players}
|
||||
onChange={handleChange}
|
||||
input={<OutlinedInput id="select-multiple-chip" label="players" />}
|
||||
renderValue={(selected) => (
|
||||
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
|
||||
{selected.map((value) => (
|
||||
<Chip key={value} label={value} />
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{names.map((name) =>
|
||||
name !== props.person ? (
|
||||
<MenuItem
|
||||
key={name}
|
||||
value={name}
|
||||
style={getStyles(name, props.players, theme)}
|
||||
>
|
||||
{name}
|
||||
</MenuItem>
|
||||
) : null
|
||||
)}
|
||||
</Select>
|
||||
<FormHelperText>max. {maxLimit}</FormHelperText>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [person, setPerson] = React.useState<string>("");
|
||||
const [players, setPlayers] = React.useState<string[]>([]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>choose your best buddies for the Igloo</h1>
|
||||
<div className="card">
|
||||
{MultipleSelectChip({ person, setPerson, players, setPlayers })}
|
||||
{SubmitButton({ person, players })}
|
||||
<p>now: click submit.</p>
|
||||
</div>
|
||||
<p className="read-the-docs">
|
||||
something not working? message <a href="https://t.me/x0124816">me</a>.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
68
prefs_page/src/index.css
Normal file
68
prefs_page/src/index.css
Normal file
@ -0,0 +1,68 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
10
prefs_page/src/main.tsx
Normal file
10
prefs_page/src/main.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
1
prefs_page/src/vite-env.d.ts
vendored
Normal file
1
prefs_page/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
25
prefs_page/tsconfig.app.json
Normal file
25
prefs_page/tsconfig.app.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "Bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
1
prefs_page/tsconfig.app.tsbuildinfo
Normal file
1
prefs_page/tsconfig.app.tsbuildinfo
Normal file
@ -0,0 +1 @@
|
||||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts"],"version":"5.6.3"}
|
7
prefs_page/tsconfig.json
Normal file
7
prefs_page/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
23
prefs_page/tsconfig.node.json
Normal file
23
prefs_page/tsconfig.node.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "Bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
1
prefs_page/tsconfig.node.tsbuildinfo
Normal file
1
prefs_page/tsconfig.node.tsbuildinfo
Normal file
@ -0,0 +1 @@
|
||||
{"root":["./vite.config.ts"],"version":"5.6.3"}
|
Loading…
Reference in New Issue
Block a user