Compare commits
	
		
			No commits in common. 'master' and 'submitter-web' have entirely different histories. 
		
	
	
		
			master
			...
			submitter-
		
	
		
	@ -1,38 +0,0 @@
 | 
			
		||||
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,11 +1,3 @@
 | 
			
		||||
node_modules
 | 
			
		||||
.node_repl_history
 | 
			
		||||
.config
 | 
			
		||||
.npm
 | 
			
		||||
.idea
 | 
			
		||||
.DS_Store
 | 
			
		||||
lib
 | 
			
		||||
dist
 | 
			
		||||
@types
 | 
			
		||||
.cache
 | 
			
		||||
public
 | 
			
		||||
/.cache/
 | 
			
		||||
/dist/
 | 
			
		||||
/node_modules/
 | 
			
		||||
 | 
			
		||||
@ -1,14 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
	"useTabs": true,
 | 
			
		||||
	"tabWidth": 4,
 | 
			
		||||
	"trailingComma": "es5",
 | 
			
		||||
	"overrides": [
 | 
			
		||||
		{
 | 
			
		||||
			"files": "*.yml",
 | 
			
		||||
			"options": {
 | 
			
		||||
				"tabWidth": 2,
 | 
			
		||||
				"useTabs": false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +0,0 @@
 | 
			
		||||
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.
											
										
									
								| 
		 Before Width: | Height: | Size: 328 B  | 
@ -1,51 +0,0 @@
 | 
			
		||||
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"));
 | 
			
		||||
@ -1,52 +0,0 @@
 | 
			
		||||
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;
 | 
			
		||||
@ -1,347 +0,0 @@
 | 
			
		||||
/* 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"));
 | 
			
		||||
@ -1,7 +0,0 @@
 | 
			
		||||
export interface FileType {
 | 
			
		||||
	lastModified: number;
 | 
			
		||||
	name: string;
 | 
			
		||||
	size: number;
 | 
			
		||||
	type: string;
 | 
			
		||||
	webkitRelativePath: string;
 | 
			
		||||
}
 | 
			
		||||
@ -1,49 +0,0 @@
 | 
			
		||||
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();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,60 +0,0 @@
 | 
			
		||||
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();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,14 +0,0 @@
 | 
			
		||||
<!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>
 | 
			
		||||
@ -1,24 +0,0 @@
 | 
			
		||||
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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,14 +0,0 @@
 | 
			
		||||
<!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>
 | 
			
		||||
@ -1,94 +0,0 @@
 | 
			
		||||
/* 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"));
 | 
			
		||||
@ -1,110 +0,0 @@
 | 
			
		||||
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)",
 | 
			
		||||
	},
 | 
			
		||||
];
 | 
			
		||||
@ -1,14 +0,0 @@
 | 
			
		||||
<!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>
 | 
			
		||||
@ -1,78 +0,0 @@
 | 
			
		||||
/* 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"));
 | 
			
		||||
@ -1,147 +0,0 @@
 | 
			
		||||
.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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,27 +0,0 @@
 | 
			
		||||
.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;
 | 
			
		||||
}
 | 
			
		||||
@ -1,127 +0,0 @@
 | 
			
		||||
/* 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;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
  <title>Zgłaszańsko</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <h1>Zgłaszańsko</h1>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
import { submit } from "poznan-sm-submit";
 | 
			
		||||
@ -1,24 +0,0 @@
 | 
			
		||||
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