This commit is contained in:
tree 2022-04-11 21:31:12 +02:00
rodič b3db1527b8
revize fe4bfb670b
16 změnil soubory, kde provedl 1321 přidání a 740 odebrání

40
package-lock.json vygenerováno
Zobrazit soubor

@ -22,6 +22,8 @@
"@sveltejs/kit": "next", "@sveltejs/kit": "next",
"autoprefixer": "^10.4.4", "autoprefixer": "^10.4.4",
"postcss": "^8.4.12", "postcss": "^8.4.12",
"prettier": "^2.6.2",
"prettier-plugin-svelte": "^2.7.0",
"svelte": "^3.46.0", "svelte": "^3.46.0",
"svelte-markdown": "^0.2.2", "svelte-markdown": "^0.2.2",
"tailwindcss": "^3.0.23" "tailwindcss": "^3.0.23"
@ -1571,6 +1573,31 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true "dev": true
}, },
"node_modules/prettier": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-svelte": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.7.0.tgz",
"integrity": "sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==",
"dev": true,
"peerDependencies": {
"prettier": "^1.16.4 || ^2.0.0",
"svelte": "^3.2.0"
}
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -2895,6 +2922,19 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true "dev": true
}, },
"prettier": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
"dev": true
},
"prettier-plugin-svelte": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.7.0.tgz",
"integrity": "sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==",
"dev": true,
"requires": {}
},
"queue-microtask": { "queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",

Zobrazit soubor

@ -6,7 +6,9 @@
"build": "svelte-kit build", "build": "svelte-kit build",
"package": "svelte-kit package", "package": "svelte-kit package",
"preview": "svelte-kit preview", "preview": "svelte-kit preview",
"prepare": "svelte-kit sync" "prepare": "svelte-kit sync",
"prettier": "prettier --write --plugin-search-dir=. ./**/*.svelte"
}, },
"devDependencies": { "devDependencies": {
"@faker-js/faker": "^6.1.2", "@faker-js/faker": "^6.1.2",
@ -15,6 +17,8 @@
"@sveltejs/kit": "next", "@sveltejs/kit": "next",
"autoprefixer": "^10.4.4", "autoprefixer": "^10.4.4",
"postcss": "^8.4.12", "postcss": "^8.4.12",
"prettier": "^2.6.2",
"prettier-plugin-svelte": "^2.7.0",
"svelte": "^3.46.0", "svelte": "^3.46.0",
"svelte-markdown": "^0.2.2", "svelte-markdown": "^0.2.2",
"tailwindcss": "^3.0.23" "tailwindcss": "^3.0.23"

Zobrazit soubor

@ -1,112 +1,150 @@
<script> <script>
export let speaker; export let speaker;
export let col = 'speakers'; export let col = "speakers";
export let size = 'normal'; export let size = "normal";
export let customSize = null; export let customSize = null;
import SvelteMarkdown from 'svelte-markdown'; import SvelteMarkdown from "svelte-markdown";
import Link from '$lib/Link.svelte'; import Link from "$lib/Link.svelte";
import { page } from '$app/stores'; import { page } from "$app/stores";
const renderers = { link: Link } const renderers = { link: Link };
let imagesRoot = 'https://spec.utxo.cz/22/photos' let imagesRoot = "https://spec.utxo.cz/22/photos";
/*if ($page.url.hostname === 'localhost') { /*if ($page.url.hostname === 'localhost') {
imagesRoot = 'http://localhost:8000/22/photos' imagesRoot = 'http://localhost:8000/22/photos'
}*/ }*/
const priority = [ 'web:svg', 'web:webp', 'web:png', 'web:jpg', 'twitter:jpg' ] const priority = ["web:svg", "web:webp", "web:png", "web:jpg", "twitter:jpg"];
$: photos = getPhotos(speaker) $: photos = getPhotos(speaker);
$: speakerImg = photos[0] $: speakerImg = photos[0];
$: speakerImgAlt = photos[1] $: speakerImgAlt = photos[1];
function getPhotos (sp) { function getPhotos(sp) {
const output = [] const output = [];
if (speaker.photos && speaker.photos.length > 0) { if (speaker.photos && speaker.photos.length > 0) {
for (const prio of priority) { for (const prio of priority) {
if (speaker.photos.includes(prio)) { if (speaker.photos.includes(prio)) {
const [ ext, format ] = prio.split(':') const [ext, format] = prio.split(":");
const fn = `${imagesRoot}/${col}/${speaker.id}-${ext}.${format}` const fn = `${imagesRoot}/${col}/${speaker.id}-${ext}.${format}`;
if (output[0]) { if (output[0]) {
output.push(fn) output.push(fn);
break break;
} }
output.push(fn) output.push(fn);
} }
} }
} }
return output return output;
} }
if (!speakerImg) { if (!speakerImg) {
speakerImg = '/img/twitter-avatar.png' speakerImg = "/img/twitter-avatar.png";
} }
function getFlagEmoji(countryCode) { function getFlagEmoji(countryCode) {
const codePoints = countryCode const codePoints = countryCode
.toUpperCase() .toUpperCase()
.split('') .split("")
.map(char => 127397 + char.charCodeAt()); .map((char) => 127397 + char.charCodeAt());
return String.fromCodePoint(...codePoints); return String.fromCodePoint(...codePoints);
} }
$: country = speaker.country ? getFlagEmoji(speaker.country) : '' $: country = speaker.country ? getFlagEmoji(speaker.country) : "";
$: currentImg = speakerImg $: currentImg = speakerImg;
function mouseOver () { function mouseOver() {
if (speakerImgAlt) { if (speakerImgAlt) {
$: currentImg = speakerImgAlt $: currentImg = speakerImgAlt;
} }
} }
function mouseLeave () { function mouseLeave() {
$: currentImg = speakerImg $: currentImg = speakerImg;
} }
</script> </script>
{#if size === 'custom'} {#if size === "custom"}
<div class="customSize} text-center pb-4 m-auto"> <div class="customSize} text-center pb-4 m-auto">
<img src={currentImg} class="{customSize} rounded-full m-auto" alt={speaker.name} /> <img
</div> src={currentImg}
class="{customSize} rounded-full m-auto"
alt={speaker.name}
/>
</div>
{/if} {/if}
{#if size === 'big'} {#if size === "big"}
<div class="w-64 text-center pb-4 m-auto"> <div class="w-64 text-center pb-4 m-auto">
<img src={currentImg} class="w-64 rounded-full m-auto shadow-xl" alt={speaker.name} on:mouseover={mouseOver} on:mouseleave={mouseLeave} /> <img
</div> src={currentImg}
class="w-64 rounded-full m-auto shadow-xl"
alt={speaker.name}
on:mouseover={mouseOver}
on:mouseleave={mouseLeave}
/>
</div>
{/if} {/if}
{#if size === 'normal'} {#if size === "normal"}
<div class="w-36 sm:w-44 text-center pb-4"> <div class="w-36 sm:w-44 text-center pb-4">
<a href="/lide?id={speaker.id}" on:mouseover={mouseOver} on:mouseleave={mouseLeave}><img src={currentImg} class="w-36 sm:w-40 rounded-full m-auto shadow-xl" alt={speaker.name} /></a> <a
<div class="mt-4 text-sm text-blue-web uppercase font-bold"><a href="/lide?id={speaker.id}">{speaker.name}</a> {country}</div> href="/lide?id={speaker.id}"
on:mouseover={mouseOver}
on:mouseleave={mouseLeave}
><img
src={currentImg}
class="w-36 sm:w-40 rounded-full m-auto shadow-xl"
alt={speaker.name}
/></a
>
<div class="mt-4 text-sm text-blue-web uppercase font-bold">
<a href="/lide?id={speaker.id}">{speaker.name}</a>
{country}
</div>
{#if speaker.bio || speaker.orgs} {#if speaker.bio || speaker.orgs}
<div class="mt-1 text-xs text-blue-web italic"><SvelteMarkdown source={speaker.bio || speaker.orgs} renderers={renderers}/></div> <div class="mt-1 text-xs text-blue-web italic">
<SvelteMarkdown source={speaker.bio || speaker.orgs} {renderers} />
</div>
{/if} {/if}
</div> </div>
{/if} {/if}
{#if size === 'small'} {#if size === "small"}
<div class="w-16 text-center"> <div class="w-16 text-center">
<img src={currentImg} class="w-16 rounded-full m-auto" alt="{speaker.name}" /> <img src={currentImg} class="w-16 rounded-full m-auto" alt={speaker.name} />
</div> </div>
{/if} {/if}
{#if size === 'semi-small'} {#if size === "semi-small"}
<div class="w-10 text-center"> <div class="w-10 text-center">
<img src={currentImg} class="w-10 rounded-full m-auto shadow-md" alt="{speaker.name}" /> <img
</div> src={currentImg}
class="w-10 rounded-full m-auto shadow-md"
alt={speaker.name}
/>
</div>
{/if} {/if}
{#if size === 'extra-small'} {#if size === "extra-small"}
<div class="w-6 h-6 text-center"> <div class="w-6 h-6 text-center">
<a href="/lide?id={speaker.id}"><img src={currentImg} class="w-6 rounded-full m-auto" alt="{speaker.name}" /></a> <a href="/lide?id={speaker.id}"
</div> ><img
src={currentImg}
class="w-6 rounded-full m-auto"
alt={speaker.name}
/></a
>
</div>
{/if} {/if}
{#if size === 'micro'} {#if size === "micro"}
<div class="w-4 h-4 text-center"> <div class="w-4 h-4 text-center">
<a href="/lide?id={speaker.id}"><img src={currentImg} class="w-4 rounded-full m-auto" alt="{speaker.name}" /></a> <a href="/lide?id={speaker.id}"
</div> ><img
src={currentImg}
class="w-4 rounded-full m-auto"
alt={speaker.name}
/></a
>
</div>
{/if} {/if}

Zobrazit soubor

@ -3,53 +3,70 @@
const e = event; const e = event;
import Avatar from '$lib/Avatar.svelte'; import Avatar from "$lib/Avatar.svelte";
import EventTypeLabel from '$lib/EventTypeLabel.svelte'; import EventTypeLabel from "$lib/EventTypeLabel.svelte";
import { bundle, userData } from '$lib/stores.js'; import { bundle, userData } from "$lib/stores.js";
function speakersMap (arr) { function speakersMap(arr) {
if (!arr) return; if (!arr) return;
return arr.map(sId => { return arr.map((sId) => {
return $bundle.spec.speakers.find(sp => sp.id === sId) return $bundle.spec.speakers.find((sp) => sp.id === sId);
}) });
} }
function trackRender (trackId) { function trackRender(trackId) {
const track = $bundle.spec.tracks.find(t => t.id === trackId) const track = $bundle.spec.tracks.find((t) => t.id === trackId);
return track.shortname || track.name return track.shortname || track.name;
} }
function getParents (e) { function getParents(e) {
return $bundle.spec.events.filter(i => i.parent === e.id) return $bundle.spec.events.filter((i) => i.parent === e.id);
} }
function handleFavorite (el) { function handleFavorite(el) {
const t = el.target.getAttribute('utxo-event-id') const t = el.target.getAttribute("utxo-event-id");
userData.update(data => { userData.update((data) => {
const fe = data.favoriteEvents const fe = data.favoriteEvents;
let output = null let output = null;
if (fe.includes(t)) { if (fe.includes(t)) {
output = Object.assign($userData, { favoriteEvents: fe.filter(f => f !== t) } ) output = Object.assign($userData, {
favoriteEvents: fe.filter((f) => f !== t),
});
} else { } else {
fe.push(t) fe.push(t);
output = Object.assign($userData, { favoriteEvents: fe }) output = Object.assign($userData, { favoriteEvents: fe });
} }
//localStorage.setItem('userData', JSON.stringify(output)) //localStorage.setItem('userData', JSON.stringify(output))
return output return output;
}) });
} }
</script> </script>
<div
<div class="transition-all mb-4 border px-3 py-2 rounded-md shadow {$userData.favoriteEvents.includes(e.id) ? 'bg-yellow-100' : '' }" > class="transition-all mb-4 border px-3 py-2 rounded-md shadow {$userData.favoriteEvents.includes(
<div class="float-right"><i class="fa-star {$userData.favoriteEvents.includes(e.id) ? 'fa-solid' : 'fa-regular'} cursor-pointer" utxo-event-id="{e.id}" on:click={handleFavorite}></i></div> e.id
<div class="text-lg font-semibold"><a href="/udalosti?id={e.id}">{e.name}</a></div> )
? 'bg-yellow-100'
: ''}"
>
<div class="float-right">
<i
class="fa-star {$userData.favoriteEvents.includes(e.id)
? 'fa-solid'
: 'fa-regular'} cursor-pointer"
utxo-event-id={e.id}
on:click={handleFavorite}
/>
</div>
<div class="text-lg font-semibold">
<a href="/udalosti?id={e.id}">{e.name}</a>
</div>
{#if e.speakers && e.speakers.length > 0} {#if e.speakers && e.speakers.length > 0}
<div class="mt-1 mb-2 flex flex-wrap gap-2"> <div class="mt-1 mb-2 flex flex-wrap gap-2">
{#each speakersMap(e.speakers) as s} {#each speakersMap(e.speakers) as s}
<div class="flex gap-1.5"> <div class="flex gap-1.5">
<Avatar speaker={s} size='extra-small' /> <Avatar speaker={s} size="extra-small" />
<div class="m-auto"><a href="/lide?id={s.id}">{s.name}</a></div> <div class="m-auto"><a href="/lide?id={s.id}">{s.name}</a></div>
</div> </div>
{/each} {/each}
@ -65,13 +82,18 @@
<div class="flex flex-wrap gap-2" cellpadding="5"> <div class="flex flex-wrap gap-2" cellpadding="5">
{#each getParents(e) as pe} {#each getParents(e) as pe}
<div class="border rounded py-1.5 px-2.5 bg-gray-100 text-sm"> <div class="border rounded py-1.5 px-2.5 bg-gray-100 text-sm">
<div class="font-bold"><a href="/udalosti?id={pe.id}">{pe.name}</a></div> <div class="font-bold">
<a href="/udalosti?id={pe.id}">{pe.name}</a>
</div>
<div class="mt-1"> <div class="mt-1">
{#if pe.speakers.length === 0} {#if pe.speakers.length === 0}
<div>TBA</div> <div>TBA</div>
{:else} {:else}
{#each speakersMap(pe.speakers) as s} {#each speakersMap(pe.speakers) as s}
<div class="flex gap-1"><Avatar speaker={s} size='micro' /><div><a href="/lide?id={s.id}">{s.name}</a></div></div> <div class="flex gap-1">
<Avatar speaker={s} size="micro" />
<div><a href="/lide?id={s.id}">{s.name}</a></div>
</div>
{/each} {/each}
{/if} {/if}
</div> </div>

Zobrazit soubor

@ -3,19 +3,23 @@
export let size = null; export let size = null;
const config = { const config = {
panel: { title: 'Panelová debata', style: 'bg-custom-red text-white' }, panel: { title: "Panelová debata", style: "bg-custom-red text-white" },
talk: { title: 'Přednáška', style: 'bg-custom-green text-white' }, talk: { title: "Přednáška", style: "bg-custom-green text-white" },
workshop: { title: 'Workshop', style: 'bg-custom-blue text-white' }, workshop: { title: "Workshop", style: "bg-custom-blue text-white" },
other: { title: 'Ostatní', style: 'bg-custom-yellow' }, other: { title: "Ostatní", style: "bg-custom-yellow" },
lightning: { title: 'Lightning talk', style: 'bg-pink-400' }, lightning: { title: "Lightning talk", style: "bg-pink-400" },
} };
const current = config[event.type]; const current = config[event.type];
</script> </script>
<div class="flex { size === 'big' ? 'h-6 text-sm' : 'h-5 text-xs' }"> <div class="flex {size === 'big' ? 'h-6 text-sm' : 'h-5 text-xs'}">
<div class="w-1 rounded-l-sm {current.style}"></div> <div class="w-1 rounded-l-sm {current.style}" />
<div class="{ size === 'big' ? 'px-2 py-0.5' : 'px-1.5 py-0.5' } rounded-r-sm bg-gray-100 uppercase">{current.title}</div> <div
class="{size === 'big'
? 'px-2 py-0.5'
: 'px-1.5 py-0.5'} rounded-r-sm bg-gray-100 uppercase"
>
{current.title}
</div>
</div> </div>

Zobrazit soubor

@ -1,14 +1,18 @@
<script> <script>
import { bundle } from '$lib/stores'; import { bundle } from "$lib/stores";
import SocialButtons from '$lib/SocialButtons.svelte'; import SocialButtons from "$lib/SocialButtons.svelte";
</script> </script>
{#if $bundle} {#if $bundle}
<div class="bg-blue-web-bg text-white"> <div class="bg-blue-web-bg text-white">
<div class="relative mx-auto px-6 pt-10 pb-2 sm:pb-6 max-w-6xl sm:flex pr-4"> <div
class="relative mx-auto px-6 pt-10 pb-2 sm:pb-6 max-w-6xl sm:flex pr-4"
>
<div class="flex-1"> <div class="flex-1">
<div> <div>
<a href="/"><img src="/img/logo-white.svg" alt="UTXO.22" class="w-24" /></a> <a href="/"
><img src="/img/logo-white.svg" alt="UTXO.22" class="w-24" /></a
>
</div> </div>
<div class="mt-4 font-semibold"> <div class="mt-4 font-semibold">
4.-5. červen 2022 @ Gabriel Loci, Praha 4.-5. červen 2022 @ Gabriel Loci, Praha
@ -17,22 +21,43 @@
Otevřená komunitní kryptoměnová konference Otevřená komunitní kryptoměnová konference
</div> </div>
<div class="mt-4"> <div class="mt-4">
<a href="{$bundle.links.docs}" class="hover:text-red-500"><i class="fas fa-book" />&nbsp; Dokumentace</a> <a href={$bundle.links.docs} class="hover:text-red-500"
><i class="fas fa-book" />&nbsp; Dokumentace</a
>
</div> </div>
</div> </div>
<div class="sm:mt-0 mt-6 sm:w-1/3 mr-2"> <div class="sm:mt-0 mt-6 sm:w-1/3 mr-2">
<SocialButtons size="normal" /> <SocialButtons size="normal" />
</div> </div>
</div> </div>
<div class="relative mx-auto px-6 pt-10 pb-6 max-w-6xl text-xs opacity-50 sm:flex"> <div
class="relative mx-auto px-6 pt-10 pb-6 max-w-6xl text-xs opacity-50 sm:flex"
>
<div class="flex-1 mt-2 mb-2"> <div class="flex-1 mt-2 mb-2">
<i class="fas fa-heart text-red-500" /> S láskou organizuje <a href="https://utxo.foundation" class="underline hover:no-underline" target="_blank">UTXO Foundation, z.s.</a> <i class="fas fa-heart text-red-500" /> S láskou organizuje
<a
href="https://utxo.foundation"
class="underline hover:no-underline"
target="_blank">UTXO Foundation, z.s.</a
>
</div> </div>
<div> <div>
<a href="https://github.com/utxo-foundation/utxo22-web" target="_blank"><span class="font-bold">v0.9.1</span></a> | powered by <a href="https://github.com/utxo-foundation/utxo22-web" target="_blank"
<a href="https://svelte.dev/" class="font-bold" target="_blank" ><img src="/img/svelte-logo.svg" class="w-5 inline" alt="Svelte" /> Svelte</a> | ><span class="font-bold">v0.9.1</span></a
grafický návrh <a href="https://www.ppmedia.cz/" target="_blank"><img src="/img/pen-production-logo.svg" class="w-28 inline-block pb-2 ml-1" alt="Pen&Production" /></a> >
| powered by
<a href="https://svelte.dev/" class="font-bold" target="_blank"
><img src="/img/svelte-logo.svg" class="w-5 inline" alt="Svelte" /> Svelte</a
>
| grafický návrh
<a href="https://www.ppmedia.cz/" target="_blank"
><img
src="/img/pen-production-logo.svg"
class="w-28 inline-block pb-2 ml-1"
alt="Pen&Production"
/></a
>
</div>
</div> </div>
</div> </div>
</div>
{/if} {/if}

Zobrazit soubor

@ -1,6 +1,8 @@
<script> <script>
export let href = '' export let href = "";
export let title = undefined export let title = undefined;
</script> </script>
<a {href} {title} class="underline hover:no-underline" target="_blank"><slot></slot></a> <a {href} {title} class="underline hover:no-underline" target="_blank"
><slot /></a
>

Zobrazit soubor

@ -1,34 +1,42 @@
<script> <script>
export let size = 'small'; export let size = "small";
import { bundle } from '$lib/stores'; import { bundle } from "$lib/stores";
import { page } from '$app/stores'; import { page } from "$app/stores";
const socials = [ const socials = [
{ link: 'twitter', ico: 'fa-brands fa-twitter', name: 'Twitter' }, { link: "twitter", ico: "fa-brands fa-twitter", name: "Twitter" },
{ link: 'instagram', ico: 'fab fa-instagram', name: 'Instagram' }, { link: "instagram", ico: "fab fa-instagram", name: "Instagram" },
{ link: 'fbevent', ico: 'fab fa-facebook', name: 'Facebook' }, { link: "fbevent", ico: "fab fa-facebook", name: "Facebook" },
{ link: 'substack', ico: 'fa-solid fa-envelope', name: 'Newsletter' }, { link: "substack", ico: "fa-solid fa-envelope", name: "Newsletter" },
{ link: 'discord', ico: 'fab fa-discord', name: 'Discord' }, { link: "discord", ico: "fab fa-discord", name: "Discord" },
{ link: 'telegram', ico: 'fab fa-telegram', name: 'Telegram' }, { link: "telegram", ico: "fab fa-telegram", name: "Telegram" },
] ];
</script> </script>
{#if $bundle} {#if $bundle}
{#if size === 'small'} {#if size === "small"}
<div class="flex block space-x-2 m-auto w-full justify-end"> <div class="flex block space-x-2 m-auto w-full justify-end">
{#each socials as soc} {#each socials as soc}
<a href="{$bundle.links[soc.link]}" class="w-6 h-6 bg-white rounded-full hover:bg-utxo-gradient hover:text-white" target="_blank"> <a
<i class="{soc.ico}" /> href={$bundle.links[soc.link]}
class="w-6 h-6 bg-white rounded-full hover:bg-utxo-gradient hover:text-white"
target="_blank"
>
<i class={soc.ico} />
</a> </a>
{/each} {/each}
</div> </div>
{/if} {/if}
{#if size === 'normal'} {#if size === "normal"}
<div class="w-auto"> <div class="w-auto">
<div class="sm:flex flex-wrap gap-3 justify-end"> <div class="sm:flex flex-wrap gap-3 justify-end">
{#each socials as soc} {#each socials as soc}
<div class="mr-4 sm:mr-0 inline-block sm:block hover:text-red-500"><a href="{$bundle.links[soc.link]}" class="block flex" target="_blank"><i class="{soc.ico} mr-2 my-auto" />{soc.name}</a></div> <div class="mr-4 sm:mr-0 inline-block sm:block hover:text-red-500">
<a href={$bundle.links[soc.link]} class="block flex" target="_blank"
><i class="{soc.ico} mr-2 my-auto" />{soc.name}</a
>
</div>
{/each} {/each}
</div> </div>
</div> </div>

Zobrazit soubor

@ -1,31 +1,61 @@
<script> <script>
import { page } from '$app/stores'; import { page } from "$app/stores";
import { bundle, userData, userDataLocal } from '$lib/stores'; import { bundle, userData, userDataLocal } from "$lib/stores";
import SocialButtons from '$lib/SocialButtons.svelte'; import SocialButtons from "$lib/SocialButtons.svelte";
function logoClick () { function logoClick() {
userData.update(ud => { userData.update((ud) => {
ud.hpTrack = 'top' ud.hpTrack = "top";
return ud return ud;
}) });
} }
</script> </script>
<header class="relative" style="background-color: #32375C;"> <header class="relative" style="background-color: #32375C;">
<!-- <li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li> --> <!-- <li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li> -->
<nav class="relative mx-auto lg:px-6 px-4 pt-4 sm:pt-6 sm:pb-6 pb-2 max-w-6xl text-center"> <nav
class="relative mx-auto lg:px-6 px-4 pt-4 sm:pt-6 sm:pb-6 pb-2 max-w-6xl text-center"
>
<div class=""> <div class="">
<div class="lg:flex lg:flex-wrap lg:space-x-10"> <div class="lg:flex lg:flex-wrap lg:space-x-10">
<div class="block justify-start lg:flex-1 my-auto text-center pb-3 lg:pb-0"> <div
<div class="w-36 lg:w-24 inline-block lg:block"><a href="/" on:click={logoClick}><img src="/img/logo-white.svg" class="w-full" alt="UTXO.22" /></a></div> class="block justify-start lg:flex-1 my-auto text-center pb-3 lg:pb-0"
>
<div class="w-36 lg:w-24 inline-block lg:block">
<a href="/" on:click={logoClick}
><img src="/img/logo-white.svg" class="w-full" alt="UTXO.22" /></a
>
</div>
</div> </div>
<div class="flex lg:space-x-10 uppercase text-sm font-bold text-white"> <div class="flex lg:space-x-10 uppercase text-sm font-bold text-white">
<a sveltekit:prefetch href="/" class="lg:w-auto w-1/3 m-auto hover:text-[#E16A61]" class:text-blue-400={$page.url.pathname === '/'}>O konferenci</a> <a
<a sveltekit:prefetch href="/program" class="lg:w-auto w-1/3 m-auto hover:text-[#E16A61]" class:text-blue-400={$page.url.pathname === '/program'}>Program</a> sveltekit:prefetch
<a sveltekit:prefetch href="/vstupenky" class="lg:w-auto w-1/3 m-auto border-solid border border-[#E16A61] rounded-full {$page.url.pathname === '/vstupenky' ? 'border-0 bg-utxo-gradient m-px' : 'hover:border-0 hover:bg-utxo-gradient hover:p-px' }"><div class="py-2 px-1 lg:px-8">Vstupenky{#if $userDataLocal.tickets && $userDataLocal.tickets.length > 0}&nbsp;({$userDataLocal.tickets.length}){/if}</div></a> href="/"
class="lg:w-auto w-1/3 m-auto hover:text-[#E16A61]"
class:text-blue-400={$page.url.pathname === "/"}>O konferenci</a
>
<a
sveltekit:prefetch
href="/program"
class="lg:w-auto w-1/3 m-auto hover:text-[#E16A61]"
class:text-blue-400={$page.url.pathname === "/program"}>Program</a
>
<a
sveltekit:prefetch
href="/vstupenky"
class="lg:w-auto w-1/3 m-auto border-solid border border-[#E16A61] rounded-full {$page
.url.pathname === '/vstupenky'
? 'border-0 bg-utxo-gradient m-px'
: 'hover:border-0 hover:bg-utxo-gradient hover:p-px'}"
><div class="py-2 px-1 lg:px-8">
Vstupenky{#if $userDataLocal.tickets && $userDataLocal.tickets.length > 0}&nbsp;({$userDataLocal
.tickets.length}){/if}
</div></a
>
</div> </div>
<div class="hidden lg:block my-auto lg:flex-1 lg:pt-0 pt-4 lg:justify-end justify-center"> <div
class="hidden lg:block my-auto lg:flex-1 lg:pt-0 pt-4 lg:justify-end justify-center"
>
<SocialButtons /> <SocialButtons />
</div> </div>
</div> </div>

Zobrazit soubor

@ -1,44 +1,41 @@
<script> <script>
import Header from '$lib/header/Header.svelte'; import Header from "$lib/header/Header.svelte";
import Footer from '$lib/Footer.svelte'; import Footer from "$lib/Footer.svelte";
import '../app.css'; import "../app.css";
import api from '$lib/api.js'; import api from "$lib/api.js";
import { page } from '$app/stores'; import { page } from "$app/stores";
import { userData, userDataLocal, apiStatus } from '$lib/stores'; import { userData, userDataLocal, apiStatus } from "$lib/stores";
import { loadOrders, loadApiStatus } from '$lib/orders'; import { loadOrders, loadApiStatus } from "$lib/orders";
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from "svelte";
let bundle = null let bundle = null;
let uds = null let uds = null;
onMount(async () => { onMount(async () => {
bundle = await api.loadBundle($page.url.hostname === 'localhost') bundle = await api.loadBundle($page.url.hostname === "localhost");
const userDataLS = localStorage.getItem('userData') const userDataLS = localStorage.getItem("userData");
if (userDataLS) { if (userDataLS) {
userData.set(JSON.parse(userDataLS)) userData.set(JSON.parse(userDataLS));
} }
uds = userData.subscribe(ud => { uds = userData.subscribe((ud) => {
localStorage.setItem('userData', JSON.stringify(ud)) localStorage.setItem("userData", JSON.stringify(ud));
}) });
await loadApiStatus() await loadApiStatus();
await loadOrders($userData) await loadOrders($userData);
}) });
onDestroy(() => { onDestroy(() => {
//userData.unsubscribe(uds) //userData.unsubscribe(uds)
}) });
// load orders // load orders
</script> </script>
{#if bundle} {#if bundle}
<div class="layout min-h-screen bg-gray-900"> <div class="layout min-h-screen bg-gray-900">
<div class="inset-0 bg-white"> <div class="inset-0 bg-white">
<Header /> <Header />
@ -47,7 +44,7 @@
</main> </main>
</div> </div>
<Footer /> <Footer />
</div> </div>
{/if} {/if}
<style> <style>

Zobrazit soubor

@ -7,11 +7,41 @@
<section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web"> <section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web">
<div class=""> <div class="">
<div class="text-3xl lg:text-4xl text-center">🎉 Gratulujeme k nákupu vstupenek!</div> <div class="text-3xl lg:text-4xl text-center">
<div class="mt-4 text-lg text-center">Najdeš je v mailu nebo na stránce <a href="/vstupenky" class="underline hover:no-underline">vstupenky</a></div> 🎉 Gratulujeme k nákupu vstupenek!
<div class="flex mt-6"><img class="m-auto w-auto lg:max-w-3xl" src="/gifs/funny-celebrate-{Math.round((Math.random() * (9 - 1) + 1))}.gif" /></div> </div>
<div class="mt-10 text-xl text-center mt-6">Co teď? Poděl se o tu radost s ostatními <a href="https://twitter.com/intent/tweet?text=M%C3%A1m%20l%C3%ADstek%20na%20%40utxoprague%20%F0%9F%8E%89%20Poj%C4%8F%20taky%21%0A%20%23utxo22" class="underline hover:no-underline" target="_blank">tweetem</a></div> <div class="mt-4 text-lg text-center">
<div class="text-xl text-center mt-6">Prohlédni si <a href="/program" class="underline hover:no-underline">aktuální program</a></div> Najdeš je v mailu nebo na stránce <a
<div class="text-xl text-center mt-6">Sleduj novinky na <a href="https://twitter.com/utxoprague" class="underline hover:no-underline" target="_blank">Twitteru</a>&nbsp;<i class="fa-brands fa-twitter"></i></div> href="/vstupenky"
class="underline hover:no-underline">vstupenky</a
>
</div>
<div class="flex mt-6">
<img
class="m-auto w-auto lg:max-w-3xl"
src="/gifs/funny-celebrate-{Math.round(
Math.random() * (9 - 1) + 1
)}.gif"
/>
</div>
<div class="mt-10 text-xl text-center mt-6">
Co teď? Poděl se o tu radost s ostatními <a
href="https://twitter.com/intent/tweet?text=M%C3%A1m%20l%C3%ADstek%20na%20%40utxoprague%20%F0%9F%8E%89%20Poj%C4%8F%20taky%21%0A%20%23utxo22"
class="underline hover:no-underline"
target="_blank">tweetem</a
>
</div>
<div class="text-xl text-center mt-6">
Prohlédni si <a href="/program" class="underline hover:no-underline"
>aktuální program</a
>
</div>
<div class="text-xl text-center mt-6">
Sleduj novinky na <a
href="https://twitter.com/utxoprague"
class="underline hover:no-underline"
target="_blank">Twitteru</a
>&nbsp;<i class="fa-brands fa-twitter" />
</div>
</div> </div>
</section> </section>

Zobrazit soubor

@ -3,59 +3,92 @@
</script> </script>
<script> <script>
import { bundle, userData } from '$lib/stores.js'; import { bundle, userData } from "$lib/stores.js";
import Avatar from '$lib/Avatar.svelte'; import Avatar from "$lib/Avatar.svelte";
import SvelteMarkdown from 'svelte-markdown'; import SvelteMarkdown from "svelte-markdown";
import Link from '$lib/Link.svelte'; import Link from "$lib/Link.svelte";
const renderers = { link: Link } const renderers = { link: Link };
let onlyLead = true let onlyLead = true;
let onlyLeadPreview = false let onlyLeadPreview = false;
$: currentBundle = $bundle; $: currentBundle = $bundle;
$: leadSpeakersCount = currentBundle ? currentBundle.spec.speakers.filter(s => !!s.lead).length : 0 $: leadSpeakersCount = currentBundle
$: tracks = currentBundle ? [{ name: 'Hlavní přednášející ('+leadSpeakersCount+')', id: 'top' }, { name: 'Vše', id: null }].concat(currentBundle.spec.tracks) : null ? currentBundle.spec.speakers.filter((s) => !!s.lead).length
: 0;
$: tracks = currentBundle
? [
{ name: "Hlavní přednášející (" + leadSpeakersCount + ")", id: "top" },
{ name: "Vše", id: null },
].concat(currentBundle.spec.tracks)
: null;
function changeTrack (tId) { function changeTrack(tId) {
return function () { return function () {
userData.update(ud => { ud.hpTrack = tId; return ud; }) userData.update((ud) => {
onlyLead = !tId ud.hpTrack = tId;
} return ud;
});
onlyLead = !tId;
};
} }
function handleShowFull () { function handleShowFull() {
userData.update(ud => { ud.hpTrack = null; return ud }) userData.update((ud) => {
ud.hpTrack = null;
return ud;
});
} }
</script> </script>
<svelte:head> <svelte:head>
<title>UTXO.22 - 4-5. červen 2022 {$bundle ? '- '+$bundle.description : ''}</title> <title
>UTXO.22 - 4-5. červen 2022 {$bundle
? "- " + $bundle.description
: ""}</title
>
</svelte:head> </svelte:head>
<section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl"> <section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl">
{#if $bundle} {#if $bundle}
<div class="flex flex-wrap gap-1.5 sm:gap-3 text-xs uppercase font-bold text-blue-web justify-left"> <div
class="flex flex-wrap gap-1.5 sm:gap-3 text-xs uppercase font-bold text-blue-web justify-left"
>
{#each tracks as track} {#each tracks as track}
<div class="py-1.5 sm:py-2 px-2.5 sm:px-8 rounded-full shadow border border-solid {$userData.hpTrack === track.id ? 'bg-utxo-gradient border-0 text-white' : 'border-blue-web hover:bg-blue-web hover:text-white hover:border-transparent cursor-pointer'}" on:click={changeTrack(track.id)}>{track.shortname || track.name} {#if !track.id}({$bundle.spec.speakers.length}){/if}</div> <div
class="py-1.5 sm:py-2 px-2.5 sm:px-8 rounded-full shadow border border-solid {$userData.hpTrack ===
track.id
? 'bg-utxo-gradient border-0 text-white'
: 'border-blue-web hover:bg-blue-web hover:text-white hover:border-transparent cursor-pointer'}"
on:click={changeTrack(track.id)}
>
{track.shortname || track.name}
{#if !track.id}({$bundle.spec.speakers.length}){/if}
</div>
{/each} {/each}
</div> </div>
<div class="flex flex-wrap gap-6 mt-6 sm:mt-14 justify-center"> <div class="flex flex-wrap gap-6 mt-6 sm:mt-14 justify-center">
{#each $bundle.spec.speakers as speaker} {#each $bundle.spec.speakers as speaker}
{#if ($userData.hpTrack === 'top' && speaker.lead === true) || $userData.hpTrack !== 'top'} {#if ($userData.hpTrack === "top" && speaker.lead === true) || $userData.hpTrack !== "top"}
{#if (!$userData.hpTrack || speaker.tracks.includes($userData.hpTrack)) || $userData.hpTrack === 'top'} {#if !$userData.hpTrack || speaker.tracks.includes($userData.hpTrack) || $userData.hpTrack === "top"}
<Avatar speaker={speaker} /> <Avatar {speaker} />
{/if} {/if}
{/if} {/if}
{/each} {/each}
</div> </div>
{#if $userData.hpTrack === 'top'} {#if $userData.hpTrack === "top"}
<div class="relative cursor-pointer mb-10"> <div class="relative cursor-pointer mb-10">
<div class="absolute inset-0 bg-gradient-to-b from-transparent to-white flex" on:click={handleShowFull}></div> <div
class="absolute inset-0 bg-gradient-to-b from-transparent to-white flex"
on:click={handleShowFull}
/>
<div class="flex flex-wrap gap-3 mt-10 justify-center"> <div class="flex flex-wrap gap-3 mt-10 justify-center">
{#each $bundle.spec.speakers.filter(s => !s.lead).sort(() => .5 - Math.random()).slice(0,27) as speaker} {#each $bundle.spec.speakers
<Avatar speaker={speaker} size="small" /> .filter((s) => !s.lead)
.sort(() => 0.5 - Math.random())
.slice(0, 27) as speaker}
<Avatar {speaker} size="small" />
{/each} {/each}
</div> </div>
</div> </div>
@ -67,24 +100,57 @@
<div class="relative mx-auto py-6 px-6 max-w-6xl"> <div class="relative mx-auto py-6 px-6 max-w-6xl">
<div class="py-6 md:py-10 md:flex gap-12"> <div class="py-6 md:py-10 md:flex gap-12">
<div class="block flex-1"> <div class="block flex-1">
<img src="/photos/gabriel-loci.jpeg" class="flex rounded-xl shadow-xl" alt="Gabriel Loci" /> <img
src="/photos/gabriel-loci.jpeg"
class="flex rounded-xl shadow-xl"
alt="Gabriel Loci"
/>
<div class="flex mt-3 gap-3"> <div class="flex mt-3 gap-3">
<div class="w-1/2"><img src="/photos/rajska-zahrada.jpeg" class="rounded-lg shadow-lg" alt="Gabriel Loci - Rajská zahrada" /></div> <div class="w-1/2">
<div class="w-1/2"><img src="/photos/knihovna.jpeg" class="rounded-lg shadow-lg" alt="Gabriel Loci - Knihovna" /></div> <img
src="/photos/rajska-zahrada.jpeg"
class="rounded-lg shadow-lg"
alt="Gabriel Loci - Rajská zahrada"
/>
</div>
<div class="w-1/2">
<img
src="/photos/knihovna.jpeg"
class="rounded-lg shadow-lg"
alt="Gabriel Loci - Knihovna"
/>
</div>
</div> </div>
</div> </div>
<div class="text-white md:w-1/2 md:pt-0 pt-6"> <div class="text-white md:w-1/2 md:pt-0 pt-6">
<div class="uppercase">Místo</div> <div class="uppercase">Místo</div>
<div class="uppercase mt-3 text-4xl font-bold">Gabriel Loci</div> <div class="uppercase mt-3 text-4xl font-bold">Gabriel Loci</div>
<div class="mt-4 font-bold"> <div class="mt-4 font-bold">
Holečkova 106/10, 150 00 Praha 5 - Smíchov 🇨🇿<br/><span class="font-normal"><a href="https://goo.gl/maps/u1aY4RxXMgcm889V7" class="underline hover:no-underline" target="_blank">Google Maps</a>, <a href="https://mapy.cz/s/cuvetubafo" class="underline hover:no-underline" target="_blank">Mapy.cz</a></span> Holečkova 106/10, 150 00 Praha 5 - Smíchov 🇨🇿<br /><span
class="font-normal"
><a
href="https://goo.gl/maps/u1aY4RxXMgcm889V7"
class="underline hover:no-underline"
target="_blank">Google Maps</a
>,
<a
href="https://mapy.cz/s/cuvetubafo"
class="underline hover:no-underline"
target="_blank">Mapy.cz</a
></span
>
</div> </div>
<div class="mt-4"> <div class="mt-4">
Benediktinky. Kulturní památka. Česká Pošta. Poštovní muzeum. Benediktinky. Kulturní památka. Česká Pošta. Poštovní muzeum. Na první
Na první pohled nesouvisející názvy, které jsou ale neodmyslitelnou součástí unikátního komplexu Gabriel Loci na pražském Smíchově doslova pár minut od centra. pohled nesouvisející názvy, které jsou ale neodmyslitelnou součástí
Na konci 19.století v klášteře sídlily řeholnice, ženské opatství řádu benediktinek beuronské kongregace. Později prostory spravovalo československé Ministerstvo pošt a telegrafů. unikátního komplexu Gabriel Loci na pražském Smíchově doslova pár
Dnes komplex využívají hlavně natáčecí studia jako HBO, Netflix nebo i ČT. No a my! 💪<br/><br/> minut od centra. Na konci 19.století v klášteře sídlily řeholnice,
Zažijte mysteriózní atmosféru komplexu na 1.ročníku konference UTXO.22. Část after-party si užijete doslova v pitevně ze seriálu Devadesátky 👌😀 ženské opatství řádu benediktinek beuronské kongregace. Později
prostory spravovalo československé Ministerstvo pošt a telegrafů. Dnes
komplex využívají hlavně natáčecí studia jako HBO, Netflix nebo i ČT.
No a my! 💪<br /><br />
Zažijte mysteriózní atmosféru komplexu na 1.ročníku konference UTXO.22.
Část after-party si užijete doslova v pitevně ze seriálu Devadesátky 👌😀
</div> </div>
</div> </div>
</div> </div>
@ -96,42 +162,81 @@ Zažijte mysteriózní atmosféru komplexu na 1.ročníku konference UTXO.22. Č
<div class="text-2xl uppercase font-bold">Partneři</div> <div class="text-2xl uppercase font-bold">Partneři</div>
<div class="mt-6">Sponzoři</div> <div class="mt-6">Sponzoři</div>
<div class="mt-6 flex flex-wrap gap-8 justify-left"> <div class="mt-6 flex flex-wrap gap-8 justify-left">
{#each $bundle.spec.partners.filter(p => p.type === 'sponsor') as p} {#each $bundle.spec.partners.filter((p) => p.type === "sponsor") as p}
<div class="w-28"> <div class="w-28">
<a href={p.web.url} target="_blank"><Avatar speaker={p} col="partners" size="custom" customSize="w-24 shadow-xl" /></a> <a href={p.web.url} target="_blank"
><Avatar
speaker={p}
col="partners"
size="custom"
customSize="w-24 shadow-xl"
/></a
>
<div class="text-center text-sm uppercase font-bold">{p.name}</div> <div class="text-center text-sm uppercase font-bold">{p.name}</div>
</div> </div>
{/each} {/each}
</div> </div>
<div class="mt-10">Komunity</div> <div class="mt-10">Komunity</div>
<div class="mt-6 flex flex-wrap gap-6 justify-left"> <div class="mt-6 flex flex-wrap gap-6 justify-left">
{#each $bundle.spec.partners.filter(p => p.type === 'community') as p} {#each $bundle.spec.partners.filter((p) => p.type === "community") as p}
<div><a href={p.web ? p.web.url : (p.twitter ? `https://twitter.com/${p.twitter}` : '')} target="_blank"><Avatar speaker={p} col="partners" size="custom" customSize="w-20 shadow-lg" /></a></div> <div>
<a
href={p.web
? p.web.url
: p.twitter
? `https://twitter.com/${p.twitter}`
: ""}
target="_blank"
><Avatar
speaker={p}
col="partners"
size="custom"
customSize="w-20 shadow-lg"
/></a
>
</div>
{/each} {/each}
</div> </div>
<div class="mt-10">Mediální partneři</div> <div class="mt-10">Mediální partneři</div>
<div class="mt-6 flex flex-wrap gap-4 justify-left"> <div class="mt-6 flex flex-wrap gap-4 justify-left">
{#each $bundle.spec.partners.filter(p => p.type === 'medium') as p} {#each $bundle.spec.partners.filter((p) => p.type === "medium") as p}
<div><a href={p.web ? p.web.url : (p.twitter ? `https://twitter.com/${p.twitter}` : '')} target="_blank"><Avatar speaker={p} col="partners" size="custom" customSize="w-16 shadow-lg" /></a></div> <div>
<a
href={p.web
? p.web.url
: p.twitter
? `https://twitter.com/${p.twitter}`
: ""}
target="_blank"
><Avatar
speaker={p}
col="partners"
size="custom"
customSize="w-16 shadow-lg"
/></a
>
</div>
{/each} {/each}
</div> </div>
</div> </div>
</section> </section>
{#if $bundle} {#if $bundle}
<section class="relative mx-auto py-10 px-6 max-w-6xl"> <section class="relative mx-auto py-10 px-6 max-w-6xl">
<div class="text-blue-web"> <div class="text-blue-web">
<h2 class="uppercase pt-5" id="faq">Často kladené dotazy (FAQ)</h2> <h2 class="uppercase pt-5" id="faq">Často kladené dotazy (FAQ)</h2>
<div class="md:columns-2 columns-1 mt-8 h-auto"> <div class="md:columns-2 columns-1 mt-8 h-auto">
{#each $bundle.spec.faqs as item} {#each $bundle.spec.faqs as item}
<div class="mb-5 break-inside-avoid-column bg-blue-100/60 rounded-xl px-8 py-6 text-left transition-all box-shadow-light overflow-visible"> <div
class="mb-5 break-inside-avoid-column bg-blue-100/60 rounded-xl px-8 py-6 text-left transition-all box-shadow-light overflow-visible"
>
<div class="mb-4 font-bold">{item.question}</div> <div class="mb-4 font-bold">{item.question}</div>
<SvelteMarkdown source={item.answer} renderers={renderers} /> <SvelteMarkdown source={item.answer} {renderers} />
</div> </div>
{/each} {/each}
</div> </div>
</div> </div>
</section> </section>
{/if} {/if}
<style> <style>

Zobrazit soubor

@ -3,47 +3,49 @@
</script> </script>
<script> <script>
import { goto } from "$app/navigation";
import Avatar from "$lib/Avatar.svelte";
import Event from "$lib/Event.svelte";
import { onMount, beforeUpdate } from "svelte";
import { page } from "$app/stores";
import { bundle } from "$lib/stores.js";
import SvelteMarkdown from "svelte-markdown";
import Link from "$lib/Link.svelte";
const renderers = { link: Link };
import { goto } from '$app/navigation'; $: id = getId($page.url.search);
import Avatar from '$lib/Avatar.svelte'; $: s = $bundle ? $bundle.spec.speakers.find((s) => s.id === id) : null;
import Event from '$lib/Event.svelte'; $: events = s
import { onMount, beforeUpdate } from 'svelte'; ? $bundle.spec.events.filter(
import { page } from '$app/stores'; (ev) => ev.speakers && ev.speakers.includes(s.id)
import { bundle } from '$lib/stores.js'; )
import SvelteMarkdown from 'svelte-markdown'; : [];
import Link from '$lib/Link.svelte';
const renderers = { link: Link }
$: id = getId($page.url.search) function getId(search) {
$: s = $bundle ? $bundle.spec.speakers.find(s => s.id === id) : null const searchParams = new URLSearchParams(search);
$: events = s ? $bundle.spec.events.filter(ev => ev.speakers && ev.speakers.includes(s.id)) : [] const cid = searchParams.get("id");
if (!$bundle.spec.speakers.find((s) => s.id === cid)) {
function getId (search) { goto("/");
const searchParams = new URLSearchParams(search)
const cid = searchParams.get('id')
if (!$bundle.spec.speakers.find(s => s.id === cid)) {
goto('/')
} }
return cid return cid;
} }
function trackRender (trackId) { function trackRender(trackId) {
const track = $bundle.spec.tracks.find(t => t.id === trackId) const track = $bundle.spec.tracks.find((t) => t.id === trackId);
return track.shortname || track.name return track.shortname || track.name;
} }
function getFlagEmoji(countryCode) { function getFlagEmoji(countryCode) {
const codePoints = countryCode const codePoints = countryCode
.toUpperCase() .toUpperCase()
.split('') .split("")
.map(char => 127397 + char.charCodeAt()); .map((char) => 127397 + char.charCodeAt());
return String.fromCodePoint(...codePoints); return String.fromCodePoint(...codePoints);
} }
</script> </script>
<svelte:head> <svelte:head>
<title>{s ? s.name : ''} | Lidé | {$bundle ? $bundle.name : 'UTXO.22'}</title> <title>{s ? s.name : ""} | Lidé | {$bundle ? $bundle.name : "UTXO.22"}</title>
</svelte:head> </svelte:head>
<section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web"> <section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web">
@ -54,33 +56,58 @@
<div class="mb-4 text-md uppercase">Přednášející</div> <div class="mb-4 text-md uppercase">Přednášející</div>
<h1 class="text-2xl font-bold">{s.name} {getFlagEmoji(s.country)}</h1> <h1 class="text-2xl font-bold">{s.name} {getFlagEmoji(s.country)}</h1>
{#if s.nickname} {#if s.nickname}
<div class="mt-1"><span class="text-xs">aka</span> <span class="font-bold">{s.nickname}</span></div> <div class="mt-1">
<span class="text-xs">aka</span>
<span class="font-bold">{s.nickname}</span>
</div>
{/if} {/if}
{#if s.bio} {#if s.bio}
<div class="mt-4 text-blue-web italic"><SvelteMarkdown source={s.bio} renderers={renderers} /></div> <div class="mt-4 text-blue-web italic">
<SvelteMarkdown source={s.bio} {renderers} />
</div>
{/if} {/if}
{#if s.orgs} {#if s.orgs}
<div class="mt-4 text-blue-web links"><SvelteMarkdown source={s.orgs} renderers={renderers} /></div> <div class="mt-4 text-blue-web links">
<SvelteMarkdown source={s.orgs} {renderers} />
</div>
{/if} {/if}
<div class="mt-4">Sekce: {s.tracks.map(t => trackRender(t)).join(', ')}</div> <div class="mt-4">
Sekce: {s.tracks.map((t) => trackRender(t)).join(", ")}
</div>
{#if s.twitter} {#if s.twitter}
<div class="mt-2">Twitter: <a href="https://twitter.com/{s.twitter}" target="_blank" class="font-bold">@{s.twitter}</a></div> <div class="mt-2">
Twitter: <a
href="https://twitter.com/{s.twitter}"
target="_blank"
class="font-bold">@{s.twitter}</a
>
</div>
{/if} {/if}
{#if s.linkedin} {#if s.linkedin}
<div class="mt-2">LinkedIn: <a href="https://linkedin.com/in/{s.twitter}" target="_blank" class="font-bold">@{s.linkedin}</a></div> <div class="mt-2">
LinkedIn: <a
href="https://linkedin.com/in/{s.twitter}"
target="_blank"
class="font-bold">@{s.linkedin}</a
>
</div>
{/if} {/if}
{#if s.web && s.web.url} {#if s.web && s.web.url}
<div class="mt-2">Web: <a href="{s.web.url}" target="_blank" class="font-bold">{s.web.name || s.web.url.replace(/^https?:\/\//, '')}</a></div> <div class="mt-2">
Web: <a href={s.web.url} target="_blank" class="font-bold"
>{s.web.name || s.web.url.replace(/^https?:\/\//, "")}</a
>
</div>
{/if} {/if}
</div> </div>
</div> </div>
{#if s.desc} {#if s.desc}
<div class="mt-6"> <div class="mt-6">
<SvelteMarkdown source={s.desc} renderers={renderers} /> <SvelteMarkdown source={s.desc} {renderers} />
</div> </div>
{/if} {/if}
<div class="mt-6"> <div class="mt-6">
<h2 class="uppercase mb-4 text-md">Události ({ events.length })</h2> <h2 class="uppercase mb-4 text-md">Události ({events.length})</h2>
<div> <div>
{#if events.length > 0} {#if events.length > 0}
{#each events as e} {#each events as e}

Zobrazit soubor

@ -3,22 +3,23 @@
</script> </script>
<script> <script>
import Event from '$lib/Event.svelte'; import Event from "$lib/Event.svelte";
import { bundle, userData } from '$lib/stores.js'; import { bundle, userData } from "$lib/stores.js";
</script> </script>
<svelte:head> <svelte:head>
<title>Program | UTXO.22</title> <title>Program | UTXO.22</title>
</svelte:head> </svelte:head>
<section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web"> <section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web">
<h1 class="uppercase text-2xl font-bold">Program</h1> <h1 class="uppercase text-2xl font-bold">Program</h1>
<div class="mt-2 text-sm">Program stále připravujeme. Jeho konečná podoba bude zveřejněna pár týdnů před konferencí.</div> <div class="mt-2 text-sm">
Program stále připravujeme. Jeho konečná podoba bude zveřejněna pár týdnů
před konferencí.
</div>
<div class="mt-6"> <div class="mt-6">
{#each $bundle.spec.events.filter(e => !e.parent) as e} {#each $bundle.spec.events.filter((e) => !e.parent) as e}
<Event event={e} /> <Event event={e} />
{/each} {/each}
</div> </div>

Zobrazit soubor

@ -3,45 +3,46 @@
</script> </script>
<script> <script>
import { page } from '$app/stores'; import { page } from "$app/stores";
import { goto } from '$app/navigation'; import { goto } from "$app/navigation";
import { onMount } from 'svelte'; import { onMount } from "svelte";
import { bundle, userData } from '$lib/stores.js'; import { bundle, userData } from "$lib/stores.js";
import EventTypeLabel from '$lib/EventTypeLabel.svelte'; import EventTypeLabel from "$lib/EventTypeLabel.svelte";
import Avatar from '$lib/Avatar.svelte'; import Avatar from "$lib/Avatar.svelte";
import SvelteMarkdown from 'svelte-markdown'; import SvelteMarkdown from "svelte-markdown";
import Link from '$lib/Link.svelte'; import Link from "$lib/Link.svelte";
const renderers = { link: Link } const renderers = { link: Link };
$: id = getId($page.url.search) $: id = getId($page.url.search);
$: e = $bundle ? $bundle.spec.events.find(ev => ev.id === id) : null $: e = $bundle ? $bundle.spec.events.find((ev) => ev.id === id) : null;
function getId (search) { function getId(search) {
const searchParams = new URLSearchParams(search) const searchParams = new URLSearchParams(search);
const cid = searchParams.get('id') const cid = searchParams.get("id");
if (!$bundle.spec.events.find(s => s.id === cid)) { if (!$bundle.spec.events.find((s) => s.id === cid)) {
goto('/') goto("/");
} }
return cid return cid;
} }
function speakersMap (arr) { function speakersMap(arr) {
if (!arr) return; if (!arr) return;
return arr.map(sId => { return arr.map((sId) => {
return $bundle.spec.speakers.find(sp => sp.id === sId) return $bundle.spec.speakers.find((sp) => sp.id === sId);
}) });
} }
function trackRender (trackId) { function trackRender(trackId) {
const track = $bundle.spec.tracks.find(t => t.id === trackId) const track = $bundle.spec.tracks.find((t) => t.id === trackId);
return track.shortname || track.name return track.shortname || track.name;
} }
</script> </script>
<svelte:head> <svelte:head>
<title>{e ? e.name : ''} | Události | {$bundle ? $bundle.name : 'UTXO.22'}</title> <title
>{e ? e.name : ""} | Události | {$bundle ? $bundle.name : "UTXO.22"}</title
>
</svelte:head> </svelte:head>
<section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web"> <section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web">
@ -56,15 +57,17 @@
<div class="mt-4 mb-2 flex flex-wrap gap-4"> <div class="mt-4 mb-2 flex flex-wrap gap-4">
{#each speakersMap(e.speakers) as s} {#each speakersMap(e.speakers) as s}
<div class="flex gap-2"> <div class="flex gap-2">
<Avatar speaker={s} size='semi-small' /> <Avatar speaker={s} size="semi-small" />
<div class="m-auto"><a href="/lide?id={s.id}" class="text-xl">{s.name}</a></div> <div class="m-auto">
<a href="/lide?id={s.id}" class="text-xl">{s.name}</a>
</div>
</div> </div>
{/each} {/each}
</div> </div>
{/if} {/if}
<div class="mt-8"> <div class="mt-8">
{#if e.description} {#if e.description}
<SvelteMarkdown source={e.description} renderers={renderers}/> <SvelteMarkdown source={e.description} {renderers} />
{/if} {/if}
</div> </div>
{/if} {/if}

Zobrazit soubor

@ -3,268 +3,299 @@
</script> </script>
<script> <script>
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from "svelte";
import { faker } from '@faker-js/faker'; import { faker } from "@faker-js/faker";
import { orderTicketForm, apiStatus, userData, userDataLocal } from '$lib/stores'; import {
orderTicketForm,
apiStatus,
userData,
userDataLocal,
} from "$lib/stores";
//import btcPay from 'https://btcpay.utxo.cz/modal/btcpay.js'; //import btcPay from 'https://btcpay.utxo.cz/modal/btcpay.js';
import api from '$lib/api.js'; import api from "$lib/api.js";
import makeBlockie from 'ethereum-blockies-base64'; import makeBlockie from "ethereum-blockies-base64";
import { format, formatDistanceToNow } from 'date-fns'; import { format, formatDistanceToNow } from "date-fns";
import { cs } from 'date-fns/locale/index.js' import { cs } from "date-fns/locale/index.js";
import { page } from '$app/stores'; import { page } from "$app/stores";
import { goto } from '$app/navigation'; import { goto } from "$app/navigation";
import { loadOrders, loadApiStatus } from '$lib/orders'; import { loadOrders, loadApiStatus } from "$lib/orders";
const orderTicketFormLS = localStorage.getItem('orderTicketForm') const orderTicketFormLS = localStorage.getItem("orderTicketForm");
let parsed = JSON.parse(orderTicketFormLS) let parsed = JSON.parse(orderTicketFormLS);
if (parsed && parsed.__v === $orderTicketForm.__v) { if (parsed && parsed.__v === $orderTicketForm.__v) {
orderTicketForm.set(parsed) orderTicketForm.set(parsed);
} }
let forceShow = false let forceShow = false;
let loading = true let loading = true;
let formProcessing = false let formProcessing = false;
$: showOrder = !(($userDataLocal.orders && $userDataLocal.orders.length > 0) || ($userDataLocal.tickets && $userDataLocal.tickets.length > 0)) $: showOrder = !(
$: orders = $userDataLocal.orders ($userDataLocal.orders && $userDataLocal.orders.length > 0) ||
$: tickets = $userDataLocal.tickets ($userDataLocal.tickets && $userDataLocal.tickets.length > 0)
);
$: orders = $userDataLocal.orders;
$: tickets = $userDataLocal.tickets;
let periodic15 = null;
let periodic15 = null let periodic60 = null;
let periodic60 = null
onMount(async () => { onMount(async () => {
const sp = $page.url.searchParams;
const sp = $page.url.searchParams if (sp.get("done")) {
if (sp.get('done')) { goto("/gratulujeme");
goto('/gratulujeme');
} }
if (sp.get('id') && sp.get('secret')) { if (sp.get("id") && sp.get("secret")) {
userData.update(ud => { userData.update((ud) => {
if (!ud.tickets) ud.tickets = []; if (!ud.tickets) ud.tickets = [];
ud.tickets.push([ sp.get('id'), sp.get('secret') ].join(':')) ud.tickets.push([sp.get("id"), sp.get("secret")].join(":"));
return ud return ud;
}) });
await loadOrders($userData) await loadOrders($userData);
goto('/vstupenky'); goto("/vstupenky");
} }
loading = false loading = false;
periodic15 = setInterval(() => { periodic15 = setInterval(() => {
loadApiStatus() loadApiStatus();
}, 15 * 1000) }, 15 * 1000);
periodic60 = setInterval(() => { periodic60 = setInterval(() => {
loadOrders($userData) loadOrders($userData);
}, 60 * 1000) }, 60 * 1000);
}) });
onDestroy(() => { onDestroy(() => {
clearInterval(periodic15) clearInterval(periodic15);
clearInterval(periodic60) clearInterval(periodic60);
}) });
faker.locale = 'cz'; faker.locale = "cz";
let orderError = null let orderError = null;
$: count = $apiStatus ? [...Array($apiStatus.config.maxTickets).keys()].map(i => i+1) : []; $: count = $apiStatus
? [...Array($apiStatus.config.maxTickets).keys()].map((i) => i + 1)
: [];
function selectPaymentMethod (el) { function selectPaymentMethod(el) {
orderTicketForm.update(f => { orderTicketForm.update((f) => {
f.paymentMethod = el.target.value f.paymentMethod = el.target.value;
return f return f;
}) });
} }
function ticketFormCountArray (count) { function ticketFormCountArray(count) {
return [...Array(count).keys()].map(i => i) return [...Array(count).keys()].map((i) => i);
} }
function processOrderError (error) { function processOrderError(error) {
if (typeof error === 'string') { if (typeof error === "string") {
return { title: error } return { title: error };
} }
const out = { title: error.title } const out = { title: error.title };
if (error.formErrors && error.formErrors.length > 0) { if (error.formErrors && error.formErrors.length > 0) {
let suberrs = [] let suberrs = [];
if (error.formErrors.find(fe => fe.instancePath === '/email')) { if (error.formErrors.find((fe) => fe.instancePath === "/email")) {
suberrs.push(`Neplatný email`) suberrs.push(`Neplatný email`);
} }
let ticket = null let ticket = null;
if (error.formErrors.find(fe => ticket = fe.instancePath.match(/\/tickets\/(\d+)\/twitter/))) { if (
suberrs.push(`Neplatný Twitter účet (Vstupenka #${Number(ticket[1])+1}).`) error.formErrors.find(
(fe) => (ticket = fe.instancePath.match(/\/tickets\/(\d+)\/twitter/))
)
) {
suberrs.push(
`Neplatný Twitter účet (Vstupenka #${Number(ticket[1]) + 1}).`
);
} }
out.title = 'Nesprávně vyplněný formulář: ' + (suberrs.length > 0 ? suberrs.join(',') : 'neznámá chyba') out.title =
out.debug = error.formErrors "Nesprávně vyplněný formulář: " +
(suberrs.length > 0 ? suberrs.join(",") : "neznámá chyba");
out.debug = error.formErrors;
} }
if (!out.title) { if (!out.title) {
out.title = 'Neznámá chyba, zkuste to prosím později.' out.title = "Neznámá chyba, zkuste to prosím později.";
} }
console.log(out) console.log(out);
return out return out;
} }
async function submitOrderHandler () { async function submitOrderHandler() {
// order process // order process
formProcessing = true formProcessing = true;
// clean previous errors // clean previous errors
$: orderError = null $: orderError = null;
const cloned = Object.assign({}, $orderTicketForm) const cloned = Object.assign({}, $orderTicketForm);
cloned.tipCustom = Number(cloned.tipCustom) cloned.tipCustom = Number(cloned.tipCustom);
cloned.totalPrice = totalPrice cloned.totalPrice = totalPrice;
const resp = await api.submitOrder(cloned) const resp = await api.submitOrder(cloned);
if (resp.error) { if (resp.error) {
$: orderError = processOrderError(resp.error) $: orderError = processOrderError(resp.error);
formProcessing = false formProcessing = false;
return null; return null;
} }
if (!resp.ok || !resp.payment.url) { if (!resp.ok || !resp.payment.url) {
return null return null;
} }
// reset tickets in form // reset tickets in form
orderTicketForm.update(of => { orderTicketForm.update((of) => {
of.count = 1 of.count = 1;
of.tickets = [] of.tickets = [];
return of return of;
}) });
// add order to user data // add order to user data
userData.update(ud => { userData.update((ud) => {
if (!ud.orders) { if (!ud.orders) {
ud.orders = [] ud.orders = [];
} }
ud.orders.push(resp.orderId) ud.orders.push(resp.orderId);
return ud return ud;
}) });
window.location.replace(resp.payment.url) window.location.replace(resp.payment.url);
} }
let ticketsNum = 0 let ticketsNum = 0;
orderTicketForm.subscribe(f => { orderTicketForm.subscribe((f) => {
if (f.count !== ticketsNum) { if (f.count !== ticketsNum) {
ticketsNum = f.count ticketsNum = f.count;
orderTicketForm.update(cf => { orderTicketForm.update((cf) => {
f.tickets = f.tickets.slice(0, f.count) f.tickets = f.tickets.slice(0, f.count);
for (let i = 0; i < f.count; i++) { for (let i = 0; i < f.count; i++) {
console.log(i) console.log(i);
if (!f.tickets[i]) { if (!f.tickets[i]) {
f.tickets[i] = { name: '', org: '', twitter: '', extras: { kino: false } } f.tickets[i] = {
name: "",
org: "",
twitter: "",
extras: { kino: false },
};
} }
} }
return f return f;
}) });
} }
localStorage.setItem('orderTicketForm', JSON.stringify(f)) localStorage.setItem("orderTicketForm", JSON.stringify(f));
}) });
function changeTip (perc) { function changeTip(perc) {
return () => { return () => {
orderTicketForm.update(of => { orderTicketForm.update((of) => {
of.tipPercent = perc of.tipPercent = perc;
of.tipCustom = '' of.tipCustom = "";
return of return of;
}) });
} };
} }
orderTicketForm.subscribe(of => { orderTicketForm.subscribe((of) => {
const val = Number(of.tipCustom) const val = Number(of.tipCustom);
if (val === NaN || !val || val < 1 || !String(val).match(/^\d+$/)) { if (val === NaN || !val || val < 1 || !String(val).match(/^\d+$/)) {
of.tipCustom = '' of.tipCustom = "";
} } else if (val && val > 0 && of.tipPercent > 0) {
else if (val && val > 0 && of.tipPercent > 0) { of.tipPercent = 0;
of.tipPercent = 0
} }
if (orderError) { if (orderError) {
orderError = false orderError = false;
} }
return of return of;
}) });
function roundPrice (num) { function roundPrice(num) {
return Math.round(num * 100) / 100 return Math.round(num * 100) / 100;
} }
function extrasStatsRender (form) { function extrasStatsRender(form) {
let obj = {} let obj = {};
$apiStatus.config.extras.forEach(ex => { $apiStatus.config.extras.forEach((ex) => {
obj[ex.id] = { count: 0, sum: 0, price: ex.price } obj[ex.id] = { count: 0, sum: 0, price: ex.price };
form.tickets.forEach(t => { form.tickets.forEach((t) => {
if (t.extras[ex.id]) { if (t.extras[ex.id]) {
obj[ex.id].count++ obj[ex.id].count++;
obj[ex.id].sum = obj[ex.id].count * ex.price obj[ex.id].sum = obj[ex.id].count * ex.price;
} }
}) });
}) });
let arr = Object.keys(obj).map(key => Object.assign({ id: key }, obj[key])) let arr = Object.keys(obj).map((key) =>
Object.assign({ id: key }, obj[key])
);
return { return {
sum: arr.reduce((p, c) => p + c.sum, 0), sum: arr.reduce((p, c) => p + c.sum, 0),
arr arr,
} };
} }
$: extrasStats = $apiStatus ? extrasStatsRender($orderTicketForm) : {} $: extrasStats = $apiStatus ? extrasStatsRender($orderTicketForm) : {};
$: ticketPrice = $apiStatus && $apiStatus.wave ? $orderTicketForm.count * $apiStatus.wave.price : 0 $: ticketPrice =
$: totalBeforeTip = ticketPrice + extrasStats.sum $apiStatus && $apiStatus.wave
$: tip = roundPrice($orderTicketForm.tipPercent > 0 ? $orderTicketForm.tipPercent * (totalBeforeTip/100) : Number($orderTicketForm.tipCustom)) ? $orderTicketForm.count * $apiStatus.wave.price
$: totalPrice = totalBeforeTip + tip : 0;
$: totalBeforeTip = ticketPrice + extrasStats.sum;
$: tip = roundPrice(
$orderTicketForm.tipPercent > 0
? $orderTicketForm.tipPercent * (totalBeforeTip / 100)
: Number($orderTicketForm.tipCustom)
);
$: totalPrice = totalBeforeTip + tip;
const orderStatuses = { const orderStatuses = {
new: { new: {
name: 'Čeká na platbu', name: "Čeká na platbu",
icon: 'fa-solid fa-clock', icon: "fa-solid fa-clock",
color: 'text-yellow-600', color: "text-yellow-600",
text: (o) => { text: (o) => {
return 'Tato objednávka čeká na platbu' return "Tato objednávka čeká na platbu";
}, },
}, },
done: { done: {
name: 'Zaplacená', name: "Zaplacená",
icon: 'fa-solid fa-check', icon: "fa-solid fa-check",
color: 'text-green-600' color: "text-green-600",
}, },
cancelled: { cancelled: {
name: 'Zrušená', name: "Zrušená",
icon: 'fa-solid fa-ban', icon: "fa-solid fa-ban",
color: 'text-gray-600' color: "text-gray-600",
}, },
expired: { expired: {
name: 'Expirovaná', name: "Expirovaná",
icon: 'fa-solid fa-clock', icon: "fa-solid fa-clock",
color: 'text-gray-600' color: "text-gray-600",
} },
};
async function orderActionHandler(id, action) {
const resp = await api.apiCall(
"updateOrder",
{ method: "POST" },
{ id, action }
);
loadOrders($userData);
loadApiStatus();
} }
async function orderActionHandler (id, action) { function removeOrder(id) {
const resp = await api.apiCall('updateOrder', { method: 'POST' }, { id, action }) userData.update((ud) => {
loadOrders($userData) console.log("change", ud.orders, id);
loadApiStatus() let index = ud.orders.indexOf(id);
}
function removeOrder (id) {
userData.update(ud => {
console.log('change', ud.orders, id)
let index = ud.orders.indexOf(id)
if (index >= 0) { if (index >= 0) {
if (ud.orders.length === 1) { if (ud.orders.length === 1) {
ud.orders = [] ud.orders = [];
} else { } else {
ud.orders.splice(index, 1) ud.orders.splice(index, 1);
} }
} }
return ud return ud;
}) });
loadOrders($userData) loadOrders($userData);
} }
</script> </script>
<svelte:head> <svelte:head>
@ -281,21 +312,37 @@
<div class="mt-4 flex flex-wrap gap-3"> <div class="mt-4 flex flex-wrap gap-3">
{#each tickets as ticket} {#each tickets as ticket}
<div class="w-full sm:w-80"> <div class="w-full sm:w-80">
<div class="h-2 bg-blue-web rounded-t-md shadow-md"></div> <div class="h-2 bg-blue-web rounded-t-md shadow-md" />
<div class="border-l border-b border-r p-4 rounded-b-md shadow-md border-blue-web"> <div
class="border-l border-b border-r p-4 rounded-b-md shadow-md border-blue-web"
>
<div class="flex gap-3 text-sm"> <div class="flex gap-3 text-sm">
<div class="w-11 h-11 rounded-md" style="background-image: url({makeBlockie(ticket.avatarHash)}); background-size: 100% 100%;"></div> <div
class="w-11 h-11 rounded-md"
style="background-image: url({makeBlockie(
ticket.avatarHash
)}); background-size: 100% 100%;"
/>
<div class="w-auto"> <div class="w-auto">
<div class="no-flex"> <div class="no-flex">
<span class="px-2 py-1 bg-blue-web text-white rounded w-auto">#{ticket.id}</span> <span
class="px-2 py-1 bg-blue-web text-white rounded w-auto"
>#{ticket.id}</span
>
</div> </div>
<div class="mt-1.5">Běžná vstupenka</div> <div class="mt-1.5">Běžná vstupenka</div>
</div> </div>
</div> </div>
{#if ticket.data} {#if ticket.data}
<div class="mt-2"> <div class="mt-2">
{#if ticket.data.name}{ticket.data.name}{:else}<span class="italic">Anonym</span>{/if} {#if ticket.data.org}({ticket.data.org}){/if} {#if ticket.data.name}{ticket.data.name}{:else}<span
{#if ticket.data.twitter}<a href="https://twitter.com/{ticket.data.twitter}" target="_blank"><i class="fa-brands fa-twitter"></i></a>{/if} class="italic">Anonym</span
>{/if}
{#if ticket.data.org}({ticket.data.org}){/if}
{#if ticket.data.twitter}<a
href="https://twitter.com/{ticket.data.twitter}"
target="_blank"><i class="fa-brands fa-twitter" /></a
>{/if}
</div> </div>
{/if} {/if}
</div> </div>
@ -313,32 +360,71 @@
<div class="mt-4"> <div class="mt-4">
{#each orders as order} {#each orders as order}
<div class="mb-2 px-3 py-2 border rounded-lg"> <div class="mb-2 px-3 py-2 border rounded-lg">
{#if ['expired', 'cancelled'].includes(order.status)} {#if ["expired", "cancelled"].includes(order.status)}
<div class="float-right"><a class="cursor-pointer" on:click={() => removeOrder(order.id)}><i class="fa-solid fa-xmark"></i></a></div> <div class="float-right">
<a
class="cursor-pointer"
on:click={() => removeOrder(order.id)}
><i class="fa-solid fa-xmark" /></a
>
</div>
{/if} {/if}
<div class="flex flex-wrap gap-3 text-sm"> <div class="flex flex-wrap gap-3 text-sm">
<div class="px-2 py-1 bg-gray-200 rounded">#{order.id}</div> <div class="px-2 py-1 bg-gray-200 rounded">#{order.id}</div>
<div class="my-auto uppercase {orderStatuses[order.status].color ? (orderStatuses[order.status].color) : ''}"> <div
class="my-auto uppercase {orderStatuses[order.status].color
? orderStatuses[order.status].color
: ''}"
>
{#if orderStatuses[order.status].icon} {#if orderStatuses[order.status].icon}
<i class="{orderStatuses[order.status].icon} mr-0.5"></i> <i class="{orderStatuses[order.status].icon} mr-0.5" />
{/if} {/if}
{orderStatuses[order.status].name} {orderStatuses[order.status].name}
{#if order.status === 'new'} {#if order.status === "new"}
&nbsp;- expirace {format(new Date(order.expiration), 'd.M. H:mm')} (zbývá {formatDistanceToNow(new Date(order.expiration), { locale: cs, includeSeconds: true })}) &nbsp;- expirace {format(
new Date(order.expiration),
"d.M. H:mm"
)} (zbývá {formatDistanceToNow(
new Date(order.expiration),
{ locale: cs, includeSeconds: true }
)})
{/if} {/if}
</div> </div>
</div> </div>
<div class="mt-2 text-sm flex gap-2"> <div class="mt-2 text-sm flex gap-2">
<div>Vytvořeno: <span class="font-bold">{format(new Date(order.created), 'd.M.y H:mm')}</span><br/></div> <div>
<div>Částka: <span class="font-bold">{order.amount}</span><br/></div> Vytvořeno: <span class="font-bold"
<div>Platební metoda: <span class="font-bold">{$apiStatus.config.paymentMethods.find(pm => pm.id === order.paymentMethod).shortname}</span></div> >{format(new Date(order.created), "d.M.y H:mm")}</span
><br />
</div>
<div>
Částka: <span class="font-bold">{order.amount}</span><br
/>
</div>
<div>
Platební metoda: <span class="font-bold"
>{$apiStatus.config.paymentMethods.find(
(pm) => pm.id === order.paymentMethod
).shortname}</span
>
</div>
</div> </div>
{#if orderStatuses[order.status].text} {#if orderStatuses[order.status].text}
<div class="mt-2 italic">{orderStatuses[order.status].text(order)}</div> <div class="mt-2 italic">
{orderStatuses[order.status].text(order)}
</div>
{#if order.actions} {#if order.actions}
<div class="flex gap-3"> <div class="flex gap-3">
{#each order.actions as action} {#each order.actions as action}
<div class="mt-2"><a href="{action.url}" class="underline hover:no-underline cursor-pointer" on:click={() => orderActionHandler(order.id, action.remote)}>{action.name}</a></div> <div class="mt-2">
<a
href={action.url}
class="underline hover:no-underline cursor-pointer"
on:click={() =>
orderActionHandler(order.id, action.remote)}
>{action.name}</a
>
</div>
{/each} {/each}
</div> </div>
{/if} {/if}
@ -352,45 +438,79 @@
<h1 class="uppercase text-2xl font-bold">Nákup vstupenek</h1> <h1 class="uppercase text-2xl font-bold">Nákup vstupenek</h1>
<div class="mt-4 mb-8"> <div class="mt-4 mb-8">
<div class="flex w-full uppercase text-sm mb-2"> <div class="flex w-full uppercase text-sm mb-2">
<div class="flex-1">{$apiStatus.wave.name} ({$apiStatus.wave.price} Kč)</div> <div class="flex-1">
<div class="justify-end">Zbývá {$apiStatus.wave.live.left} / {$apiStatus.wave.count}</div> {$apiStatus.wave.name} ({$apiStatus.wave.price} Kč)
</div>
<div class="justify-end">
Zbývá {$apiStatus.wave.live.left} / {$apiStatus.wave.count}
</div>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-2.5"> <div class="w-full bg-gray-200 rounded-full h-2.5">
<div class="bg-utxo-gradient h-2.5 rounded-full transition-all" style="width: {$apiStatus.wave.live.perc}%"></div> <div
class="bg-utxo-gradient h-2.5 rounded-full transition-all"
style="width: {$apiStatus.wave.live.perc}%"
/>
</div> </div>
</div> </div>
{#if $apiStatus.wave.live.left <= 0} {#if $apiStatus.wave.live.left <= 0}
<div class="mt-2">Aktuální vlna je vyprodaná. {#if $apiStatus.wave.live.waiting > 0}Zarezervované a nezaplacené vstupenky ({$apiStatus.wave.live.waiting}) se postupně vrací do prodeje.{/if}</div> <div class="mt-2">
{:else} Aktuální vlna je vyprodaná. {#if $apiStatus.wave.live.waiting > 0}Zarezervované
{#if !showOrder && !forceShow} a nezaplacené vstupenky ({$apiStatus.wave.live.waiting}) se
<div class="mt-4"> postupně vrací do prodeje.{/if}
<button class="border rounded-full border-[#E16A61] hover:border-0 hover:p-px hover:bg-utxo-gradient hover:text-white" on:click={() => forceShow = true}><div class="px-6 py-2">Nakoupit další vstupenky</div></button>
</div> </div>
{:else} {:else if !showOrder && !forceShow}
{#if !$apiStatus.wave} <div class="mt-4">
<button
class="border rounded-full border-[#E16A61] hover:border-0 hover:p-px hover:bg-utxo-gradient hover:text-white"
on:click={() => (forceShow = true)}
><div class="px-6 py-2">Nakoupit další vstupenky</div></button
>
</div>
{:else if !$apiStatus.wave}
<div class="mt-4">V současné době nelze zakoupit vstupenky.</div> <div class="mt-4">V současné době nelze zakoupit vstupenky.</div>
{:else} {:else}
<div class="p-3 bg-blue-web-light rounded-md mt-6 text-blue-web shadow-md"> <div
class="p-3 bg-blue-web-light rounded-md mt-6 text-blue-web shadow-md"
>
<div class="sm:m-2 border rounded-md p-4 shadow bg-white mb-6"> <div class="sm:m-2 border rounded-md p-4 shadow bg-white mb-6">
<div class="mb-4"> <div class="mb-4">
Název akce: <span class="font-bold">UTXO.22</span><br/> Název akce: <span class="font-bold">UTXO.22</span><br />
Datum konání: <span class="font-bold">4. - 5. červen 2022</span> (sobota - neděle)<br/> Datum konání: <span class="font-bold">4. - 5. červen 2022</span>
Místo: <span class="font-bold">Gabriel Loci</span>, Praha 5<br/><br/> (sobota - neděle)<br />
Aktuální cena vstupenky: <span class="font-bold">{$apiStatus.wave.price}</span> / osobu ({ $apiStatus.wave.name }) Místo: <span class="font-bold">Gabriel Loci</span>, Praha 5<br
/><br />
Aktuální cena vstupenky:
<span class="font-bold">{$apiStatus.wave.price}</span>
/ osobu ({$apiStatus.wave.name})
</div> </div>
<div class="md:flex gap-3"> <div class="md:flex gap-3">
<div class="md:w-1/2 p-2"> <div class="md:w-1/2 p-2">
<div class="font-bold uppercase">&nbsp;Vstupenka obsahuje:</div> <div class="font-bold uppercase">
&nbsp;Vstupenka obsahuje:
</div>
<div class="mt-4"> <div class="mt-4">
<div class="mb-2">• přístup na všechny přednášky, workshopy a další události v rámci konference</div> <div class="mb-2">
<div class="mb-2">• platnost oba dva konferenční dny (sobota + neděle)</div> • přístup na všechny přednášky, workshopy a další události
<div class="mb-2">• přístup na <span class="font-bold">UTXO.Party</span> v sobotu večer</div> v rámci konference
</div>
<div class="mb-2">
• platnost oba dva konferenční dny (sobota + neděle)
</div>
<div class="mb-2">
• přístup na <span class="font-bold">UTXO.Party</span> v sobotu
večer
</div>
</div> </div>
</div> </div>
<div class="md:w-1/2 p-2 md:mt-0 mt-3"> <div class="md:w-1/2 p-2 md:mt-0 mt-3">
<div class="font-bold uppercase">&nbsp;Vstupenka NEobsahuje:</div> <div class="font-bold uppercase">
&nbsp;Vstupenka NEobsahuje:
</div>
<div class="mt-4"> <div class="mt-4">
<div class="mb-2">• jídlo a pití - občerstvení bude možné zakoupit na místě (platba kartou nebo Lightning)</div> <div class="mb-2">
• jídlo a pití - občerstvení bude možné zakoupit na místě
(platba kartou nebo Lightning)
</div>
</div> </div>
</div> </div>
</div> </div>
@ -398,49 +518,110 @@
<div class="p-2"> <div class="p-2">
<div> <div>
<div class="uppercase text-sm font-bold">Email</div> <div class="uppercase text-sm font-bold">Email</div>
<div class="mt-2 text-sm">Kontaktní email, na který budou zaslány vstupenky.</div> <div class="mt-2 text-sm">
<div class="mt-2"><input type="text" class="border border-blue-web rounded-md p-2 w-full lg:w-1/2 text-blue-web" bind:value={$orderTicketForm.email} /></div> Kontaktní email, na který budou zaslány vstupenky.
</div>
<div class="mt-2">
<input
type="text"
class="border border-blue-web rounded-md p-2 w-full lg:w-1/2 text-blue-web"
bind:value={$orderTicketForm.email}
/>
</div>
</div> </div>
<div class="mt-6"> <div class="mt-6">
<div class="uppercase text-sm font-bold">Počet vstupenek</div> <div class="uppercase text-sm font-bold">Počet vstupenek</div>
<div class="mt-2 flex gap-3"> <div class="mt-2 flex gap-3">
<select name="count" class="border border-blue-web rounded-md p-2 text-blue-web bg-white" bind:value={$orderTicketForm.count}> <select
name="count"
class="border border-blue-web rounded-md p-2 text-blue-web bg-white"
bind:value={$orderTicketForm.count}
>
{#each count as i} {#each count as i}
<option value={i}>{i}</option> <option value={i}>{i}</option>
{/each} {/each}
</select> </select>
<div class="my-auto"> <div class="my-auto">
× <span class="font-bold">{$apiStatus.wave.price}</span> (cena vstupenky) × <span class="font-bold">{$apiStatus.wave.price}</span> (cena
vstupenky)
</div> </div>
</div> </div>
</div> </div>
<div class="mt-6"> <div class="mt-6">
<div class="uppercase text-sm font-bold">Vstupenky a příplatky</div> <div class="uppercase text-sm font-bold">
<div class="mt-2 text-sm">Informace, které budou vytištěné na Vaší konferenční jmenovku. Tyto údaje jsou nepovinné a je možné je změnit později.</div> Vstupenky a příplatky
</div>
<div class="mt-2 text-sm">
Informace, které budou vytištěné na Vaší konferenční jmenovku.
Tyto údaje jsou nepovinné a je možné je změnit později.
</div>
</div> </div>
<div class="mt-2"> <div class="mt-2">
{#each ticketFormCountArray($orderTicketForm.count) as i} {#each ticketFormCountArray($orderTicketForm.count) as i}
<div class="p-4 bg-utxo-gradient text-white rounded-md mb-6 shadow-md"> <div
class="p-4 bg-utxo-gradient text-white rounded-md mb-6 shadow-md"
>
<div class=""> <div class="">
<div class="uppercase font-bold">Vstupenka #{i+1}</div> <div class="uppercase font-bold">Vstupenka #{i + 1}</div>
<div class="md:flex gap-2"> <div class="md:flex gap-2">
<div class="mt-2 flex-1"> <div class="mt-2 flex-1">
<div class="uppercase text-sm">Jméno (Přezdívka)</div> <div class="uppercase text-sm">Jméno (Přezdívka)</div>
<div class="mt-2"><input type="text" maxlength="25" class="border border-blue-web rounded-md p-2 w-full text-blue-web" bind:value={$orderTicketForm.tickets[i].name} placeholder="{faker.name.findName()}" /></div> <div class="mt-2">
<input
type="text"
maxlength="25"
class="border border-blue-web rounded-md p-2 w-full text-blue-web"
bind:value={$orderTicketForm.tickets[i].name}
placeholder={faker.name.findName()}
/>
</div>
</div> </div>
<div class="mt-2 flex-1"> <div class="mt-2 flex-1">
<div class="uppercase text-sm">Organizace (Firma)</div> <div class="uppercase text-sm">
<div class="mt-2"><input type="text" maxlength="25" class="border border-blue-web rounded-md p-2 w-full text-blue-web" bind:value={$orderTicketForm.tickets[i].org} placeholder="{faker.company.companyName()}" /></div> Organizace (Firma)
</div>
<div class="mt-2">
<input
type="text"
maxlength="25"
class="border border-blue-web rounded-md p-2 w-full text-blue-web"
bind:value={$orderTicketForm.tickets[i].org}
placeholder={faker.company.companyName()}
/>
</div>
</div> </div>
<div class="mt-2"> <div class="mt-2">
<div class="uppercase text-sm">Twitter účet</div> <div class="uppercase text-sm">Twitter účet</div>
<div class="mt-2"><input type="text" maxlength="25" class="border border-blue-web rounded-md p-2 w-full text-blue-web" bind:value={$orderTicketForm.tickets[i].twitter} placeholder="@{faker.internet.userName()}" /></div> <div class="mt-2">
<input
type="text"
maxlength="25"
class="border border-blue-web rounded-md p-2 w-full text-blue-web"
bind:value={$orderTicketForm.tickets[i].twitter}
placeholder="@{faker.internet.userName()}"
/>
</div> </div>
</div> </div>
<div class="uppercase text-sm font-bold mt-3">Volitelné příplatky</div> </div>
<div class="uppercase text-sm font-bold mt-3">
Volitelné příplatky
</div>
<div class="mt-1"> <div class="mt-1">
{#each $apiStatus.config.extras as ex} {#each $apiStatus.config.extras as ex}
<div><label class="cursor-pointer"><input type="checkbox" class="mr-0.5 cursor-pointer" bind:checked={$orderTicketForm.tickets[i].extras[ex.id]} /> <span class="font-semibold">{ex.name} ({ex.price} Kč)</span></label> - <span class="text-sm">{ex.desc}</span></div> <div>
<label class="cursor-pointer"
><input
type="checkbox"
class="mr-0.5 cursor-pointer"
bind:checked={$orderTicketForm.tickets[i]
.extras[ex.id]}
/>
<span class="font-semibold"
>{ex.name} ({ex.price} Kč)</span
></label
>
- <span class="text-sm">{ex.desc}</span>
</div>
{/each} {/each}
</div> </div>
</div> </div>
@ -448,12 +629,30 @@
{/each} {/each}
</div> </div>
<div class="mt-6"> <div class="mt-6">
<div class="uppercase text-sm font-bold">Dýško pro organizátory</div> <div class="uppercase text-sm font-bold">
Dýško pro organizátory
</div>
<div class="mt-2 flex flex-wrap gap-2"> <div class="mt-2 flex flex-wrap gap-2">
{#each $apiStatus.config.tipPercentages as tp} {#each $apiStatus.config.tipPercentages as tp}
<button class="block py-1.5 px-4 min-w-16 {$orderTicketForm.tipPercent === tp[0] && $orderTicketForm.tipCustom < 1 ? (tp[0] === 0 ? 'bg-gray-400' : 'bg-blue-web' )+' rounded-full text-white shadow-lg' : 'border border-solid border-blue-web rounded-full'}" on:click={changeTip(tp[0])}>{tp[1] || tp[0] + '%'}</button> <button
class="block py-1.5 px-4 min-w-16 {$orderTicketForm.tipPercent ===
tp[0] && $orderTicketForm.tipCustom < 1
? (tp[0] === 0 ? 'bg-gray-400' : 'bg-blue-web') +
' rounded-full text-white shadow-lg'
: 'border border-solid border-blue-web rounded-full'}"
on:click={changeTip(tp[0])}>{tp[1] || tp[0] + "%"}</button
>
{/each} {/each}
<div class="px-2 py-1 {$orderTicketForm.tipCustom > 0 ? 'px-5 border border-blue-web rounded-full bg-blue-web text-white' : ''}">Jiná částka: <input class="border border border-blue-web rounded-md px-2 py-1 text-blue-web w-16" bind:value={$orderTicketForm.tipCustom} /></div> <div
class="px-2 py-1 {$orderTicketForm.tipCustom > 0
? 'px-5 border border-blue-web rounded-full bg-blue-web text-white'
: ''}"
>
Jiná částka: <input
class="border border border-blue-web rounded-md px-2 py-1 text-blue-web w-16"
bind:value={$orderTicketForm.tipCustom}
/> Kč
</div>
</div> </div>
</div> </div>
<div class="mt-8"> <div class="mt-8">
@ -461,67 +660,113 @@
<div class="mt-2"> <div class="mt-2">
{#each $apiStatus.config.paymentMethods as pm} {#each $apiStatus.config.paymentMethods as pm}
<div class="mb-2"> <div class="mb-2">
<label class="cursor-pointer"><input checked={$orderTicketForm.paymentMethod === pm.id} name="paymentMethod" value="{pm.id}" type="radio" on:change={selectPaymentMethod} class="cursor-pointer" /> {pm.name}</label> <label class="cursor-pointer"
><input
checked={$orderTicketForm.paymentMethod === pm.id}
name="paymentMethod"
value={pm.id}
type="radio"
on:change={selectPaymentMethod}
class="cursor-pointer"
/>
{pm.name}</label
>
</div> </div>
{/each} {/each}
</div> </div>
</div> </div>
<div class="mt-6 py-3 px-4 bg-white rounded-md shadow-md text-sm"> <div class="mt-6 py-3 px-4 bg-white rounded-md shadow-md text-sm">
<div class="uppercase text-sm font-bold">Shrnutí objednávky</div> <div class="uppercase text-sm font-bold">
Shrnutí objednávky
</div>
<div class="mt-1.5"> <div class="mt-1.5">
<table cellpadding="5" class="table-auto"> <table cellpadding="5" class="table-auto">
<tr> <tr>
<td>{$orderTicketForm.count}× UTXO.22 vstupenka ({$apiStatus.wave.price} Kč)</td> <td
>{$orderTicketForm.count}× UTXO.22 vstupenka ({$apiStatus
.wave.price} Kč)</td
>
<td>{ticketPrice}</td> <td>{ticketPrice}</td>
</tr> </tr>
{#if extrasStats.sum > 0} {#if extrasStats.sum > 0}
{#each extrasStats.arr as es} {#each extrasStats.arr as es}
<tr> <tr>
<td>{es.count}× {$apiStatus.config.extras.find(e => e.id === es.id).shortname} ({es.price} Kč)</td> <td
>{es.count}× {$apiStatus.config.extras.find(
(e) => e.id === es.id
).shortname} ({es.price} Kč)</td
>
<td>{es.sum}</td> <td>{es.sum}</td>
</tr> </tr>
{/each} {/each}
{/if} {/if}
{#if tip > 0} {#if tip > 0}
<tr> <tr>
<td>dýško pro organizátory ({$orderTicketForm.tipPercent > 0 ? $orderTicketForm.tipPercent + '%' : $orderTicketForm.tipCustom + ' Kč'})</td> <td
>dýško pro organizátory ({$orderTicketForm.tipPercent >
0
? $orderTicketForm.tipPercent + "%"
: $orderTicketForm.tipCustom + " Kč"})</td
>
<td>{tip}</td> <td>{tip}</td>
</tr> </tr>
{/if} {/if}
<tr> <tr>
<th align="right">Celkem:</th> <th align="right">Celkem:</th>
<th class="font-bold">{totalPrice}<th> <th class="font-bold">{totalPrice}</th><th /></tr
</tr> >
</table> </table>
</div> </div>
<div class="mt-1.5">Platební metoda: <span class="font-bold">{#if $orderTicketForm.paymentMethod === 'btcpay'}Bitcoin{:else}Platební karta{/if}</span></div> <div class="mt-1.5">
Platební metoda: <span class="font-bold"
>{#if $orderTicketForm.paymentMethod === "btcpay"}Bitcoin{:else}Platební
karta{/if}</span
>
</div>
</div> </div>
<div class="mt-6"> <div class="mt-6">
<div class="flex gap-3"> <div class="flex gap-3">
{#if !formProcessing} {#if !formProcessing}
<div><button class="{formProcessing ? 'bg-gray-800' : 'hover:bg-utxo-gradient bg-[#E16A61]'} text-white font-semibold rounded-full shadow-md" disabled={formProcessing} on:click={submitOrderHandler}><div class="py-2 px-4">Odeslat objednávku - zaplatit {totalPrice}{#if $orderTicketForm.paymentMethod === 'btcpay'}bitcoinem{:else}platební kartou{/if}</div></button></div> <div>
<button
class="{formProcessing
? 'bg-gray-800'
: 'hover:bg-utxo-gradient bg-[#E16A61]'} text-white font-semibold rounded-full shadow-md"
disabled={formProcessing}
on:click={submitOrderHandler}
><div class="py-2 px-4">
Odeslat objednávku - zaplatit {totalPrice}{#if $orderTicketForm.paymentMethod === "btcpay"}bitcoinem{:else}platební
kartou{/if}
</div></button
>
</div>
{/if} {/if}
{#if formProcessing} {#if formProcessing}
<div class="flex gap-3"> <div class="flex gap-3">
<div class=""><img src="/img/Spin-1s-200px.gif" class="w-10" /></div> <div class="">
<div class="my-auto text-red-600 font-bold">Odesílám objednávku ..</div> <img src="/img/Spin-1s-200px.gif" class="w-10" />
</div>
<div class="my-auto text-red-600 font-bold">
Odesílám objednávku ..
</div>
</div> </div>
{/if} {/if}
</div> </div>
{#if orderError} {#if orderError}
<div class="py-2 px-3 mt-4 text-red-600 bg-red-200 rounded-xl shadow-md">Chyba: {orderError.title}</div> <div
class="py-2 px-3 mt-4 text-red-600 bg-red-200 rounded-xl shadow-md"
>
Chyba: {orderError.title}
</div>
{/if} {/if}
</div> </div>
</div> </div>
</div> </div>
{/if} {/if}
{/if}
{/if}
</div> </div>
<!--pre>{JSON.stringify($orderTicketForm, null, 2)}</pre> <!--pre>{JSON.stringify($orderTicketForm, null, 2)}</pre>
<pre>{JSON.stringify(extrasStats, null, 2)}</pre --> <pre>{JSON.stringify(extrasStats, null, 2)}</pre -->
{/if} {/if}
{/if} {/if}
</section> </section>