780 lines
38 KiB
JavaScript
780 lines
38 KiB
JavaScript
import '../styles/index.scss';
|
|
|
|
import React, { Component } from 'react';
|
|
import { render } from "react-dom";
|
|
|
|
import Gallery from "react-photo-gallery";
|
|
import { Modal, ModalGateway } from "react-images";
|
|
import ImageGallery from "react-image-gallery";
|
|
import axios from 'axios';
|
|
|
|
|
|
const addr_prefix = "" //"http://localhost:8000"
|
|
const delivery_cost = 79
|
|
|
|
|
|
class Main extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
status_text: "",
|
|
status: "",
|
|
faq: false,
|
|
racer: {},
|
|
showRacerModalWindow: false,
|
|
results: [],
|
|
gallery_open: undefined,
|
|
page: window.location.hash,
|
|
photos: [],
|
|
products: [],
|
|
photoIsOpen: false,
|
|
progress: false,
|
|
currentImage: 0,
|
|
uploadProgress: 0.0,
|
|
login_status: {},
|
|
cart: [],
|
|
delivery: undefined,
|
|
};
|
|
fetch(addr_prefix + "/api/results").then(resp => resp.json()).then(json => {
|
|
this.setState({results: json})
|
|
})
|
|
fetch(addr_prefix + "/api/photos").then(resp => resp.json()).then(json => {
|
|
this.setState({photos: json})
|
|
})
|
|
fetch(addr_prefix + "/api/products").then(resp => resp.json()).then(json => {
|
|
this.setState({products: json})
|
|
})
|
|
fetch(addr_prefix + "/api/cart").then(resp => resp.json()).then(json => {
|
|
this.setState({cart: [...json, ...this.state.cart]})
|
|
})
|
|
fetch(addr_prefix + "/api/login_status").then(resp => resp.json()).then(json => {
|
|
this.setState({login_status: json})
|
|
})
|
|
fetch(addr_prefix + "/api/cart/delivery").then(resp => resp.json()).then(json => {
|
|
if (json){
|
|
this.setState({
|
|
cart: [...this.state.cart, {"name": "Doprava na " + json, "id": 2, "price": delivery_cost, "locked": true, "quantity": 1}],
|
|
delivery: json,
|
|
})
|
|
}
|
|
})
|
|
if(window.location.search){
|
|
let searchParams = new URLSearchParams(window.location.search);
|
|
for(const [key, value] of searchParams){
|
|
if (key == "refId"){
|
|
window.history.replaceState( {} , document.title , "/#" );
|
|
fetch(addr_prefix + "/api/payment_state?refId=" + value).then(resp => resp.json()).then(json =>{
|
|
this.setState({status: json.status, status_text: json.reason})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
window.addEventListener("hashchange", (e) => {this.onHashChange(e)})
|
|
}
|
|
openGallery = (i) => {
|
|
this.setState({gallery_open: i})
|
|
}
|
|
closePhoto = () => {
|
|
this.setState(state => ({ photoIsOpen: false }))
|
|
}
|
|
openPhoto = (event, {index}) => {
|
|
this.setState({
|
|
currentImage: index,
|
|
photoIsOpen: true,
|
|
})
|
|
}
|
|
onHashChange(event){
|
|
this.setState({page: window.location.hash, gallery_open: undefined})
|
|
}
|
|
editRacer = (racer) => {
|
|
this.setState({
|
|
racer: racer,
|
|
showRacerModalWindow: true,
|
|
})
|
|
}
|
|
addToCart = (product) =>{
|
|
fetch(addr_prefix + "/api/cart/add?id=" + product.id).then(resp => resp.json()).then(json=>{
|
|
if(json.status == "failed"){
|
|
this.setState({
|
|
status_text: json.reason,
|
|
status: "failed",
|
|
})
|
|
}else{
|
|
if(this.state.cart.filter(iter_product => {
|
|
if(iter_product.id == product.id){
|
|
iter_product.quantity += 1
|
|
return true
|
|
}else{
|
|
return false
|
|
}
|
|
}).length == 0){
|
|
this.setState({
|
|
status_text: json.reason,
|
|
status: "success",
|
|
cart: [...this.state.cart, {...product, quantity: 1}],
|
|
})
|
|
}else {
|
|
this.setState({
|
|
status_text: json.reason,
|
|
status: "success",
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
calculateTotalPrice(){
|
|
return this.state.cart.reduce((acc, cur) => acc + cur.price * cur.quantity, 0)
|
|
}
|
|
decreaseInCart(product){
|
|
fetch(addr_prefix + "/api/cart/decrease?id=" + product.id).then(resp => resp.json()).then(json=>{
|
|
if(json.status == "failed"){
|
|
this.setState({
|
|
status_text: json.reason,
|
|
status: "failed",
|
|
})
|
|
}else{
|
|
this.setState({
|
|
cart: this.state.cart.filter(iter_product => {
|
|
if(iter_product.id == product.id){
|
|
iter_product.quantity -= 1
|
|
if(iter_product.quantity == 0)
|
|
return false;
|
|
return true
|
|
}else{
|
|
return true
|
|
}
|
|
}),
|
|
status_text: json.reason,
|
|
status: "success",
|
|
})
|
|
}
|
|
})
|
|
}
|
|
onSubmit(event, options={}){
|
|
event.preventDefault();
|
|
let form = document.getElementById("form");
|
|
let api_endpoint = form.action;
|
|
let formData = new FormData(form);
|
|
for(let option in options){
|
|
formData.append(option, options[option]);
|
|
}
|
|
if(this.state.progress){
|
|
return;
|
|
}
|
|
this.setState({progress: true})
|
|
axios(
|
|
{
|
|
method: 'post',
|
|
url: addr_prefix + api_endpoint,
|
|
data: formData,
|
|
onUploadProgress: ({loaded, total}) => this.setState({uploadProgress: loaded/total})
|
|
}
|
|
).then(({data}) => {
|
|
this.setState({progress:false, uploadProgress: 1.0});
|
|
if(data.reason){
|
|
this.setState({
|
|
status_text: data.reason,
|
|
status: "failed",
|
|
})
|
|
} else {
|
|
fetch(addr_prefix + "/api/login_status").then(resp => resp.json()).then(json => {
|
|
this.setState({login_status: json})
|
|
})
|
|
fetch(addr_prefix + "/api/cart").then(resp => resp.json()).then(json => {
|
|
this.setState({cart: json})
|
|
})
|
|
this.setState({
|
|
showRacerModalWindow: false,
|
|
status_text: data.success,
|
|
status: "success",
|
|
})
|
|
if (data.redirect){
|
|
window.open(data.redirect ,"_self")
|
|
}
|
|
}
|
|
}).catch((error) => {
|
|
this.setState({
|
|
progress: false,
|
|
status_text: "Chyba: " + error.response.status +", " + error.response.data.reason,
|
|
status: "failed",
|
|
})
|
|
});
|
|
}
|
|
handleChange = (event) => {
|
|
const { target } = event;
|
|
this.setState((prevState) => ({
|
|
...prevState,
|
|
racer: {...prevState.racer, [target.name]: target.value,}
|
|
}));
|
|
};
|
|
selectDeliveryCallback(point){
|
|
if(point){
|
|
fetch(addr_prefix + '/api/cart/select_delivery?delivery=' + point.name).then(resp => resp.json()).then(json => {
|
|
if(json.status == "failed"){
|
|
this.setState({
|
|
status_text: json.reason,
|
|
status: "failed",
|
|
})
|
|
}else{
|
|
this.setState({
|
|
cart: [
|
|
...this.state.cart.filter(product => !product.name.startsWith("Doprava na ")),
|
|
{"name": "Doprava na " + point.name, "id": 2, "price": delivery_cost, "locked": true, "quantity": 1}
|
|
],
|
|
delivery: point.name,
|
|
status_text: json.reason,
|
|
status: "success",
|
|
})
|
|
}
|
|
});
|
|
}
|
|
}
|
|
selectDelivery = () => {
|
|
Packeta.Widget.pick("8599115769fd895", this.selectDeliveryCallback.bind(this), {
|
|
language: "cs",
|
|
})
|
|
}
|
|
togglefaq(){
|
|
this.setState({faq: !this.state.faq});
|
|
}
|
|
renderCloseButton(){
|
|
return <button class="image-gallery-close image-gallery-icon" onClick={(e) => {this.closePhoto()}}>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="92" id="cross"><path fill="white" d="M70.7 64.3c1.8 1.8 1.8 4.6 0 6.4-.9.9-2 1.3-3.2 1.3-1.2 0-2.3-.4-3.2-1.3L46 52.4 27.7 70.7c-.9.9-2 1.3-3.2 1.3s-2.3-.4-3.2-1.3c-1.8-1.8-1.8-4.6 0-6.4L39.6 46 21.3 27.7c-1.8-1.8-1.8-4.6 0-6.4 1.8-1.8 4.6-1.8 6.4 0L46 39.6l18.3-18.3c1.8-1.8 4.6-1.8 6.4 0 1.8 1.8 1.8 4.6 0 6.4L52.4 46l18.3 18.3z"></path></svg>
|
|
</button>
|
|
}
|
|
deleteProduct(product){
|
|
fetch(addr_prefix + "/api/cart/delete?id=" + product.id).then(resp => resp.json()).then(json=>{
|
|
if(json.status == "failed"){
|
|
this.setState({
|
|
status_text: json.reason,
|
|
status: "failed",
|
|
})
|
|
}else{
|
|
this.setState({
|
|
status_text: json.reason,
|
|
status: "success",
|
|
cart: this.state.cart.filter(p => product.id != p.id),
|
|
})
|
|
}
|
|
})
|
|
}
|
|
render(){
|
|
let photos_gallery = [];
|
|
let photos_carousel = [];
|
|
if(this.state.gallery_open !== undefined){
|
|
for (const photo of this.state.photos[this.state.gallery_open].photos){
|
|
photos_gallery.push({
|
|
src: photo.thumbnail,
|
|
width: photo.original_width,
|
|
height: photo.original_height,
|
|
})
|
|
photos_carousel.push({
|
|
original: photo.original
|
|
})
|
|
};
|
|
}
|
|
return (
|
|
<div>
|
|
<ModalGateway>
|
|
{this.state.photoIsOpen &&
|
|
<Modal onClose={this.closePhoto}>
|
|
<ImageGallery
|
|
items={photos_carousel}
|
|
lazyLoad={true}
|
|
showPlayButton={false}
|
|
startIndex={this.state.currentImage}
|
|
showThumbnails={false}
|
|
renderCustomControls={this.renderCloseButton.bind(this)}
|
|
/>
|
|
</Modal>
|
|
}
|
|
</ModalGateway>
|
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
|
<div class="container">
|
|
<ul class="navbar-nav mb-0">
|
|
<li class="nav-item">
|
|
<a class="nav-link" aria-current="page" href="#">Domů</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#gallery">Galerie</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#upload_files">Nahrát vlastní fotky/videa</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#payment">Platba</a>
|
|
</li>
|
|
{Object.keys(this.state.login_status).length === 0 &&
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#login">Přihlášení</a>
|
|
</li>
|
|
}
|
|
{Object.keys(this.state.login_status).length === 0 &&
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#register_user">Registrace</a>
|
|
</li>
|
|
}
|
|
{(this.state.login_status.racers || []).length > 0 &&
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#racers">Závodníci</a>
|
|
</li>
|
|
}
|
|
{Object.keys(this.state.login_status).length > 0 &&
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#register_racer">Registrovat závodníka</a>
|
|
</li>
|
|
}
|
|
{Object.keys(this.state.login_status).length > 0 &&
|
|
<li class="nav-item">
|
|
<div class="nav-link">Přihlášen {this.state.login_status.first_name} {this.state.login_status.last_name} <a href="/api/logout">Odhlásit</a></div>
|
|
</li>
|
|
}
|
|
{this.state.products.length > 0 &&
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#eshop">Eshop</a>
|
|
</li>
|
|
}
|
|
{this.state.cart.length > 0 &&
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#cart">Košík</a>
|
|
</li>
|
|
}
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
{this.state.status == "success" &&
|
|
<div class="absolute container-fluid alert alert-success" onClick={(e) => {this.setState({status: "", status_text:""})}}>
|
|
{this.state.status_text}
|
|
</div>}
|
|
{this.state.status == "failed" &&
|
|
<div class="absolute container-fluid alert alert-danger" onClick={(e) => {this.setState({status: "", status_text:""})}}>
|
|
{this.state.status_text}
|
|
</div>}
|
|
{this.state.page == "#cart" &&
|
|
<div class="container">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Název</th>
|
|
<th scope="col">Množství</th>
|
|
<th scope="col">Cena</th>
|
|
<th scope="col">Smazat</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{this.state.cart.map(product =>
|
|
<tr>
|
|
<td>{product.name}</td>
|
|
<td>
|
|
{!product.locked && <button type="button" class="btn btn-light" onClick={(e) => {this.addToCart(product)}}>+</button>}
|
|
{product.quantity}
|
|
{!product.locked && <button type="button" class="btn btn-light" onClick={(e) => {this.decreaseInCart(product)}}>-</button>}
|
|
</td>
|
|
<td>{product.price} Kč</td>
|
|
<td>{!product.locked && <button type="button" class="btn btn-light" onClick={(e) => {this.deleteProduct(product)}}>Smazat</button>}</td>
|
|
</tr>)}
|
|
<tr>
|
|
<td>Celkem</td>
|
|
<td><button class="btn btn-primary" type="submit" onClick={(e) => this.selectDelivery()}>Vybrat Zásilkovnu</button></td>
|
|
<td>{this.calculateTotalPrice()} Kč</td>
|
|
<td><form id="form" action='/api/cart/buy' onSubmit={(e) => this.onSubmit(e)}><button class="btn btn-primary" type="submit"><input type="hidden" name="something" value="nothing" />Koupit</button></form></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>}
|
|
{this.state.page == "#eshop" &&
|
|
<div class="container" style={{display: "flex"}}>
|
|
{this.state.products.map(product =>
|
|
<div class="card" style={{width: "18rem"}}>
|
|
<img src={product.img} class="card-img-top" alt="..." />
|
|
<div class="card-body">
|
|
<h5 class="card-title">{product.name}</h5>
|
|
<p class="card-text">{product.description}</p>
|
|
{product.price} Kč <a href="#" onClick={(e) => {e.preventDefault();this.addToCart(product)}} class="btn btn-primary">Přidat do košíku</a>
|
|
</div>
|
|
</div>)}
|
|
</div>}
|
|
{this.state.page == "#racers" &&
|
|
<div class="container">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Jméno</th>
|
|
<th scope="col">Přijmení</th>
|
|
<th scope="col">Email</th>
|
|
<th scope="col">Team</th>
|
|
<th scope="col">Datum narození</th>
|
|
<th scope="col">Telefon</th>
|
|
<th scope="col">Zaplatil</th>
|
|
<th scope="col">Změnit</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{(this.state.login_status.racers || []).map(racer =>
|
|
<tr>
|
|
<td>{racer.first_name}</td>
|
|
<td>{racer.last_name}</td>
|
|
<td>{racer.email}</td>
|
|
<td>{racer.team}</td>
|
|
<td>{racer.date_of_birth}</td>
|
|
<td>{racer.phone}</td>
|
|
<td>{racer.paid?"Zaplatil":"Nezaplatil"}</td>
|
|
<td><button type="button" class="btn btn-light" onClick={(e) => {this.editRacer(racer)}}>Změnit</button></td>
|
|
</tr>)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
{this.state.page == "#register_racer" &&
|
|
<div>
|
|
<form id="form" class="container" action="/api/register_racer" onSubmit={(e) => this.onSubmit(e)}>
|
|
<h1>Registrace Závodníka</h1>
|
|
<div class="mb-3">
|
|
<label for="first_name" class="form-label">Jméno</label>
|
|
<input type="text" class="form-control" id="first_name" name="first_name" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="last_name" class="form-label">Přijmení</label>
|
|
<input type="text" class="form-control" id="last_name" name="last_name" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label">Emailová adresa (volitelné)</label>
|
|
<input type="email" class="form-control" id="email" name="email" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="date_of_birth" class="form-label">Datum narození</label>
|
|
<input type="date" class="form-control" id="date_of_birth" name="date_of_birth" max={new Date().toJSON().slice(0, 10)} />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="phone" class="form-label">Telefonní číslo (volitelné)</label>
|
|
<input type="text" class="form-control" id="phone" name="phone" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="phone" class="form-label">Team (volitelné)</label>
|
|
<input type="text" class="form-control" id="team" name="team" />
|
|
</div>
|
|
<div class="mb-3 form-check">
|
|
<input type="checkbox" class="form-check-input" id="agreement" name="agreement" />
|
|
<label class="form-check-label" for="agreement">Souhlas se <a href="/public/ochrana_osobnich_udaju.pdf">zpracováním osobních údajů</a>, <a href="/public/reverz.pdf">podmínkami závodu</a> a <a href="/public/obchodni_podminky_alkator.pdf">obchodníma podmínkami závodu</a>.</label>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Registrovat a zaplatit</button>
|
|
</form>
|
|
</div>
|
|
}
|
|
{this.state.page == "#register_user" &&
|
|
<div>
|
|
<form id="form" action="/api/register_user" class="container" onSubmit={(e) => this.onSubmit(e)}>
|
|
<div class="mb-3">
|
|
<label for="first_name" class="form-label">Jméno</label>
|
|
<input type="text" class="form-control" id="first_name" name="first_name" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="last_name" class="form-label">Přijmení</label>
|
|
<input type="text" class="form-control" id="last_name" name="last_name" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label">Přihlašovací email:</label>
|
|
<input name="email" class="form-control" type="email" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="address" class="form-label">Fakturační Adresa</label>
|
|
<input type="text" class="form-control" id="address" name="address" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="phone" class="form-label">Telefon</label>
|
|
<input type="text" class="form-control" id="phone" name="phone" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="password1" class="form-label">Heslo:</label>
|
|
<input type="password" class="form-control" name="password1"/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="password2" class="form-label">Znovu heslo:</label>
|
|
<input type="password" class="form-control" name="password2"/>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Registrovat</button>
|
|
</form>
|
|
</div>
|
|
}
|
|
{this.state.page == "#forgotten_password" &&
|
|
<div>
|
|
<form id="form" action="/api/forgotten_password" class="container" onSubmit={(e) => this.onSubmit(e)}>
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label">Přihlašovací email:</label>
|
|
<input name="email" class="form-control" type="email" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="code" class="form-label">Kód pro obnovení hesla:</label>
|
|
<input type="text" class="form-control" name="code"/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="password1" class="form-label">Nové heslo:</label>
|
|
<input type="password" class="form-control" name="password1"/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="password2" class="form-label">Nové heslo znova:</label>
|
|
<input type="password" class="form-control" name="password2"/>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Obnovit heslo a přihlásit</button>
|
|
</form>
|
|
</div>
|
|
}
|
|
{this.state.page == "#login" &&
|
|
<div>
|
|
<form id="form" action="/api/login" class="container" onSubmit={(e) => this.onSubmit(e)}>
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label">Přihlašovací email:</label>
|
|
<input name="email" class="form-control" type="email" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="password" class="form-label">Heslo:</label>
|
|
<input type="password" class="form-control" name="password"/>
|
|
</div>
|
|
<button name="forgotten_password" value="true" class="btn btn-light" onClick={(e) => {this.onSubmit(e, {"forgotten_password": "true"})}}>Zapomenuté heslo</button>
|
|
<button type="submit" class="btn btn-primary">Přihlásit</button>
|
|
</form>
|
|
</div>
|
|
}
|
|
{this.state.page == "#upload_files" &&
|
|
<div>
|
|
<form id="form" action="/api/upload_files" class="container" onSubmit={(e) => this.onSubmit(e)}>
|
|
<label>Zvolte soubory <input type="file" name="files" accept=".jpg,.png,.webp,.webm,.avi,.mkv,.mp4" multiple={true} onChange={(e)=> this.handleChangeFiles.bind(this)(e)}/></label>
|
|
<button type="submit" class="btn btn-primary">Nahrát</button><b>progress: {(this.state.uploadProgress * 100).toFixed(1)}%</b><br />
|
|
Nahráním fotek/videí nám dávate souhlas s použitím vaších fotek k propagaci a zvěřejněním na stránkách.
|
|
</form>
|
|
</div>}
|
|
{this.state.page == "#payment" &&
|
|
<div class="container text">
|
|
Platební bránu poskytuje firma <a href="https://www.comgate.cz/cz/platebni-brana">Comgate, a.s</a>. Platit je možné pomocí <a href="https://help.comgate.cz/v1/docs/cs/platby-kartou">platební karty</a> nebo <a href="https://help.comgate.cz/docs/bankovni-prevody">bankovním převodem</a>. Podrobnosti a přesný postup platby včetně animací najdete v odkazech.<br />
|
|
Kontakt na Comgate, a.s.<br />
|
|
Gočárova třída 1754 / 48b, Hradec Králové<br />
|
|
E-mail: platby-podpora@comgate.cz<br />
|
|
Tel: +420 228 224 267<br />
|
|
</div>
|
|
}
|
|
{this.state.page == "#gallery" &&
|
|
<div class="container" style={{display: "flex"}}>
|
|
{this.state.photos.map((photos, index) =>
|
|
<div class="card" style={{"width": "18rem", margin: "10px"}}>
|
|
<img src={'/photos/plakat_' + photos.index + '.jpg'} class="card-img-top" />
|
|
<div class="card-body">
|
|
<h5 class="card-title">{photos.name}</h5>
|
|
<a href="#gallery" class="btn btn-primary" onClick={(e) => {e.preventDefault(); this.openGallery(index)}}>Otevřít galerii</a>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
}
|
|
{this.state.page == "#gallery" && this.state.gallery_open !== undefined &&
|
|
<div class="container">
|
|
<Gallery style={{}} photos={photos_gallery} onClick={this.openPhoto}/>
|
|
</div>
|
|
}
|
|
{(this.state.faq || this.state.showRacerModalWindow) &&
|
|
<div class="shadow"></div>}
|
|
{this.state.showRacerModalWindow &&
|
|
<div class="faq" onClick={(e) => this.setState({showRacerModalWindow: false})}>
|
|
<div class="container text" onClick={(e) => e.stopPropagation()}>
|
|
<form id="form" class="container" action="/api/change_racer" onSubmit={(e) => this.onSubmit(e)}>
|
|
<h1>Výměna závodníka</h1>
|
|
<input type="hidden" name="id" value={this.state.racer.id} />
|
|
<div class="mb-3">
|
|
<label for="first_name" class="form-label">Jméno</label>
|
|
<input type="text" class="form-control" id="first_name" name="first_name" value={this.state.racer.first_name} onChange={(e) => this.handleChange(e)} />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="last_name" class="form-label">Přijmení</label>
|
|
<input type="text" class="form-control" id="last_name" name="last_name" value={this.state.racer.last_name} onChange={(e) => this.handleChange(e)}/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label">Emailová adresa (volitelné)</label>
|
|
<input type="email" class="form-control" id="email" name="email" value={this.state.racer.email} onChange={(e) => this.handleChange(e)}/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="date_of_birth" class="form-label">Datum narození</label>
|
|
<input type="date" class="form-control" id="date_of_birth" name="date_of_birth" max={new Date().toJSON().slice(0, 10)} value={this.state.racer.date_of_birth} onChange={(e) => this.handleChange(e)}/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="phone" class="form-label">Telefonní číslo (volitelné)</label>
|
|
<input type="text" class="form-control" id="phone" name="phone" value={this.state.racer.phone} onChange={(e) => this.handleChange(e)}/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="phone" class="form-label">Team (volitelné)</label>
|
|
<input type="text" class="form-control" id="team" name="team" value={this.state.racer.team} onChange={(e) => this.handleChange(e)} />
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Vyměnit závodníka</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
}
|
|
{this.state.faq &&
|
|
<div class="faq" onClick={(e) => this.togglefaq()}>
|
|
<div class="container" onClick={(e) => e.stopPropagation()}>
|
|
<button type="button" class="btn-close" aria-label="Close" onClick={(e) => this.togglefaq()}></button>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Proč je cena startovného taková, jaká je?</h5>
|
|
<p class="card-text">Vetšinu ceny dělá alkohol a nápoje, které se během závodu konzumují. Dostanete přívěšek s panákem, který Vám zůstává a pokud závod doběhnete, dostanete památeční medaili. Zbytek startovného tvoří výherní ceny, dále náklady spojené s organizací závodu. Výroba překážek, časovačů na čekárnách, náramky pro závodníký, tisk plakátů a jejich propagace, atd. Cenu jsme se snažili nastavit co nejnižší, zároveň takovou, abychom byli schopní Alkátor Race uskutečnit.</p>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">K čemu slouží Tokken?</h5>
|
|
<p class="card-text">V kontextu Alkátor Race by se to dalo spíše nazvat kontrolním bodem. Závodník doběhne na určité místo, kde tokken sebere a následně ho zanese na další stanoviště, kde ho předáte kontrolorovi. Tím dáte najevo, že jste běžel trasu správě. Je to pojistka, aby jsi byl podctivý Alkátor a nešvindloval!</p>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Jak funguje Quíz?</h5>
|
|
<p class="card-text">Jedná se o překážku, kde na daném místě vylosujete otázku a běžíte k dalšímu stanovišti ke kontrolorovi. Mezi tím si můžete promyslet jednu z ze tří odpovědí (formát A,B,C). Špatná odpopvěď znamená panáčka. Navíc to má obdobnou funkci, jako Tokken.</p>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Jak funguje Ruleta?</h5>
|
|
<p class="card-text">Na stanovišti na Vás budou čekat panáčky. Obsah může být cokoliv alkoholického i nealkoholického, od dobrot po blééé. Nejlepší na tom je, že podle čichu a barvy nemáte šanci poznat, o co se jedná. Jelikož panáčky jsou uzavřené a dobarvené potravinářským barvivem.</p>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Musím v Alkátor race utíkat nebo se jinak fyzicky namáhat?</h5>
|
|
<p class="card-text">Strategii závodu si určím sám s pomocí mého osobního trenéra. Tudíž se zaměřím hlavně na alkohol a abych dorazil do cíle jako hrdina a ostřílený alko-veterán.</p>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Kolik spoluúčastníků mám očekávat?</h5>
|
|
<p class="card-text">Zatím je kapacita závodu 50 účastníků.</p>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Co když jsem zde svůj dotaz nenašel?</h5>
|
|
<p class="card-text">Tak neváhej a kontaktuj nás na sociálních sítích nebo emailu, rádi pomůžeme.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
{this.state.page == "" &&
|
|
<div>
|
|
<div class="container">
|
|
<img class="logo" src="/public/logo.svg"></img>
|
|
</div>
|
|
<div class="container info text-center">
|
|
<div class="row">
|
|
<h1 class="col val">
|
|
<b>KDY?</b>
|
|
<br />
|
|
5.10.2024
|
|
</h1>
|
|
<h1 class="col val">
|
|
<b>V KOLIK?</b>
|
|
<br />
|
|
10:00
|
|
</h1>
|
|
<h1 class="col val">
|
|
<b>KDE?</b>
|
|
<br />
|
|
Studené
|
|
</h1>
|
|
<h1 class="col val">
|
|
<b>CENA</b>
|
|
<br />
|
|
Od 21.9. 790 kč*
|
|
</h1>
|
|
</div>
|
|
</div>
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="text col-xl-6">
|
|
<p>Vítej člověče! Momentálně se nacházíš na oficiální stránce závodu ALKÁTOR RACE.</p>
|
|
|
|
<p>A co že jsme si to zase vymysleli!? </p>
|
|
|
|
<p>Jedná se o překážkový závod u nás v Orlických horách. Celkem máme připravených kolem 30 překážek a trať měří kolem 4 km. V závodě tě čeká pěkných pár panáků, kdy počty se pohybují od 3-15 kousků podle toho jak dobře si na překážkách povedeš. Dále máme v závodě překážku zvanou "Čekárna" na které si deset minut odpočneš a dáš si oddychové pivínko na nejkrásnějších místech v okolí Studené. Čekárny tě v závodě čekají hned čtyři. </p>
|
|
|
|
<p>Pokud v průběhu závodu dostaneš pocit, že je to nad tvé síly, vždy budeš mít tu možnost přestoupit do kategorie Alkátor Race LIGHT, kde sice ztratíš možnost stanout na stupních vítězů, ale budeš moci plně regulovat svou obtížnost závodu.
|
|
Takže když budeš mít špatný den, nikdo tě nebude do ničeho nutit a ty si užiješ atmosféru akce na plné pecky.</p>
|
|
|
|
<p>Našim smělým alkátorkám nebudeme nic extra zjednodušovat, pouze na čekárnách budou konzumovat poloviční dávky piva(pokud budou chtít).
|
|
Pravděpodobně bude otevřena i kategorie NEAlkátor Race - zaměřena na řidiče, abstinenty a nezletilé.</p>
|
|
|
|
<p>Pro alkátory/rky na stupních vítězů čekají úžasné ceny.</p>
|
|
|
|
<p>Start závodu je v 10:00, ale check-in se zahájením akce je již od 8:30.</p>
|
|
|
|
<p>* Do 20.9 byla cena 690 kč.</p>
|
|
|
|
<p>Rozhodně se podívej na FAQ sekci, zde na stránce, kde najdeš důležité informace týkající se závodu ALKÁTOR RACE.</p>
|
|
<p>Sportem ku chlastu!</p>
|
|
<iframe width="100%" height="500px" src="https://www.youtube.com/embed/INYakiBnVNk?si=Nx9QtADyt_4XI5BC" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="true" />
|
|
</div>
|
|
<div class="col-xl-6">
|
|
<h1 class="text">Výsledky 2. ročníku (Podzim 2024)</h1>
|
|
{this.state.results &&
|
|
<table class="table results">
|
|
<thead>
|
|
<th>Umístění</th>
|
|
<th>Čas</th>
|
|
<th>Kategorie</th>
|
|
<th>Startovní číslo</th>
|
|
</thead>
|
|
<tbody>
|
|
{this.state.results.map(user =>
|
|
<tr>
|
|
<th>{user.order}</th>
|
|
<td>{user.duration}</td>
|
|
<td>{user.alkator_category}</td>
|
|
<td>{user.starting_number}</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
}
|
|
{/*
|
|
<img src="/public/trasa.png" style={{'width': '100%', 'height': 'auto'}}/>
|
|
*/}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="container text text-center">
|
|
<h2>Stále nevíš, jestli se zúčastníš? Přečti si náš <button type="button" class="btn btn-warning" onClick={(e) => this.togglefaq()}>FAQ</button></h2>
|
|
</div>
|
|
<div class="container text text-center">
|
|
<h1>Další ročník bude na jaře 2025.</h1>
|
|
</div>
|
|
<div class="container text-center social">
|
|
<div class="row">
|
|
<a class="col-lg-3" style={{"color": "black"}} href="mailto:info@alkator.cz"><b>info@alkator.cz</b></a>
|
|
<a class="col-lg-3" href="https://www.facebook.com/AlkatorRace"><img height="40px" src="/public/fb_logo.svg" /></a>
|
|
<a class="col-lg-3" href="https://www.youtube.com/@AlkatorRace"><img height="40px" src="/public/youtube_logo.svg"/></a>
|
|
<a class="col-lg-3" href="https://www.instagram.com/alkator_race/"><img height="40px" src="/public/instagram_logo.svg" /></a>
|
|
</div>
|
|
</div>
|
|
<div class="container video">
|
|
<iframe width="100%" height="800px" src="https://www.youtube.com/embed/v3r2IAPdFFQ?si=kkh2_BBJXw56gVaP" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="true"></iframe>
|
|
</div>
|
|
</div>
|
|
}
|
|
<div class="container text">
|
|
<a href="/public/reverz.pdf">Reverz</a><br />
|
|
<a href="/public/ochrana_osobnich_udaju.pdf">Ochrana osobních údajů</a><br />
|
|
<a href="/public/obchodni_podminky_alkator.pdf">Obchodní podmínky závodu alkátor</a><br />
|
|
Název společnosti: Zhoor s.r.o.<br />
|
|
IČ: 212 44 863<br />
|
|
DIČ: Nejsme plátci DPH.<br />
|
|
sídlo: Červenovodská 548, Králíky, 561 69<br />
|
|
Číslo účtu: 131-2219860207/0100<br />
|
|
E-mail: info@alkator.cz<br />
|
|
Tel: +420 728 018 088<br />
|
|
<img src="/public/footer.png" style={{"width":"100%","height":"auto"}} /><br/>
|
|
© Alkátor team 2024
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
const root = document.getElementById("root");
|
|
render(<Main />, root); |