Compare commits
	
		
			13 Commits 
		
	
	
		
			submitter-
			...
			master
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						3a785c212a | 5 years ago | 
| 
							
							
								
								 | 
						4ea0a209f5 | 5 years ago | 
| 
							
							
								
								 | 
						5110955b22 | 5 years ago | 
| 
							
							
								
								 | 
						525660221b | 5 years ago | 
| 
							
							
								
								 | 
						2082dc1866 | 5 years ago | 
| 
							
							
								
								 | 
						724b047eaa | 5 years ago | 
| 
							
							
								
								 | 
						df52776c28 | 5 years ago | 
| 
							
							
								
								 | 
						1cdf4a2d83 | 5 years ago | 
| 
							
							
								
								 | 
						3192dbb4f4 | 5 years ago | 
| 
							
							
								
								 | 
						bde299396d | 5 years ago | 
| 
							
							
								
								 | 
						5eff436c79 | 5 years ago | 
| 
							
							
								
									
								
								 | 
						b9ea4a9474 | 5 years ago | 
| 
							
							
								
									
								
								 | 
						de4184a2d7 | 5 years ago | 
@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
						env: { node: true },
 | 
				
			||||||
 | 
						parser: "@typescript-eslint/parser",
 | 
				
			||||||
 | 
						plugins: ["@typescript-eslint", "prettier", "jsdoc"],
 | 
				
			||||||
 | 
						extends: [
 | 
				
			||||||
 | 
							"eslint:recommended",
 | 
				
			||||||
 | 
							"plugin:@typescript-eslint/recommended",
 | 
				
			||||||
 | 
							"plugin:@typescript-eslint/recommended-requiring-type-checking",
 | 
				
			||||||
 | 
							"plugin:prettier/recommended",
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						parserOptions: {
 | 
				
			||||||
 | 
							sourceType: "module",
 | 
				
			||||||
 | 
							ecmaFeatures: {
 | 
				
			||||||
 | 
								modules: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							project: "./tsconfig.json",
 | 
				
			||||||
 | 
							tsconfigRootDir: __dirname,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						rules: {
 | 
				
			||||||
 | 
							"@typescript-eslint/require-await": 0,
 | 
				
			||||||
 | 
							"jsdoc/require-description": 2,
 | 
				
			||||||
 | 
							"no-await-in-loop": 2,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						settings: { jsdoc: { mode: "typescript" } },
 | 
				
			||||||
 | 
						overrides: [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								files: ["*.subtest.ts", "*.test.ts"],
 | 
				
			||||||
 | 
								rules: {
 | 
				
			||||||
 | 
									"@typescript-eslint/no-unsafe-member-access": 0,
 | 
				
			||||||
 | 
									"prefer-const": 0,
 | 
				
			||||||
 | 
									"@typescript-eslint/no-unsafe-call": 0,
 | 
				
			||||||
 | 
									"@typescript-eslint/no-unsafe-return": 0,
 | 
				
			||||||
 | 
									"@typescript-eslint/no-unsafe-assignment": 0,
 | 
				
			||||||
 | 
									"no-await-in-loop": 1, // sometimes it's easier to debug when requests run sequentially
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,3 +1,11 @@
 | 
				
			|||||||
/.cache/
 | 
					node_modules
 | 
				
			||||||
/dist/
 | 
					.node_repl_history
 | 
				
			||||||
/node_modules/
 | 
					.config
 | 
				
			||||||
 | 
					.npm
 | 
				
			||||||
 | 
					.idea
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					lib
 | 
				
			||||||
 | 
					dist
 | 
				
			||||||
 | 
					@types
 | 
				
			||||||
 | 
					.cache
 | 
				
			||||||
 | 
					public
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"useTabs": true,
 | 
				
			||||||
 | 
						"tabWidth": 4,
 | 
				
			||||||
 | 
						"trailingComma": "es5",
 | 
				
			||||||
 | 
						"overrides": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"files": "*.yml",
 | 
				
			||||||
 | 
								"options": {
 | 
				
			||||||
 | 
									"tabWidth": 2,
 | 
				
			||||||
 | 
									"useTabs": false
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					version: "3.7"
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  db:
 | 
				
			||||||
 | 
					    image: mongo:4.4-bionic
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - "127.0.0.1:20724:27017"
 | 
				
			||||||
 | 
					  mailcatcher:
 | 
				
			||||||
 | 
					    image: schickling/mailcatcher:latest
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - "127.0.0.1:1080:1080"
 | 
				
			||||||
 | 
					      - "127.0.0.1:1025:1025"
 | 
				
			||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								
											
												Binary file not shown.
											
										
									
								| 
		 After Width: | Height: | Size: 328 B  | 
@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					import Koa from "koa";
 | 
				
			||||||
 | 
					import _locreq from "locreq";
 | 
				
			||||||
 | 
					import { resolve } from "path";
 | 
				
			||||||
 | 
					import Static from "koa-static";
 | 
				
			||||||
 | 
					import Router from "@koa/router";
 | 
				
			||||||
 | 
					import mount from "koa-mount";
 | 
				
			||||||
 | 
					const locreq = _locreq(__dirname);
 | 
				
			||||||
 | 
					import Sealious, { App } from "sealious";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare module "koa" {
 | 
				
			||||||
 | 
						interface BaseContext {
 | 
				
			||||||
 | 
							$context: Sealious.Context;
 | 
				
			||||||
 | 
							$app: Sealious.App;
 | 
				
			||||||
 | 
							$body: Record<string, unknown>;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const app = new (class extends App {
 | 
				
			||||||
 | 
						config = {
 | 
				
			||||||
 | 
							upload_path: locreq.resolve("uploaded_files"),
 | 
				
			||||||
 | 
							datastore_mongo: {
 | 
				
			||||||
 | 
								host: "localhost",
 | 
				
			||||||
 | 
								port: 20724,
 | 
				
			||||||
 | 
								db_name: "zglaszansko-web",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							email: {
 | 
				
			||||||
 | 
								from_address: "sealious-playground@example.com",
 | 
				
			||||||
 | 
								from_name: "Sealious playground app",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						manifest = {
 | 
				
			||||||
 | 
							name: "Sealious Playground",
 | 
				
			||||||
 | 
							logo: resolve(__dirname, "../assets/logo.png"),
 | 
				
			||||||
 | 
							version: "0.0.1",
 | 
				
			||||||
 | 
							default_language: "en",
 | 
				
			||||||
 | 
							base_url: "http://localhost:8080",
 | 
				
			||||||
 | 
							admin_email: "admin@example.com",
 | 
				
			||||||
 | 
							colors: {
 | 
				
			||||||
 | 
								primary: "#5294a1",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						collections = {
 | 
				
			||||||
 | 
							...App.BaseCollections,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const port = 8080;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log(`Listening on 127.0.0.1:${port}`);
 | 
				
			||||||
 | 
					app.start();
 | 
				
			||||||
 | 
					app.HTTPServer.addStaticRoute("/", locreq.resolve("public"));
 | 
				
			||||||
@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					import React, { ReactElement } from "react";
 | 
				
			||||||
 | 
					import "./styles/nav.scss";
 | 
				
			||||||
 | 
					interface Props {
 | 
				
			||||||
 | 
						isLogged: boolean;
 | 
				
			||||||
 | 
						logoutState: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const NOT_LOGGED = [
 | 
				
			||||||
 | 
						{ name: "aplikacja", href: "/" },
 | 
				
			||||||
 | 
						{ name: "logowanie", href: "login.html" },
 | 
				
			||||||
 | 
						{ name: "rejestracja", href: "register.html" },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const LOGGED = [{ name: "aplikacja", href: "/" }];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Nav({ isLogged, logoutState }: Props): ReactElement {
 | 
				
			||||||
 | 
						const logout = async () => {
 | 
				
			||||||
 | 
							await axios.delete(
 | 
				
			||||||
 | 
								"http://localhost:8080/api/v1/collections/sessions/current"
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							logoutState();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<nav className="navbar">
 | 
				
			||||||
 | 
								<h2 className="navbar__title">Zgłaszańsko web</h2>
 | 
				
			||||||
 | 
								<div className="navbar__links">
 | 
				
			||||||
 | 
									{isLogged
 | 
				
			||||||
 | 
										? LOGGED.map((ele) => (
 | 
				
			||||||
 | 
												<>
 | 
				
			||||||
 | 
													<a href={ele.href} className="button-link">
 | 
				
			||||||
 | 
														{ele.name}
 | 
				
			||||||
 | 
													</a>
 | 
				
			||||||
 | 
													<button
 | 
				
			||||||
 | 
														onClick={logout}
 | 
				
			||||||
 | 
														className="button-link"
 | 
				
			||||||
 | 
													>
 | 
				
			||||||
 | 
														Wyloguj
 | 
				
			||||||
 | 
													</button>
 | 
				
			||||||
 | 
												</>
 | 
				
			||||||
 | 
										  ))
 | 
				
			||||||
 | 
										: NOT_LOGGED.map((ele) => (
 | 
				
			||||||
 | 
												<a href={ele.href} className="button-link">
 | 
				
			||||||
 | 
													{ele.name}
 | 
				
			||||||
 | 
												</a>
 | 
				
			||||||
 | 
										  ))}
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</nav>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Nav;
 | 
				
			||||||
@ -0,0 +1,347 @@
 | 
				
			|||||||
 | 
					/* eslint-disable @typescript-eslint/no-unused-vars */
 | 
				
			||||||
 | 
					import React, {
 | 
				
			||||||
 | 
						useState,
 | 
				
			||||||
 | 
						useReducer,
 | 
				
			||||||
 | 
						MouseEvent,
 | 
				
			||||||
 | 
						FC,
 | 
				
			||||||
 | 
						ChangeEvent,
 | 
				
			||||||
 | 
						FormEvent,
 | 
				
			||||||
 | 
						useEffect,
 | 
				
			||||||
 | 
					} from "react";
 | 
				
			||||||
 | 
					import { render } from "react-dom";
 | 
				
			||||||
 | 
					import { offenses } from "./offenses";
 | 
				
			||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					import Nav from "./Nav";
 | 
				
			||||||
 | 
					//Importing fileReducer to use in useReducer hook, and File interface to use in looping over array of files
 | 
				
			||||||
 | 
					import { fileReducer, filesInitialState, File } from "./fileReducer";
 | 
				
			||||||
 | 
					import { formReducer, fromInitialState } from "./formReducer";
 | 
				
			||||||
 | 
					import "regenerator-runtime/runtime";
 | 
				
			||||||
 | 
					import { MapContainer, TileLayer, Marker, useMapEvents } from "react-leaflet";
 | 
				
			||||||
 | 
					import "./styles/reset.css";
 | 
				
			||||||
 | 
					import "./styles/app.scss";
 | 
				
			||||||
 | 
					import { getAddress } from "./location";
 | 
				
			||||||
 | 
					import { FileType } from "./file";
 | 
				
			||||||
 | 
					const App: FC = () => {
 | 
				
			||||||
 | 
						const [isLogged, setIsLogged] = useState(false);
 | 
				
			||||||
 | 
						const [fileState, fileDispatch] = useReducer(
 | 
				
			||||||
 | 
							fileReducer,
 | 
				
			||||||
 | 
							filesInitialState
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						const [formState, formDispatch] = useReducer(formReducer, fromInitialState);
 | 
				
			||||||
 | 
						const [mapPin, setMapPin] = useState({ lat: 0, lon: 0 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const checkUser = async (): Promise<void> => {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								await axios.get(
 | 
				
			||||||
 | 
									"http://localhost:8080/api/v1/collections/users/me"
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								setIsLogged(true);
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								setIsLogged(false);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						useEffect(() => {
 | 
				
			||||||
 | 
							void checkUser();
 | 
				
			||||||
 | 
						}, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						useEffect(() => {
 | 
				
			||||||
 | 
							void getAddress(mapPin).then((blob) =>
 | 
				
			||||||
 | 
								formDispatch({
 | 
				
			||||||
 | 
									type: "CHANGE_FIELD",
 | 
				
			||||||
 | 
									payload: {
 | 
				
			||||||
 | 
										field: "address",
 | 
				
			||||||
 | 
										value: blob,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}, [mapPin]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleSubmit = async (
 | 
				
			||||||
 | 
							event: FormEvent<HTMLFormElement> | MouseEvent<HTMLButtonElement>
 | 
				
			||||||
 | 
						): Promise<void> => {
 | 
				
			||||||
 | 
							event.preventDefault();
 | 
				
			||||||
 | 
							console.log(fileState, formState);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleUpload = (e: ChangeEvent<HTMLInputElement>) => {
 | 
				
			||||||
 | 
							const files = e.target.files;
 | 
				
			||||||
 | 
							const readAndPreview = (file: FileType) => {
 | 
				
			||||||
 | 
								console.log(file);
 | 
				
			||||||
 | 
								// Make sure `file.name` matches our extensions criteria
 | 
				
			||||||
 | 
								// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
 | 
				
			||||||
 | 
								if (/\.(jpe?g|png|svg)$/i.test(file.name)) {
 | 
				
			||||||
 | 
									const reader = new FileReader();
 | 
				
			||||||
 | 
									reader.addEventListener(
 | 
				
			||||||
 | 
										"load",
 | 
				
			||||||
 | 
										function () {
 | 
				
			||||||
 | 
											fileDispatch({
 | 
				
			||||||
 | 
												type: "ADD_FILE",
 | 
				
			||||||
 | 
												payload: {
 | 
				
			||||||
 | 
													img: this.result,
 | 
				
			||||||
 | 
													id: Date.now().toString(),
 | 
				
			||||||
 | 
													selected: false,
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											});
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										false
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									reader.readAsDataURL((file as unknown) as Blob);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							if (files) {
 | 
				
			||||||
 | 
								[].forEach.call(files, readAndPreview);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const MapComponent = () => {
 | 
				
			||||||
 | 
							useMapEvents({
 | 
				
			||||||
 | 
								click: async (e) => {
 | 
				
			||||||
 | 
									setMapPin({ lat: e.latlng.lat, lon: e.latlng.lng });
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const formMessage = `Rejestracja: ${formState.plate}\nMiejsce: ${
 | 
				
			||||||
 | 
							formState.address.road === undefined ? "" : formState.address.road
 | 
				
			||||||
 | 
						} ${
 | 
				
			||||||
 | 
							formState.address.house_number === undefined
 | 
				
			||||||
 | 
								? ""
 | 
				
			||||||
 | 
								: formState.address.house_number
 | 
				
			||||||
 | 
						}\nData: ${new Date().toLocaleString()}\nMoje dane: ${
 | 
				
			||||||
 | 
							formState.name + ","
 | 
				
			||||||
 | 
						} ${formState.email}\n${
 | 
				
			||||||
 | 
							formState.my_address
 | 
				
			||||||
 | 
						}\nPowód: ${formState.offenses.join(", \n")}\nKomentarz: ${
 | 
				
			||||||
 | 
							formState.comment
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					  `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<>
 | 
				
			||||||
 | 
								<Nav isLogged={isLogged} logoutState={() => setIsLogged(false)} />
 | 
				
			||||||
 | 
								<div className="container">
 | 
				
			||||||
 | 
									<section className="container__section">
 | 
				
			||||||
 | 
										<h2 className="container__section__title">Zdjęcia</h2>
 | 
				
			||||||
 | 
										<div className="container__section__photos">
 | 
				
			||||||
 | 
											{fileState.files.map((file: File, index: number) => (
 | 
				
			||||||
 | 
												<div
 | 
				
			||||||
 | 
													key={index}
 | 
				
			||||||
 | 
													onClick={(e: MouseEvent) =>
 | 
				
			||||||
 | 
														fileDispatch({
 | 
				
			||||||
 | 
															type: "SELECT_FILE",
 | 
				
			||||||
 | 
															payload: {
 | 
				
			||||||
 | 
																id: (e.target as Element).id,
 | 
				
			||||||
 | 
															},
 | 
				
			||||||
 | 
														})
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
													className="section__photos__item"
 | 
				
			||||||
 | 
												>
 | 
				
			||||||
 | 
													<img
 | 
				
			||||||
 | 
														src={file.img as string}
 | 
				
			||||||
 | 
														id={file.id}
 | 
				
			||||||
 | 
														className={
 | 
				
			||||||
 | 
															file.selected ? "img--active" : "img"
 | 
				
			||||||
 | 
														}
 | 
				
			||||||
 | 
													/>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
											))}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<input
 | 
				
			||||||
 | 
											type="file"
 | 
				
			||||||
 | 
											name="image-upload"
 | 
				
			||||||
 | 
											id="input"
 | 
				
			||||||
 | 
											accept="image/*"
 | 
				
			||||||
 | 
											className="input"
 | 
				
			||||||
 | 
											multiple
 | 
				
			||||||
 | 
											onChange={(e) => handleUpload(e)}
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									</section>
 | 
				
			||||||
 | 
									<section className="container__section">
 | 
				
			||||||
 | 
										<h2 className="container__section__title">Mapa</h2>
 | 
				
			||||||
 | 
										<div>
 | 
				
			||||||
 | 
											<MapContainer
 | 
				
			||||||
 | 
												center={[52.39663, 16.89866]}
 | 
				
			||||||
 | 
												className="container__section__map"
 | 
				
			||||||
 | 
												zoom={16}
 | 
				
			||||||
 | 
												scrollWheelZoom={true}
 | 
				
			||||||
 | 
											>
 | 
				
			||||||
 | 
												<TileLayer
 | 
				
			||||||
 | 
													attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
 | 
				
			||||||
 | 
													url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"
 | 
				
			||||||
 | 
												/>
 | 
				
			||||||
 | 
												{mapPin.lat !== 0 ? (
 | 
				
			||||||
 | 
													<Marker
 | 
				
			||||||
 | 
														position={[mapPin.lat, mapPin.lon]}
 | 
				
			||||||
 | 
													></Marker>
 | 
				
			||||||
 | 
												) : null}
 | 
				
			||||||
 | 
												<MapComponent />
 | 
				
			||||||
 | 
											</MapContainer>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<p>
 | 
				
			||||||
 | 
											Wybrany adres:{" "}
 | 
				
			||||||
 | 
											{formState.address.road === undefined
 | 
				
			||||||
 | 
												? ""
 | 
				
			||||||
 | 
												: formState.address.road}{" "}
 | 
				
			||||||
 | 
											{formState.address.house_number === undefined
 | 
				
			||||||
 | 
												? ""
 | 
				
			||||||
 | 
												: formState.address.house_number}
 | 
				
			||||||
 | 
										</p>
 | 
				
			||||||
 | 
									</section>
 | 
				
			||||||
 | 
									<section className="container__section">
 | 
				
			||||||
 | 
										<h2 className="container__section__title">
 | 
				
			||||||
 | 
											Formularz zgłoszeniowy
 | 
				
			||||||
 | 
										</h2>
 | 
				
			||||||
 | 
										<form
 | 
				
			||||||
 | 
											className="container__section__form"
 | 
				
			||||||
 | 
											onSubmit={(e) => handleSubmit(e)}
 | 
				
			||||||
 | 
										>
 | 
				
			||||||
 | 
											<label htmlFor="name">Twoje imię i nazwisko</label>
 | 
				
			||||||
 | 
											<input
 | 
				
			||||||
 | 
												type="text"
 | 
				
			||||||
 | 
												className="input"
 | 
				
			||||||
 | 
												id="name"
 | 
				
			||||||
 | 
												value={formState.name}
 | 
				
			||||||
 | 
												onChange={(e) =>
 | 
				
			||||||
 | 
													formDispatch({
 | 
				
			||||||
 | 
														type: "CHANGE_FIELD",
 | 
				
			||||||
 | 
														payload: {
 | 
				
			||||||
 | 
															field: e.target.id,
 | 
				
			||||||
 | 
															value: e.target.value,
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													})
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
											<label htmlFor="email">Twój adres email</label>
 | 
				
			||||||
 | 
											<input
 | 
				
			||||||
 | 
												type="text"
 | 
				
			||||||
 | 
												className="input"
 | 
				
			||||||
 | 
												id="email"
 | 
				
			||||||
 | 
												value={formState.email}
 | 
				
			||||||
 | 
												onChange={(e) =>
 | 
				
			||||||
 | 
													formDispatch({
 | 
				
			||||||
 | 
														type: "CHANGE_FIELD",
 | 
				
			||||||
 | 
														payload: {
 | 
				
			||||||
 | 
															field: e.target.id,
 | 
				
			||||||
 | 
															value: e.target.value,
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													})
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
											<label htmlFor="my_address">
 | 
				
			||||||
 | 
												Twój adres zamieszkania
 | 
				
			||||||
 | 
											</label>
 | 
				
			||||||
 | 
											<input
 | 
				
			||||||
 | 
												type="text"
 | 
				
			||||||
 | 
												className="input"
 | 
				
			||||||
 | 
												id="my_address"
 | 
				
			||||||
 | 
												value={formState.my_address}
 | 
				
			||||||
 | 
												onChange={(e) =>
 | 
				
			||||||
 | 
													formDispatch({
 | 
				
			||||||
 | 
														type: "CHANGE_FIELD",
 | 
				
			||||||
 | 
														payload: {
 | 
				
			||||||
 | 
															field: e.target.id,
 | 
				
			||||||
 | 
															value: e.target.value,
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													})
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
											<label htmlFor="plate">
 | 
				
			||||||
 | 
												Numer tablicy rejestracyjnej
 | 
				
			||||||
 | 
											</label>
 | 
				
			||||||
 | 
											<input
 | 
				
			||||||
 | 
												type="text"
 | 
				
			||||||
 | 
												className="input"
 | 
				
			||||||
 | 
												id="plate"
 | 
				
			||||||
 | 
												value={formState.plate}
 | 
				
			||||||
 | 
												onChange={(e) =>
 | 
				
			||||||
 | 
													formDispatch({
 | 
				
			||||||
 | 
														type: "CHANGE_FIELD",
 | 
				
			||||||
 | 
														payload: {
 | 
				
			||||||
 | 
															field: e.target.id,
 | 
				
			||||||
 | 
															value: e.target.value,
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													})
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
											<div className="container__section__form__offenses">
 | 
				
			||||||
 | 
												{offenses.map((offence, index) => (
 | 
				
			||||||
 | 
													<div key={index}>
 | 
				
			||||||
 | 
														<input
 | 
				
			||||||
 | 
															type="checkbox"
 | 
				
			||||||
 | 
															id={offence.id}
 | 
				
			||||||
 | 
															value={offence.car_is}
 | 
				
			||||||
 | 
															name={offence.car_is}
 | 
				
			||||||
 | 
															className="chceckbox"
 | 
				
			||||||
 | 
															onChange={(e) =>
 | 
				
			||||||
 | 
																e.target.checked === true
 | 
				
			||||||
 | 
																	? formDispatch({
 | 
				
			||||||
 | 
																			type: "ADD_OFFENCE",
 | 
				
			||||||
 | 
																			payload: {
 | 
				
			||||||
 | 
																				value:
 | 
				
			||||||
 | 
																					e.target.value,
 | 
				
			||||||
 | 
																				field: "checkbox",
 | 
				
			||||||
 | 
																			},
 | 
				
			||||||
 | 
																	  })
 | 
				
			||||||
 | 
																	: formDispatch({
 | 
				
			||||||
 | 
																			type: "DELETE_OFFENCE",
 | 
				
			||||||
 | 
																			payload: {
 | 
				
			||||||
 | 
																				value:
 | 
				
			||||||
 | 
																					e.target.value,
 | 
				
			||||||
 | 
																				field: "checkbox",
 | 
				
			||||||
 | 
																			},
 | 
				
			||||||
 | 
																	  })
 | 
				
			||||||
 | 
															}
 | 
				
			||||||
 | 
														/>
 | 
				
			||||||
 | 
														<label
 | 
				
			||||||
 | 
															htmlFor={offence.id}
 | 
				
			||||||
 | 
															className="label"
 | 
				
			||||||
 | 
														>
 | 
				
			||||||
 | 
															{offence.name}
 | 
				
			||||||
 | 
														</label>
 | 
				
			||||||
 | 
													</div>
 | 
				
			||||||
 | 
												))}
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<label htmlFor="comment">
 | 
				
			||||||
 | 
												Twój komentarz do zgłoszenia
 | 
				
			||||||
 | 
											</label>
 | 
				
			||||||
 | 
											<textarea
 | 
				
			||||||
 | 
												className="textarea textarea--small"
 | 
				
			||||||
 | 
												id="comment"
 | 
				
			||||||
 | 
												name="comment"
 | 
				
			||||||
 | 
												value={formState.comment}
 | 
				
			||||||
 | 
												onChange={(e) =>
 | 
				
			||||||
 | 
													formDispatch({
 | 
				
			||||||
 | 
														type: "CHANGE_FIELD",
 | 
				
			||||||
 | 
														payload: {
 | 
				
			||||||
 | 
															field: e.target.id,
 | 
				
			||||||
 | 
															value: e.target.value,
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													})
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
											<label htmlFor="message">
 | 
				
			||||||
 | 
												Wiadomość dla straży miejskiej
 | 
				
			||||||
 | 
											</label>
 | 
				
			||||||
 | 
											<textarea
 | 
				
			||||||
 | 
												className="textarea"
 | 
				
			||||||
 | 
												id="message"
 | 
				
			||||||
 | 
												disabled={true}
 | 
				
			||||||
 | 
												value={formMessage}
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<button
 | 
				
			||||||
 | 
												className="button"
 | 
				
			||||||
 | 
												onClick={(e) => handleSubmit(e)}
 | 
				
			||||||
 | 
											>
 | 
				
			||||||
 | 
												Wysłanie zgłoszenia
 | 
				
			||||||
 | 
											</button>
 | 
				
			||||||
 | 
										</form>
 | 
				
			||||||
 | 
									</section>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					render(<App />, document.getElementById("root"));
 | 
				
			||||||
@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					export interface FileType {
 | 
				
			||||||
 | 
						lastModified: number;
 | 
				
			||||||
 | 
						name: string;
 | 
				
			||||||
 | 
						size: number;
 | 
				
			||||||
 | 
						type: string;
 | 
				
			||||||
 | 
						webkitRelativePath: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					export interface File {
 | 
				
			||||||
 | 
						img?: unknown;
 | 
				
			||||||
 | 
						id: string;
 | 
				
			||||||
 | 
						selected?: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					interface FilesState {
 | 
				
			||||||
 | 
						files: Array<File>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					interface FilesAction {
 | 
				
			||||||
 | 
						type: string;
 | 
				
			||||||
 | 
						payload: File;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const filesInitialState = {
 | 
				
			||||||
 | 
						files: [],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export function fileReducer(
 | 
				
			||||||
 | 
						state: FilesState,
 | 
				
			||||||
 | 
						action: FilesAction
 | 
				
			||||||
 | 
					): FilesState {
 | 
				
			||||||
 | 
						switch (action.type) {
 | 
				
			||||||
 | 
							case "ADD_FILE":
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									...state,
 | 
				
			||||||
 | 
									files: [
 | 
				
			||||||
 | 
										...state.files,
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											img: action.payload.img,
 | 
				
			||||||
 | 
											id: action.payload.id,
 | 
				
			||||||
 | 
											selected: action.payload.selected,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									],
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							case "SELECT_FILE":
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									...state,
 | 
				
			||||||
 | 
									files: state.files.map((file: File) =>
 | 
				
			||||||
 | 
										file.id == action.payload.id
 | 
				
			||||||
 | 
											? { ...file, selected: true }
 | 
				
			||||||
 | 
											: { ...file, selected: false }
 | 
				
			||||||
 | 
									),
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							case "CLEAR_FILES":
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									files: [],
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								throw new Error();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					import { Address } from "./location";
 | 
				
			||||||
 | 
					interface FormState {
 | 
				
			||||||
 | 
						name: string;
 | 
				
			||||||
 | 
						email: string;
 | 
				
			||||||
 | 
						plate: string;
 | 
				
			||||||
 | 
						offenses: (Address | void | string)[];
 | 
				
			||||||
 | 
						comment: string;
 | 
				
			||||||
 | 
						my_address: string;
 | 
				
			||||||
 | 
						address: Address;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					interface FormAction {
 | 
				
			||||||
 | 
						type: string;
 | 
				
			||||||
 | 
						payload: {
 | 
				
			||||||
 | 
							field: string;
 | 
				
			||||||
 | 
							value: Address | void | string;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const fromInitialState = {
 | 
				
			||||||
 | 
						name: "",
 | 
				
			||||||
 | 
						email: "",
 | 
				
			||||||
 | 
						plate: "",
 | 
				
			||||||
 | 
						my_address: "",
 | 
				
			||||||
 | 
						offenses: [],
 | 
				
			||||||
 | 
						comment: "",
 | 
				
			||||||
 | 
						address: {
 | 
				
			||||||
 | 
							house_number: "",
 | 
				
			||||||
 | 
							road: "",
 | 
				
			||||||
 | 
							suburb: "",
 | 
				
			||||||
 | 
							neighbourhood: "",
 | 
				
			||||||
 | 
							hamlet: "",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export function formReducer(state: FormState, action: FormAction): FormState {
 | 
				
			||||||
 | 
						switch (action.type) {
 | 
				
			||||||
 | 
							case "CHANGE_FIELD":
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									...state,
 | 
				
			||||||
 | 
									//action.payload.field is equal to name of the imput, example: e.target.name = plate
 | 
				
			||||||
 | 
									[action.payload.field]: action.payload.value,
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							case "ADD_OFFENCE":
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									...state,
 | 
				
			||||||
 | 
									offenses: [...state.offenses, action.payload.value],
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							case "DELETE_OFFENCE":
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									...state,
 | 
				
			||||||
 | 
									offenses: state.offenses.filter(
 | 
				
			||||||
 | 
										(offence: string) => offence !== action.payload.value
 | 
				
			||||||
 | 
									),
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							case "CHANGE_ADDRESS":
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									...state,
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								throw new Error();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					  <head>
 | 
				
			||||||
 | 
					    <link
 | 
				
			||||||
 | 
					      href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css"
 | 
				
			||||||
 | 
					      rel="stylesheet"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <title>Zgłaszańsko</title>
 | 
				
			||||||
 | 
					  </head>
 | 
				
			||||||
 | 
					  <body>
 | 
				
			||||||
 | 
					    <div id="root"></div>
 | 
				
			||||||
 | 
					    <script src="./app.tsx"></script>
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					export type Location = {
 | 
				
			||||||
 | 
						lat: number;
 | 
				
			||||||
 | 
						lon: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Address = {
 | 
				
			||||||
 | 
						house_number?: string;
 | 
				
			||||||
 | 
						road?: string;
 | 
				
			||||||
 | 
						suburb?: string;
 | 
				
			||||||
 | 
						neighbourhood?: string;
 | 
				
			||||||
 | 
						hamlet?: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getAddress(location: Location): Promise<Address | void> {
 | 
				
			||||||
 | 
						const nominatim_url = `https://nominatim.openstreetmap.org/reverse?lat=${location.lat}&lon=${location.lon}&format=jsonv2`;
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const data = await axios.get(nominatim_url);
 | 
				
			||||||
 | 
							const nominatim_dat = data.data as { address: Address };
 | 
				
			||||||
 | 
							return nominatim_dat.address;
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.log(123);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
						<head>
 | 
				
			||||||
 | 
							<link
 | 
				
			||||||
 | 
								href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css"
 | 
				
			||||||
 | 
								rel="stylesheet"
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
							<title>Zgłaszańsko</title>
 | 
				
			||||||
 | 
						</head>
 | 
				
			||||||
 | 
						<body>
 | 
				
			||||||
 | 
							<div id="root"></div>
 | 
				
			||||||
 | 
							<script src="./login.tsx"></script>
 | 
				
			||||||
 | 
						</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					/* eslint-disable @typescript-eslint/no-unused-vars */
 | 
				
			||||||
 | 
					import React, { ReactElement, useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import ReactDOM from "react-dom";
 | 
				
			||||||
 | 
					import Nav from "./Nav";
 | 
				
			||||||
 | 
					import "./styles/reset.css";
 | 
				
			||||||
 | 
					import "./styles/app.scss";
 | 
				
			||||||
 | 
					import "regenerator-runtime/runtime";
 | 
				
			||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Login(): ReactElement {
 | 
				
			||||||
 | 
						const [isLogged, setIsLogged] = useState(false);
 | 
				
			||||||
 | 
						const [error, setError] = useState(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const [loginState, setLoginState] = useState({
 | 
				
			||||||
 | 
							username: "",
 | 
				
			||||||
 | 
							password: "",
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						const checkUser = async (): Promise<void> => {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								await axios.get(
 | 
				
			||||||
 | 
									"http://localhost:8080/api/v1/collections/users/me"
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								setIsLogged(true);
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								setIsLogged(false);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						useEffect(() => {
 | 
				
			||||||
 | 
							void checkUser();
 | 
				
			||||||
 | 
						}, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const postLogin = async () => {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								await axios.post("http://localhost:8080/api/v1/sessions/", {
 | 
				
			||||||
 | 
									username: loginState.username,
 | 
				
			||||||
 | 
									password: loginState.password,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								void checkUser();
 | 
				
			||||||
 | 
							} catch (e) {
 | 
				
			||||||
 | 
								console.log(e);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const notLoggedForm = () => {
 | 
				
			||||||
 | 
							return (
 | 
				
			||||||
 | 
								<form>
 | 
				
			||||||
 | 
									<label htmlFor="username">
 | 
				
			||||||
 | 
										Nazwa użytkownika
 | 
				
			||||||
 | 
										<input
 | 
				
			||||||
 | 
											type="text"
 | 
				
			||||||
 | 
											name="username"
 | 
				
			||||||
 | 
											value={loginState.username}
 | 
				
			||||||
 | 
											onChange={(e) =>
 | 
				
			||||||
 | 
												setLoginState({
 | 
				
			||||||
 | 
													...loginState,
 | 
				
			||||||
 | 
													username: e.target.value,
 | 
				
			||||||
 | 
												})
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									</label>
 | 
				
			||||||
 | 
									<label htmlFor="password">
 | 
				
			||||||
 | 
										Hasło
 | 
				
			||||||
 | 
										<input
 | 
				
			||||||
 | 
											type="password"
 | 
				
			||||||
 | 
											name="password"
 | 
				
			||||||
 | 
											value={loginState.password}
 | 
				
			||||||
 | 
											onChange={(e) =>
 | 
				
			||||||
 | 
												setLoginState({
 | 
				
			||||||
 | 
													...loginState,
 | 
				
			||||||
 | 
													password: e.target.value,
 | 
				
			||||||
 | 
												})
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									</label>
 | 
				
			||||||
 | 
									<button
 | 
				
			||||||
 | 
										type="submit"
 | 
				
			||||||
 | 
										onClick={(e) => {
 | 
				
			||||||
 | 
											e.preventDefault();
 | 
				
			||||||
 | 
											console.log(postLogin());
 | 
				
			||||||
 | 
										}}
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										Zaloguj
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
								</form>
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<>
 | 
				
			||||||
 | 
								<Nav isLogged={isLogged} logoutState={() => setIsLogged(false)} />
 | 
				
			||||||
 | 
								{isLogged ? <h1>Jesteś zalogowany</h1> : notLoggedForm()}
 | 
				
			||||||
 | 
							</>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					ReactDOM.render(<Login />, document.getElementById("root"));
 | 
				
			||||||
@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					export type Offense = {
 | 
				
			||||||
 | 
						name: string;
 | 
				
			||||||
 | 
						car_is: string;
 | 
				
			||||||
 | 
						id: string;
 | 
				
			||||||
 | 
						implies?: string[];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const offenses: Offense[] = [
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "obszar_wyłączony",
 | 
				
			||||||
 | 
							name: "Parkowanie na obszarze wyłączonym z ruchu",
 | 
				
			||||||
 | 
							car_is: "jest zaparkowany na obszarze wyłączonym z ruchu",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "widoczność_pasy",
 | 
				
			||||||
 | 
							name: "Ograniczanie widoczności na pasach",
 | 
				
			||||||
 | 
							car_is:
 | 
				
			||||||
 | 
								"ogranicza widoczność na przejściu dla pieszych, powodując zagrożenie osób korzystających z przejścia",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "widoczność_skrz",
 | 
				
			||||||
 | 
							name: "Ograniczanie widoczności na skrzyżowaniu",
 | 
				
			||||||
 | 
							car_is:
 | 
				
			||||||
 | 
								"ogranicza widoczność na skrzyżowaniu, powodując zagrożenie dla uczestników ruchu",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "utrudnia_ruch_rowerom",
 | 
				
			||||||
 | 
							name: "Utrudnianie ruchu rowerowego",
 | 
				
			||||||
 | 
							car_is: "utrudnia ruch rowerowy",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "utrudnia_ruch_pieszym",
 | 
				
			||||||
 | 
							name: "Utrudnianie ruchu pieszego",
 | 
				
			||||||
 | 
							car_is: "utrudnia ruch pieszych",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "poza_wyzn",
 | 
				
			||||||
 | 
							name: "Parkowanie poza wyznaczonym miejscem",
 | 
				
			||||||
 | 
							car_is: "jest zaparkowany poza wyznaczonym miejscem parkingowym",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "przed_przejsc",
 | 
				
			||||||
 | 
							name: "Parkowanie <10m przed przejściem dla pieszych",
 | 
				
			||||||
 | 
							car_is: "jest zaparkowany mniej niż 10m przed przejściem dla pieszych",
 | 
				
			||||||
 | 
							implies: ["widoczność_pasy"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "na_zakazie",
 | 
				
			||||||
 | 
							name: "Parkowanie za znakiem zakazu parkowania",
 | 
				
			||||||
 | 
							car_is: "jest zaparkowany za znakiem zakazu parkowania",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "brama",
 | 
				
			||||||
 | 
							name: "Blokowanie bramy wjazdowej",
 | 
				
			||||||
 | 
							car_is: "blokuje bramę wjazdową",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "na_chodzie",
 | 
				
			||||||
 | 
							name: "Postój na chodzie",
 | 
				
			||||||
 | 
							car_is: "ma włączony silnik podczas postoju",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "blk_chodnik",
 | 
				
			||||||
 | 
							name: "Blokowanie chodnika",
 | 
				
			||||||
 | 
							car_is:
 | 
				
			||||||
 | 
								"jest zaparkowany na chodniku pozostawiając mniej niż 1,5m dla pieszych",
 | 
				
			||||||
 | 
							implies: ["utrudnia_ruch_pieszym"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "chyba2.5t",
 | 
				
			||||||
 | 
							name: "Duży samochód dostawczy na chodniku",
 | 
				
			||||||
 | 
							car_is:
 | 
				
			||||||
 | 
								"najprawdopodobniej przekracza dopuszczalną całkowitą masę 2,5t będąc zaparkowanym na chodniku",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "hydrant",
 | 
				
			||||||
 | 
							name: "Blokowanie hydrantu",
 | 
				
			||||||
 | 
							car_is: "blokuje dostęp do hydrantu",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "przystanek_15",
 | 
				
			||||||
 | 
							name: "Parkowanie <15m od przystanku",
 | 
				
			||||||
 | 
							car_is:
 | 
				
			||||||
 | 
								"jest zaparkowany w odległości mniejszej niż 15m od tablicy oznaczającej przystanek",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "utr_innym_zapark",
 | 
				
			||||||
 | 
							name: "Utrudnianie wyjazdu innym zaparkowanym samochodom",
 | 
				
			||||||
 | 
							car_is:
 | 
				
			||||||
 | 
								"dokonuje postoju w miejscu utrudniającym dostęp do innych, prawidłowo zaparkowanych pojazdów lub wyjazd tych pojazdów",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "pas_rowerow",
 | 
				
			||||||
 | 
							name: "blokuje pas rowerów",
 | 
				
			||||||
 | 
							car_is: "zaparkowany na pasie dla rowerów",
 | 
				
			||||||
 | 
							implies: ["utrudnia_ruch_rowerom"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "t30",
 | 
				
			||||||
 | 
							name: "Parkowanie niezgodnie z rysunkiem na tabliczce (T-30)",
 | 
				
			||||||
 | 
							car_is:
 | 
				
			||||||
 | 
								"nie stosuje się do znaku T-30, wskazującego sposób ustawienia pojazdu względem krawędzi jezdni",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							id: "blokuje_znak",
 | 
				
			||||||
 | 
							name: "Zasłanianie znaku drogowego",
 | 
				
			||||||
 | 
							car_is:
 | 
				
			||||||
 | 
								"zaparkowany w odległości mniejszej niż 10m od przedniej strony znaku drogowego, zasłaniając go (Art 49, punkt 1, ust. 6)",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
						<head>
 | 
				
			||||||
 | 
							<link
 | 
				
			||||||
 | 
								href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css"
 | 
				
			||||||
 | 
								rel="stylesheet"
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
							<title>Zgłaszańsko</title>
 | 
				
			||||||
 | 
						</head>
 | 
				
			||||||
 | 
						<body>
 | 
				
			||||||
 | 
							<div id="root"></div>
 | 
				
			||||||
 | 
							<script src="./register.tsx"></script>
 | 
				
			||||||
 | 
						</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					/* eslint-disable @typescript-eslint/no-unused-vars */
 | 
				
			||||||
 | 
					import React, { ReactElement, useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import ReactDOM from "react-dom";
 | 
				
			||||||
 | 
					import Nav from "./Nav";
 | 
				
			||||||
 | 
					import "./styles/reset.css";
 | 
				
			||||||
 | 
					import "./styles/app.scss";
 | 
				
			||||||
 | 
					import "regenerator-runtime/runtime";
 | 
				
			||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Register(): ReactElement {
 | 
				
			||||||
 | 
						const [isLogged, setIsLogged] = useState(false);
 | 
				
			||||||
 | 
						const [registerState, setRegisterState] = useState({
 | 
				
			||||||
 | 
							email: "",
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						const [error, setError] = useState(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const checkUser = async (): Promise<void> => {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								await axios.get(
 | 
				
			||||||
 | 
									"http://localhost:8080/api/v1/collections/users/me"
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								setIsLogged(true);
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								setIsLogged(false);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						useEffect(() => {
 | 
				
			||||||
 | 
							void checkUser();
 | 
				
			||||||
 | 
						}, []);
 | 
				
			||||||
 | 
						const postRegister = async () => {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								await axios.post(
 | 
				
			||||||
 | 
									"http://localhost:8080/api/v1/collections/registration-intents",
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										email: registerState.email,
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								void checkUser();
 | 
				
			||||||
 | 
							} catch (e) {
 | 
				
			||||||
 | 
								console.log(e);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						const notLoggedForm = () => {
 | 
				
			||||||
 | 
							return (
 | 
				
			||||||
 | 
								<form>
 | 
				
			||||||
 | 
									<label htmlFor="email">
 | 
				
			||||||
 | 
										Adres email
 | 
				
			||||||
 | 
										<input
 | 
				
			||||||
 | 
											type="email"
 | 
				
			||||||
 | 
											name="email"
 | 
				
			||||||
 | 
											value={registerState.email}
 | 
				
			||||||
 | 
											onChange={(e) =>
 | 
				
			||||||
 | 
												setRegisterState({
 | 
				
			||||||
 | 
													email: e.target.value,
 | 
				
			||||||
 | 
												})
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									</label>
 | 
				
			||||||
 | 
									<button
 | 
				
			||||||
 | 
										type="submit"
 | 
				
			||||||
 | 
										onClick={(e) => {
 | 
				
			||||||
 | 
											e.preventDefault();
 | 
				
			||||||
 | 
											console.log(postRegister());
 | 
				
			||||||
 | 
										}}
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										Zaloguj
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
								</form>
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<>
 | 
				
			||||||
 | 
								<Nav isLogged={isLogged} logoutState={() => setIsLogged(false)} />
 | 
				
			||||||
 | 
								{isLogged ? <h1>Jesteś zalogowany</h1> : notLoggedForm()}
 | 
				
			||||||
 | 
							</>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					ReactDOM.render(<Register />, document.getElementById("root"));
 | 
				
			||||||
@ -0,0 +1,147 @@
 | 
				
			|||||||
 | 
					.button:hover {
 | 
				
			||||||
 | 
						filter: brightness(1.1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.container {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						height: 100vh;
 | 
				
			||||||
 | 
						display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button {
 | 
				
			||||||
 | 
						background-color: #999;
 | 
				
			||||||
 | 
						height: 2rem;
 | 
				
			||||||
 | 
						line-height: 2rem;
 | 
				
			||||||
 | 
						padding: 0 0.75rem;
 | 
				
			||||||
 | 
						text-decoration: none;
 | 
				
			||||||
 | 
						color: white;
 | 
				
			||||||
 | 
						border: none;
 | 
				
			||||||
 | 
						cursor: pointer;
 | 
				
			||||||
 | 
						margin: 0 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.container__section {
 | 
				
			||||||
 | 
						flex: 1;
 | 
				
			||||||
 | 
						padding: 15px;
 | 
				
			||||||
 | 
						display: block;
 | 
				
			||||||
 | 
						height: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.container__section__title {
 | 
				
			||||||
 | 
						margin: 15px 0;
 | 
				
			||||||
 | 
						font-size: 1.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.container__section__photos {
 | 
				
			||||||
 | 
						display: grid;
 | 
				
			||||||
 | 
						grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
 | 
				
			||||||
 | 
						row-gap: 35px;
 | 
				
			||||||
 | 
						column-gap: 15px;
 | 
				
			||||||
 | 
						justify-items: stretch;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.section__photos__item {
 | 
				
			||||||
 | 
						width: 150px;
 | 
				
			||||||
 | 
						height: 150px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.section__photos__item--active {
 | 
				
			||||||
 | 
						width: 150px;
 | 
				
			||||||
 | 
						height: 150px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.container__section__map {
 | 
				
			||||||
 | 
						cursor: default;
 | 
				
			||||||
 | 
						height: 600px;
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.container__section__form {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						height: 600px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.input {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						height: 30px;
 | 
				
			||||||
 | 
						margin-bottom: 15px;
 | 
				
			||||||
 | 
						margin-top: 5px;
 | 
				
			||||||
 | 
						display: block;
 | 
				
			||||||
 | 
						box-sizing: border-box;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.textarea {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						height: 350px;
 | 
				
			||||||
 | 
						margin-bottom: 15px;
 | 
				
			||||||
 | 
						margin-top: 5px;
 | 
				
			||||||
 | 
						display: block;
 | 
				
			||||||
 | 
						box-sizing: border-box;
 | 
				
			||||||
 | 
						resize: none;
 | 
				
			||||||
 | 
						font-size: 1.1rem;
 | 
				
			||||||
 | 
						padding: 3px 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.textarea--small {
 | 
				
			||||||
 | 
						height: 150px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.label {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.container__section__form__offenses {
 | 
				
			||||||
 | 
						margin-bottom: 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					//to change name
 | 
				
			||||||
 | 
					.img {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.img--active {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						height: 100%;
 | 
				
			||||||
 | 
						border: 3px solid seagreen;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chceckbox {
 | 
				
			||||||
 | 
						appearance: none;
 | 
				
			||||||
 | 
						vertical-align: middle;
 | 
				
			||||||
 | 
						font-size: inherit;
 | 
				
			||||||
 | 
						cursor: pointer;
 | 
				
			||||||
 | 
						width: 1.5em;
 | 
				
			||||||
 | 
						height: 1.5em;
 | 
				
			||||||
 | 
						background: white;
 | 
				
			||||||
 | 
						border: 0.1em solid #000000;
 | 
				
			||||||
 | 
						position: relative;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					input[type="checkbox"]:checked {
 | 
				
			||||||
 | 
						background: #000000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/*breakpoint for mobile*/
 | 
				
			||||||
 | 
					@media (max-width: 1200px) {
 | 
				
			||||||
 | 
						body {
 | 
				
			||||||
 | 
							font-size: 3rem;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.container {
 | 
				
			||||||
 | 
							flex-direction: column;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.container__section__title {
 | 
				
			||||||
 | 
							margin: 25px 0;
 | 
				
			||||||
 | 
							font-size: 1.5em;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.container__section__photos {
 | 
				
			||||||
 | 
							grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.section__photos__item {
 | 
				
			||||||
 | 
							width: 200px;
 | 
				
			||||||
 | 
							height: 200px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.container__section__form {
 | 
				
			||||||
 | 
							width: 100%;
 | 
				
			||||||
 | 
							height: 600px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.textarea {
 | 
				
			||||||
 | 
							font-size: 2rem;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.input {
 | 
				
			||||||
 | 
							width: 90%;
 | 
				
			||||||
 | 
							height: 75px;
 | 
				
			||||||
 | 
							font-size: 2rem;
 | 
				
			||||||
 | 
							margin-top: 10px;
 | 
				
			||||||
 | 
							margin-bottom: 25px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.input--textarea {
 | 
				
			||||||
 | 
							height: 450px;
 | 
				
			||||||
 | 
							width: 90%;
 | 
				
			||||||
 | 
							margin-top: 10px;
 | 
				
			||||||
 | 
							margin-bottom: 25px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					.navbar {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						display: flex;
 | 
				
			||||||
 | 
						align-items: center;
 | 
				
			||||||
 | 
						justify-content: space-between;
 | 
				
			||||||
 | 
						border-bottom: #000000 2px solid;
 | 
				
			||||||
 | 
						padding: 15px 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.navbar__title {
 | 
				
			||||||
 | 
						font-size: 1.5em;
 | 
				
			||||||
 | 
						margin: 0 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.navbar__links {
 | 
				
			||||||
 | 
						display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.button-link {
 | 
				
			||||||
 | 
						background-color: #999;
 | 
				
			||||||
 | 
						height: 2rem;
 | 
				
			||||||
 | 
						line-height: 2rem;
 | 
				
			||||||
 | 
						padding: 0 0.75rem;
 | 
				
			||||||
 | 
						text-decoration: none;
 | 
				
			||||||
 | 
						color: white;
 | 
				
			||||||
 | 
						border: none;
 | 
				
			||||||
 | 
						cursor: pointer;
 | 
				
			||||||
 | 
						margin: 0 15px;
 | 
				
			||||||
 | 
						font-size: 0.9em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,127 @@
 | 
				
			|||||||
 | 
					/* http://meyerweb.com/eric/tools/css/reset/ 
 | 
				
			||||||
 | 
					   v2.0 | 20110126
 | 
				
			||||||
 | 
					   License: none (public domain)
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html,
 | 
				
			||||||
 | 
					body,
 | 
				
			||||||
 | 
					div,
 | 
				
			||||||
 | 
					span,
 | 
				
			||||||
 | 
					applet,
 | 
				
			||||||
 | 
					object,
 | 
				
			||||||
 | 
					iframe,
 | 
				
			||||||
 | 
					h1,
 | 
				
			||||||
 | 
					h2,
 | 
				
			||||||
 | 
					h3,
 | 
				
			||||||
 | 
					h4,
 | 
				
			||||||
 | 
					h5,
 | 
				
			||||||
 | 
					h6,
 | 
				
			||||||
 | 
					p,
 | 
				
			||||||
 | 
					blockquote,
 | 
				
			||||||
 | 
					pre,
 | 
				
			||||||
 | 
					a,
 | 
				
			||||||
 | 
					abbr,
 | 
				
			||||||
 | 
					acronym,
 | 
				
			||||||
 | 
					address,
 | 
				
			||||||
 | 
					big,
 | 
				
			||||||
 | 
					cite,
 | 
				
			||||||
 | 
					code,
 | 
				
			||||||
 | 
					del,
 | 
				
			||||||
 | 
					dfn,
 | 
				
			||||||
 | 
					em,
 | 
				
			||||||
 | 
					img,
 | 
				
			||||||
 | 
					ins,
 | 
				
			||||||
 | 
					kbd,
 | 
				
			||||||
 | 
					q,
 | 
				
			||||||
 | 
					s,
 | 
				
			||||||
 | 
					samp,
 | 
				
			||||||
 | 
					small,
 | 
				
			||||||
 | 
					strike,
 | 
				
			||||||
 | 
					strong,
 | 
				
			||||||
 | 
					sub,
 | 
				
			||||||
 | 
					sup,
 | 
				
			||||||
 | 
					tt,
 | 
				
			||||||
 | 
					var,
 | 
				
			||||||
 | 
					b,
 | 
				
			||||||
 | 
					u,
 | 
				
			||||||
 | 
					i,
 | 
				
			||||||
 | 
					center,
 | 
				
			||||||
 | 
					dl,
 | 
				
			||||||
 | 
					dt,
 | 
				
			||||||
 | 
					dd,
 | 
				
			||||||
 | 
					ol,
 | 
				
			||||||
 | 
					ul,
 | 
				
			||||||
 | 
					li,
 | 
				
			||||||
 | 
					fieldset,
 | 
				
			||||||
 | 
					form,
 | 
				
			||||||
 | 
					label,
 | 
				
			||||||
 | 
					legend,
 | 
				
			||||||
 | 
					table,
 | 
				
			||||||
 | 
					caption,
 | 
				
			||||||
 | 
					tbody,
 | 
				
			||||||
 | 
					tfoot,
 | 
				
			||||||
 | 
					thead,
 | 
				
			||||||
 | 
					tr,
 | 
				
			||||||
 | 
					th,
 | 
				
			||||||
 | 
					td,
 | 
				
			||||||
 | 
					article,
 | 
				
			||||||
 | 
					aside,
 | 
				
			||||||
 | 
					canvas,
 | 
				
			||||||
 | 
					details,
 | 
				
			||||||
 | 
					embed,
 | 
				
			||||||
 | 
					figure,
 | 
				
			||||||
 | 
					figcaption,
 | 
				
			||||||
 | 
					footer,
 | 
				
			||||||
 | 
					header,
 | 
				
			||||||
 | 
					hgroup,
 | 
				
			||||||
 | 
					menu,
 | 
				
			||||||
 | 
					nav,
 | 
				
			||||||
 | 
					output,
 | 
				
			||||||
 | 
					ruby,
 | 
				
			||||||
 | 
					section,
 | 
				
			||||||
 | 
					summary,
 | 
				
			||||||
 | 
					time,
 | 
				
			||||||
 | 
					mark,
 | 
				
			||||||
 | 
					audio,
 | 
				
			||||||
 | 
					video {
 | 
				
			||||||
 | 
						margin: 0;
 | 
				
			||||||
 | 
						padding: 0;
 | 
				
			||||||
 | 
						border: 0;
 | 
				
			||||||
 | 
						vertical-align: baseline;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/* HTML5 display-role reset for older browsers */
 | 
				
			||||||
 | 
					article,
 | 
				
			||||||
 | 
					aside,
 | 
				
			||||||
 | 
					details,
 | 
				
			||||||
 | 
					figcaption,
 | 
				
			||||||
 | 
					figure,
 | 
				
			||||||
 | 
					footer,
 | 
				
			||||||
 | 
					header,
 | 
				
			||||||
 | 
					hgroup,
 | 
				
			||||||
 | 
					menu,
 | 
				
			||||||
 | 
					nav,
 | 
				
			||||||
 | 
					section {
 | 
				
			||||||
 | 
						display: block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
						line-height: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					ol,
 | 
				
			||||||
 | 
					ul {
 | 
				
			||||||
 | 
						list-style: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					blockquote,
 | 
				
			||||||
 | 
					q {
 | 
				
			||||||
 | 
						quotes: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					blockquote:before,
 | 
				
			||||||
 | 
					blockquote:after,
 | 
				
			||||||
 | 
					q:before,
 | 
				
			||||||
 | 
					q:after {
 | 
				
			||||||
 | 
						content: "";
 | 
				
			||||||
 | 
						content: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					table {
 | 
				
			||||||
 | 
						border-collapse: collapse;
 | 
				
			||||||
 | 
						border-spacing: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					 | 
				
			||||||
<html>
 | 
					 | 
				
			||||||
<head>
 | 
					 | 
				
			||||||
  <title>Zgłaszańsko</title>
 | 
					 | 
				
			||||||
</head>
 | 
					 | 
				
			||||||
<body>
 | 
					 | 
				
			||||||
  <h1>Zgłaszańsko</h1>
 | 
					 | 
				
			||||||
</body>
 | 
					 | 
				
			||||||
</html>
 | 
					 | 
				
			||||||
@ -1 +0,0 @@
 | 
				
			|||||||
import { submit } from "poznan-sm-submit";
 | 
					 | 
				
			||||||
@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					FROM node:15-alpine
 | 
				
			||||||
 | 
					LABEL maintainer="Jakub Pieńkowski <jakski@sealcode.org>"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENV UID=node \
 | 
				
			||||||
 | 
					    GID=node \
 | 
				
			||||||
 | 
					    HOME=/opt/sealious
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN sed -i 's/http\:\/\/dl-cdn.alpinelinux.org/https\:\/\/mirrors.dotsrc.org/g' /etc/apk/repositories
 | 
				
			||||||
 | 
					# Tini will ensure that any orphaned processes get reaped properly.
 | 
				
			||||||
 | 
					RUN apk add --no-cache tini
 | 
				
			||||||
 | 
					RUN apk --update add git
 | 
				
			||||||
 | 
					RUN apk --update add python
 | 
				
			||||||
 | 
					RUN apk --update add make
 | 
				
			||||||
 | 
					RUN apk --update add g++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VOLUME $HOME
 | 
				
			||||||
 | 
					WORKDIR $HOME
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USER $UID:$GID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXPOSE 8080
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRYPOINT ["/sbin/tini", "--"]
 | 
				
			||||||
 | 
					CMD ["/usr/local/bin/node", "."]
 | 
				
			||||||
					Loading…
					
					
				
		Reference in New Issue