This commit is contained in:
tree 2022-04-06 17:33:02 +02:00
rodič 651b531d72
revize 8a1e1f9bcc
21 změnil soubory, kde provedl 696 přidání a 140 odebrání

36
.github/workflows/gh-pages.yml vendorováno Normal file
Zobrazit soubor

@ -0,0 +1,36 @@
name: GitHub Pages
on:
push:
branches:
- master # Set a branch to deploy
pull_request:
jobs:
deploy:
runs-on: ubuntu-20.04
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- uses: actions/checkout@v2
with:
submodules: true # Fetch Hugo themes (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- uses: actions/setup-node@v2
with:
node-version: '17'
- run: npm install
- run: npm run build
- name: Add custom domain
run: "touch build/CNAME && echo \"new.utxo.cz\" >> build/CNAME"
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
if: ${{ github.ref == 'refs/heads/master' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build

71
package-lock.json vygenerováno
Zobrazit soubor

@ -13,11 +13,13 @@
"cookie": "^0.4.1"
},
"devDependencies": {
"@faker-js/faker": "^6.1.2",
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next",
"autoprefixer": "^10.4.4",
"postcss": "^8.4.12",
"svelte": "^3.46.0",
"svelte-markdown": "^0.2.2",
"tailwindcss": "^3.0.23"
}
},
@ -118,6 +120,16 @@
"node": ">=4"
}
},
"node_modules/@faker-js/faker": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-6.1.2.tgz",
"integrity": "sha512-QSvmexHCxeRUk1/yKmoEDaWB5Hohjvtim5g2JJwy8S/l0L4b3y/GxSpE6vN4SBoVGGahEQW21uqyRr7EofI35A==",
"dev": true,
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
}
},
"node_modules/@fontsource/fira-mono": {
"version": "4.5.7",
"resolved": "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-4.5.7.tgz",
@ -283,6 +295,12 @@
}
}
},
"node_modules/@types/marked": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.3.tgz",
"integrity": "sha512-HnMWQkLJEf/PnxZIfbm0yGJRRZYYMhb++O9M36UCTA9z53uPvVoSlAwJr3XOpDEryb7Hwl1qAx/MV6YIW1RXxg==",
"dev": true
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -1278,6 +1296,18 @@
"node": ">=12"
}
},
"node_modules/marked": {
"version": "4.0.12",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz",
"integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==",
"dev": true,
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -1747,6 +1777,19 @@
"svelte": ">=3.19.0"
}
},
"node_modules/svelte-markdown": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.2.2.tgz",
"integrity": "sha512-bNxpG3gRbC2UjAFakIMcLEzVq5db2Md57aeU4wKCuffSfD6f7RkvtYDqbrAk2ycC55t3wykOy0E7KDuvVKyDYA==",
"dev": true,
"dependencies": {
"@types/marked": "^4.0.1",
"marked": "^4.0.10"
},
"peerDependencies": {
"svelte": "^3.0.0"
}
},
"node_modules/tailwindcss": {
"version": "3.0.23",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.23.tgz",
@ -1963,6 +2006,12 @@
}
}
},
"@faker-js/faker": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-6.1.2.tgz",
"integrity": "sha512-QSvmexHCxeRUk1/yKmoEDaWB5Hohjvtim5g2JJwy8S/l0L4b3y/GxSpE6vN4SBoVGGahEQW21uqyRr7EofI35A==",
"dev": true
},
"@fontsource/fira-mono": {
"version": "4.5.7",
"resolved": "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-4.5.7.tgz",
@ -2088,6 +2137,12 @@
"svelte-hmr": "^0.14.11"
}
},
"@types/marked": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.3.tgz",
"integrity": "sha512-HnMWQkLJEf/PnxZIfbm0yGJRRZYYMhb++O9M36UCTA9z53uPvVoSlAwJr3XOpDEryb7Hwl1qAx/MV6YIW1RXxg==",
"dev": true
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -2708,6 +2763,12 @@
"sourcemap-codec": "^1.4.8"
}
},
"marked": {
"version": "4.0.12",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz",
"integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==",
"dev": true
},
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -2995,6 +3056,16 @@
"dev": true,
"requires": {}
},
"svelte-markdown": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.2.2.tgz",
"integrity": "sha512-bNxpG3gRbC2UjAFakIMcLEzVq5db2Md57aeU4wKCuffSfD6f7RkvtYDqbrAk2ycC55t3wykOy0E7KDuvVKyDYA==",
"dev": true,
"requires": {
"@types/marked": "^4.0.1",
"marked": "^4.0.10"
}
},
"tailwindcss": {
"version": "3.0.23",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.23.tgz",

Zobrazit soubor

@ -9,11 +9,13 @@
"prepare": "svelte-kit sync"
},
"devDependencies": {
"@faker-js/faker": "^6.1.2",
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next",
"autoprefixer": "^10.4.4",
"postcss": "^8.4.12",
"svelte": "^3.46.0",
"svelte-markdown": "^0.2.2",
"tailwindcss": "^3.0.23"
},
"type": "module",

Zobrazit soubor

@ -4,108 +4,3 @@
@tailwind components;
@tailwind utilities;
:root {
font-family: Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
--font-mono: 'Fira Mono', monospace;
--pure-white: #ffffff;
--primary-color: #b9c6d2;
--secondary-color: #d0dde9;
--tertiary-color: #edf0f8;
--accent-color: #ff3e00;
--heading-color: rgba(0, 0, 0, 0.7);
--text-color: #444444;
--background-without-opacity: rgba(255, 255, 255, 0.7);
--column-width: 42rem;
--column-margin-top: 4rem;
}
body {
min-height: 100vh;
margin: 0;
background-color: var(--primary-color);
background: linear-gradient(
180deg,
var(--primary-color) 0%,
var(--secondary-color) 10.45%,
var(--tertiary-color) 41.35%
);
}
body::before {
content: '';
width: 80vw;
height: 100vh;
position: absolute;
top: 0;
left: 10vw;
z-index: -1;
background: radial-gradient(
50% 50% at 50% 50%,
var(--pure-white) 0%,
rgba(255, 255, 255, 0) 100%
);
opacity: 0.05;
}
#svelte {
min-height: 100vh;
display: flex;
flex-direction: column;
}
h1,
h2,
p {
font-weight: 400;
color: var(--heading-color);
}
p {
line-height: 1.5;
}
a {
color: var(--accent-color);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1 {
font-size: 2rem;
text-align: center;
}
h2 {
font-size: 1rem;
}
pre {
font-size: 16px;
font-family: var(--font-mono);
background-color: rgba(255, 255, 255, 0.45);
border-radius: 3px;
box-shadow: 2px 2px 6px rgb(255 255 255 / 25%);
padding: 0.5em;
overflow-x: auto;
color: var(--text-color);
}
input,
button {
font-size: inherit;
font-family: inherit;
}
button:focus:not(:focus-visible) {
outline: none;
}
@media (min-width: 720px) {
h1 {
font-size: 2.4rem;
}
}

Zobrazit soubor

@ -5,6 +5,7 @@
<meta name="description" content="Svelte demo app" />
<link rel="icon" href="%svelte.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm" crossorigin="anonymous">
%svelte.head%
</head>
<body>

88
src/lib/Avatar.svelte Normal file
Zobrazit soubor

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

Zobrazit soubor

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

26
src/lib/Footer.svelte Normal file
Zobrazit soubor

@ -0,0 +1,26 @@
<script>
import { bundle } from '$lib/stores';
import SocialButtons from '$lib/SocialButtons.svelte';
</script>
{#if $bundle}
<div class="bg-blue-web-bg text-white">
<div class="relative mx-auto px-6 pt-10 pb-8 max-w-6xl flex">
<div class="flex-1">
<div>
<a href="/"><img src="/img/logo.png" alt="UTXO.22" class="w-28" /></a>
</div>
<div class="mt-4">
Otevřená komunitní kryptoměnová konference
</div>
<div class="mt-4">
<a href="{$bundle.links.docs}" class="hover:text-red-500"><i class="fas fa-book" />&nbsp; Dokumentace</a>
</div>
</div>
<SocialButtons size="normal" />
</div>
<div class="relative mx-auto px-6 pt-10 pb-10 max-w-6xl text-xs opacity-50">
<i class="fas fa-heart text-red-500" /> S láskou vytváří <a href="https://utxo.foundation" class="underline hover:no-underline">UTXO Foundation, z.s.</a>
</div>
</div>
{/if}

Zobrazit soubor

@ -0,0 +1,35 @@
<script>
export let size = 'small';
import { bundle } from '$lib/stores';
import { page } from '$app/stores';
const socials = [
{ link: 'twitter', ico: 'fab fa-twitter', name: 'Twitter' },
{ link: 'instagram', ico: 'fab fa-instagram', name: 'Instagram' },
{ link: 'fbevent', ico: 'fab fa-facebook', name: 'Facebook' },
{ link: 'discord', ico: 'fab fa-discord', name: 'Discord' },
{ link: 'telegram', ico: 'fab fa-telegram', name: 'Telegram' },
]
</script>
{#if $bundle}
{#if size === 'small'}
<div class="flex block space-x-2 m-auto justify-end flex-1">
{#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">
<i class="{soc.ico}" />
</a>
{/each}
</div>
{/if}
{#if size === 'normal'}
<div class="w-1/4">
<div class="flex flex-wrap gap-3 justify-between">
{#each socials as soc}
<div class="block hover:text-red-500"><a href="{$bundle.links[soc.link]}" class="" target="_blank"><i class="{soc.ico}" />&nbsp;&nbsp;{soc.name}</a></div>
{/each}
</div>
</div>
{/if}
{/if}

Zobrazit soubor

@ -1,7 +1,40 @@
import { bundle } from '$lib/stores.js';
import staticBundle from '$lib/bundle.json';
export async function spec () {
class API {
constructor () {
this.bundle = null
}
const resp = await fetch('https://spec.utxo.cz/22/bundle.json')
return resp.json()
async load () {
await this.loadBundle()
}
async loadBundle (local = false) {
if (!this.bundle) {
if (local) {
this.bundle = staticBundle
} else {
const resp = await fetch('https://spec.utxo.cz/22/bundle.json')
this.bundle = await resp.json()
}
bundle.set(this.bundle)
}
return this.bundle
}
async submitOrder (data) {
const resp = await fetch('https://api.utxo.cz/submitOrder', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
return resp.json()
}
}
const api = new API();
export default api;

1
src/lib/bundle.json Symbolický odkaz
Zobrazit soubor

@ -0,0 +1 @@
../../../utxo/dist/22/bundle.json

Zobrazit soubor

@ -1,11 +1,26 @@
<script>
import { page } from '$app/stores';
import logo from './svelte-logo.svg';
import { bundle } from '$lib/stores';
import SocialButtons from '$lib/SocialButtons.svelte';
</script>
<header>
<header class="relative" style="background-color: #32375C;">
<!-- <li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li> -->
<nav>
<nav class="relative mx-auto py-6 px-6 max-w-6xl text-center">
<div class="">
<div class="sm:flex flex-wrap space-x-10">
<div class="block justify-start flex-1">
<a href="/"><img src="/img/logo.png" alt="UTXO.22" style="width: 110px;" /></a>
</div>
<div class="sm:flex sm:space-x-10 uppercase text-sm font-bold text-white">
<a sveltekit:prefetch href="/" class="m-auto hover:text-red-500" class:text-blue-400={$page.url.pathname === '/'}>O konferenci</a>
<a sveltekit:prefetch href="/program" class="m-auto hover:text-red-500" class:text-blue-400={$page.url.pathname === '/program'}>Program</a>
<a sveltekit:prefetch href="/vstupenky" class="border-solid border border-red-500 rounded-full py-2 px-8 {$page.url.pathname === '/vstupenky' ? 'border-0 bg-utxo-gradient' : 'hover:border-0 hover:my-px hover:bg-utxo-gradient' }">Vstupenky</a>
</div>
<SocialButtons />
</div>
</div>
</nav>
</header>

10
src/lib/stores.js Normal file
Zobrazit soubor

@ -0,0 +1,10 @@
import { writable } from 'svelte/store';
export const bundle = writable(null);
export const orderTicketForm = writable({
count: 1,
email: '',
tickets: [],
paymentMethod: 'card',
});

Zobrazit soubor

@ -1,17 +1,21 @@
<script>
import Header from '$lib/header/Header.svelte';
import Footer from '$lib/Footer.svelte';
import '../app.css';
import * as api from '$lib/api.js';
import api from '$lib/api.js';
import { page } from '$app/stores';
import { onMount } from 'svelte';
let spec = null
let bundle = null
onMount(async () => {
spec = await api.spec()
bundle = await api.loadBundle($page.url.hostname === 'localhost')
})
</script>
<div class="layout min-h-screen py-6 flex flex-col justify-center relative overflow-hidden">
{#if bundle}
<div class="layout min-h-screen">
<div class="inset-0">
<Header />
@ -19,10 +23,9 @@
<slot />
</main>
</div>
<footer>
</footer>
<Footer />
</div>
{/if}
<style>
</style>

Zobrazit soubor

@ -3,35 +3,107 @@
</script>
<script>
import Counter from '$lib/Counter.svelte';
import { bundle } from '$lib/stores.js';
import Avatar from '$lib/Avatar.svelte';
import SvelteMarkdown from 'svelte-markdown';
let currentTrack = 'top'
let onlyLead = true
let onlyLeadPreview = false
$: currentBundle = $bundle;
$: leadSpeakersCount = currentBundle ? 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) {
return function () {
currentTrack = tId
onlyLead = !tId
}
}
function handleShowFull () {
$: currentTrack = null
}
</script>
<svelte:head>
<title>Home</title>
</svelte:head>
<section>
<h1>
<div class="welcome">
<picture>
<source srcset="svelte-welcome.webp" type="image/webp" />
<img src="svelte-welcome.png" alt="Welcome" />
</picture>
</div>
<section class="relative mx-auto py-10 px-6 max-w-6xl">
{#if $bundle}
<div class="flex flex-wrap gap-3 text-xs uppercase font-bold text-blue-web justify-left">
{#each tracks as track}
<div class="py-2 px-8 rounded-full border border-solid {currentTrack === 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}
</div>
to your new<br />SvelteKit app
</h1>
<h2>
try editing <strong>src/routes/index.svelte</strong>
</h2>
<Counter />
<h1 class="text-3xl font-bold underline">
Hello world!
</h1>
<div class="flex flex-wrap gap-6 mt-14 justify-center">
{#each $bundle.spec.speakers as speaker}
{#if (currentTrack === 'top' && speaker.lead === true) || currentTrack !== 'top'}
{#if (!currentTrack || speaker.tracks.includes(currentTrack)) || currentTrack === 'top'}
<Avatar speaker={speaker} />
{/if}
{/if}
{/each}
</div>
{#if currentTrack === 'top'}
<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="flex flex-wrap gap-3 mt-10 justify-center">
{#each $bundle.spec.speakers as speaker}
{#if !speaker.lead}
<Avatar speaker={speaker} size="small" />
{/if}
{/each}
</div>
</div>
{/if}
{/if}
</section>
<section class="bg-utxo-gradient">
<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="block flex-1">
<img src="/photos/gabriel-loci.jpeg" class="flex rounded-xl" />
</div>
<div class="text-white md:w-1/2 md:pt-0 pt-6">
<div class="uppercase">Místo</div>
<div class="uppercase mt-4 text-4xl font-bold">Gabriel Loci</div>
<div class="mt-4 font-bold">
Holečkova 106/10<br/>
150 00 Praha 5 - Smíchov
</div>
<div class="mt-4">
Benediktinky. Kulturní památka. Česká Pošta. Poštovní muzeum.
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.
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ů.
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>
</section>
{#if $bundle}
<section class="relative mx-auto py-10 px-6 max-w-6xl">
<div class="text-blue-web">
<h2 class="uppercase pt-5" id="faq">Často kladené dotazy (FAQ)</h2>
<div class="md:columns-2 columns-1 mt-8 h-auto">
{#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-4 font-bold">{item.question}</div>
<SvelteMarkdown source={item.answer} />
</div>
{/each}
</div>
</div>
</section>
{/if}
<style>
</style>

71
src/routes/program.svelte Normal file
Zobrazit soubor

@ -0,0 +1,71 @@
<script>
import { bundle } from '$lib/stores.js';
import EventTypeLabel from '$lib/EventTypeLabel.svelte';
import Avatar from '$lib/Avatar.svelte';
function speakersMap (arr) {
if (!arr) return;
return arr.map(sId => {
return $bundle.spec.speakers.find(sp => sp.id === sId)
})
}
function trackRender (trackId) {
const track = $bundle.spec.tracks.find(t => t.id === trackId)
return track.shortname || track.name
}
function getParents (e) {
return $bundle.spec.events.filter(i => i.parent === e.id)
}
</script>
<section class="relative mx-auto py-10 px-6 max-w-6xl mb-10 text-blue-web">
<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 par týdnů před konferencí.</div>
<div class="mt-6 ml-2">
{#each $bundle.spec.events.filter(e => !e.parent) as e}
<div class="mb-4 border px-3 py-2 rounded">
<div class="text-lg font-semibold">{e.name}</div>
{#if e.speakers && e.speakers.length > 0}
<div class="mt-1 mb-2 flex flex-wrap gap-2">
{#each speakersMap(e.speakers) as s}
<div class="flex gap-1.5">
<Avatar speaker={s} size='extra-small' />
<div class="m-auto">{s.name}</div>
</div>
{/each}
</div>
{/if}
<div class="mt-2 text-sm flex flex-wrap gap-2">
<div><EventTypeLabel event={e} /></div>
<div class="text-sm my-auto">{trackRender(e.track)}</div>
<div class="text-xs my-auto">{e.duration}m</div>
</div>
{#if getParents(e).length > 0}
<div class="p-2 mt-2 w-auto">
<div class="flex flex-wrap gap-2" cellpadding="5">
{#each getParents(e) as pe}
<div class="mb-2 border rounded-md py-1.5 px-2.5 bg-gray-100 text-sm">
<div class="font-bold">{pe.name}</div>
<div class="mt-1">
{#if pe.speakers.length === 0}
<div>TBA</div>
{:else}
{#each speakersMap(pe.speakers) as s}
<div class="flex gap-1"><Avatar speaker={s} size='micro' /><div>{s.name}</div></div>
{/each}
{/if}
</div>
</div>
{/each}
</div>
</div>
{/if}
</div>
{/each}
</div>
</section>

160
src/routes/vstupenky.svelte Normal file
Zobrazit soubor

@ -0,0 +1,160 @@
<script>
import { faker } from '@faker-js/faker';
import { orderTicketForm } from '$lib/stores';
import api from '$lib/api.js';
const orderTicketFormLS = localStorage.getItem('orderTicketForm')
if (orderTicketFormLS) {
orderTicketForm.set(JSON.parse(orderTicketFormLS))
}
faker.locale = 'cz';
const paymentMethods = [
{ id: 'card', name: 'Platební karta' },
{ id: 'btcpay', name: 'Bitcoin (BTC) / Lightning Network (LNBTC)' },
]
const maxTickets = 10
const ticketPrice = 400
const count = [...Array(maxTickets).keys()].map(i => i+1);
function selectPaymentMethod (el) {
orderTicketForm.update(f => {
f.paymentMethod = el.target.value
return f
})
}
function ticketFormCountArray (count) {
return [...Array(count).keys()].map(i => i)
}
async function submitOrderHandler () {
console.log($orderTicketForm)
const resp = await api.submitOrder($orderTicketForm)
console.log(resp)
}
let tickets = 0
orderTicketForm.subscribe(f => {
if (f.count !== tickets) {
tickets = f.count
orderTicketForm.update(cf => {
f.tickets = f.tickets.slice(0, f.count)
for (let i = 0; i < f.count; i++) {
console.log(i)
if (!f.tickets[i]) {
f.tickets[i] = { name: '', org: '', twitter: '' }
}
}
return f
})
}
localStorage.setItem('orderTicketForm', JSON.stringify(f))
})
</script>
<section class="relative mx-auto py-10 px-6 max-w-6xl mb-10 text-blue-web">
<div class="">
<h1 class="uppercase text-2xl font-bold">Vaše vstupenky</h1>
<div class="mt-6">Nemáte žádnou vstupenku</div>
</div>
<div class="mt-10">
<h1 class="uppercase text-2xl font-bold">Nákup vstupenek</h1>
<div class="mb-10 mt-4">
<div class="text-right text-sm">Zbývá 333 z 333 (celková emise: 999 vstupenek)</div>
<div class="w-full bg-gray-200 rounded-full h-6 dark:bg-gray-200 mt-2 flex text-xs text-white">
<div class="bg-[#39AEA9] h-6 rounded-l-full" style="width: 10%"><div class="py-1 px-2">I. vlna</div></div>
<!--div class="bg-[#A2D5AB] h-6 text-blue-web" style="width: 33%"><div class="py-1 px-2">II. vlna</div></div>
div class="bg-[#E5EFC1] h-6 rounded-r-full text-blue-web" style="width: 34%"><div class="py-1 px-2">III. vlna</div></div>-->
</div>
</div>
<div class="bg-gray-200 rounded-lg p-6">
<div class="mb-4">
Aktuální cena vstupenky: <span class="font-bold">{ticketPrice}</span> / osobu
</div>
<div class="flex">
<div class="w-1/2 p-4">
<div class="font-bold">✅ Vstupenka obsahuje</div>
<div class="mt-4 ml-4 text-sm">
<div>• přístup na všechny přednášky, workshopy a další událostí v rámci konference (do naplnění kapacity sálu)</div>
<div>• platnost oba dva konferenční dny (sobota + neděle)</div>
<div>• přístup na <b>UTXO.Party</b> během sobotního večera/noci</div>
<div>• skvělou možnost networkingu v rámci lokální kryptokomunity</div>
</div>
</div>
<div class="w-1/2 p-4">
<div class="font-bold">❌ Vstupenka NEobsahuje</div>
<div class="mt-4 ml-4 text-sm">
<div>• jídlo nebo pití - teplé i studené občerstvení bude možné zakoupit na místě, platba kartou nebo Lightning Network (LN)</div>
<div>• v případě menších přednášejících sálů negarantujeme místo pro každého</div>
</div>
</div>
</div>
</div>
<div class="p-5 bg-blue-web-light rounded-xl mt-4 text-blue-web">
<div>
<div class="uppercase text-sm font-bold">Počet vstupenek</div>
<div class="mt-2">
<select name="count" id="count" class="border border-blue-web rounded-md p-2 text-blue-web" bind:value={$orderTicketForm.count}>
{#each count as i}
<option value={i}>{i}</option>
{/each}
</select>
</div>
</div>
<div class="mt-4">
<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"><input type="text" class="border border-blue-web rounded-md p-2 w-1/2 text-blue-web" bind:value={$orderTicketForm.email} /></div>
</div>
<div class="mt-4">
<div class="uppercase text-sm font-bold">Údaje na jmenovku</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 class="mt-2">
{#each ticketFormCountArray($orderTicketForm.count) as i}
<div class="p-4 bg-utxo-gradient text-white rounded-md mb-6">
<div class="">
<div class="uppercase font-bold">Vstupenka #{i+1}</div>
<div class="md:flex gap-2">
<div class="mt-2 flex-1">
<div class="uppercase text-sm">Jméno</div>
<div class="mt-2"><input type="text" 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 class="mt-2 flex-1">
<div class="uppercase text-sm">Organizace</div>
<div class="mt-2"><input type="text" 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 class="mt-2">
<div class="uppercase text-sm">Twitter handle</div>
<div class="mt-2"><input type="text" 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>
{/each}
</div>
<div class="mt-4">
<div class="uppercase text-sm font-bold">Platební metoda</div>
<div class="mt-2">
{#each paymentMethods as pm}
<div class="mb-2">
<label class="cursor-pointer"><input checked={$orderTicketForm.paymentMethod === pm.id} name="paymentMethod" value="{pm.id}" type="radio" on:change={selectPaymentMethod} /> {pm.name}</label>
</div>
{/each}
</div>
</div>
<div class="mt-10">
Celkem: <span class="font-bold">{$orderTicketForm.count * ticketPrice}</span>
</div>
<div class="mt-4">
<button class="bg-utxo-gradient hover:drop-shadow-lg text-white font-bold py-2 px-4 rounded-full" on:click={submitOrderHandler}>Odeslat objednávku - přejít k platbě</button>
</div>
</div>
</div>
</section>

Binární soubor nebyl zobrazen.

Před

Šířka:  |  Výška:  |  Velikost: 34 KiB

binární
static/img/logo.png Normal file

Binární soubor nebyl zobrazen.

Za

Šířka:  |  Výška:  |  Velikost: 77 KiB

binární
static/photos/gabriel-loci.jpeg Normal file

Binární soubor nebyl zobrazen.

Za

Šířka:  |  Výška:  |  Velikost: 1.7 MiB

Zobrazit soubor

@ -1,7 +1,25 @@
module.exports = {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {},
extend: {
colors: {
custom: {
blue: '#4D96FF',
green: '#6BCB77',
yellow: '#FFD93D',
red: '#FF6B6B',
},
blue: {
web: '#393F67',
'web-bg': '#191B2E',
'web-light': '#DBE8F4',
utxo: '#00A1FE'
}
},
backgroundImage: {
'utxo-gradient': 'linear-gradient(130deg,#FF634D,#00A1FE)'
}
}
},
plugins: [],
}