1.2.1
This commit is contained in:
rodič
fde5799d73
revize
715f2a6379
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "utxo22-web",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"scripts": {
|
||||
"dev": "svelte-kit dev",
|
||||
"build": "svelte-kit build",
|
||||
|
|
|
@ -1 +1 @@
|
|||
export const version = "1.1-dev";
|
||||
export const version = "1.3-dev";
|
||||
|
|
|
@ -1,161 +1,193 @@
|
|||
<script context="module">
|
||||
export const prerender = true;
|
||||
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { format, compareAsc, compareDesc } from "date-fns"
|
||||
import { format, compareAsc, compareDesc } from "date-fns";
|
||||
import { bundle, userData, loadInfo, schedulePref } from "$lib/stores.js";
|
||||
import { cs } from "date-fns/locale/index.js";
|
||||
|
||||
let planNumber = 0
|
||||
$: plan = $bundle ? $bundle.spec['schedule-candidates'][planNumber] : null
|
||||
let planNumber = 0;
|
||||
$: plan = $bundle ? $bundle.spec["schedule-candidates"][planNumber] : null;
|
||||
|
||||
onMount(async () => {
|
||||
|
||||
bundle.subscribe(bundle => {
|
||||
const pref = {}
|
||||
pref.stages = bundle.spec.stages.map(s => s.id)
|
||||
pref.tracks = bundle.spec.tracks.map(s => s.id)
|
||||
schedulePref.set(pref)
|
||||
})
|
||||
})
|
||||
bundle.subscribe((bundle) => {
|
||||
const pref = {};
|
||||
pref.stages = bundle.spec.stages.map((s) => s.id);
|
||||
pref.tracks = bundle.spec.tracks.map((s) => s.id);
|
||||
schedulePref.set(pref);
|
||||
});
|
||||
});
|
||||
|
||||
function filterDateStage(arr, date, stageId) {
|
||||
return arr
|
||||
.filter(i => i.date === date)
|
||||
.filter(i => i.stage === stageId)
|
||||
.filter((i) => i.date === date)
|
||||
.filter((i) => i.stage === stageId);
|
||||
}
|
||||
|
||||
function findSegment(bundle, stage, period) {
|
||||
for (const st of stage.times) {
|
||||
const p = parsePeriod(bundle, st)
|
||||
if (compareAsc(period.start, p.period.end) !== -1 && compareDesc(period.end, p.period.start) !== 1) {
|
||||
continue
|
||||
const p = parsePeriod(bundle, st);
|
||||
if (
|
||||
compareAsc(period.start, p.period.end) !== -1 &&
|
||||
compareDesc(period.end, p.period.start) !== 1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
return p
|
||||
return p;
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
function dateSlots(pl, period, bundle, schedulePref = null) {
|
||||
let time = period.start
|
||||
const endTime = period.end
|
||||
const arr = []
|
||||
const rowspans = {}
|
||||
let time = period.start;
|
||||
const endTime = period.end;
|
||||
const arr = [];
|
||||
const rowspans = {};
|
||||
|
||||
while (compareAsc(time, endTime) === -1) {
|
||||
const stages = {}
|
||||
const stages = {};
|
||||
for (const stage of bundle.spec.stages) {
|
||||
if (rowspans[stage.id] > 0) {
|
||||
stages[stage.id] = null
|
||||
rowspans[stage.id]--
|
||||
continue
|
||||
stages[stage.id] = null;
|
||||
rowspans[stage.id]--;
|
||||
continue;
|
||||
}
|
||||
if (schedulePref && !schedulePref.stages.includes(stage.id)) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
let si = pl.schedule.find(pi => (new Date(pi.period.start).getTime() === new Date(time).getTime()) && pi.stage === stage.id)
|
||||
stages[stage.id] = si
|
||||
let si = pl.schedule.find(
|
||||
(pi) =>
|
||||
new Date(pi.period.start).getTime() === new Date(time).getTime() &&
|
||||
pi.stage === stage.id
|
||||
);
|
||||
stages[stage.id] = si;
|
||||
|
||||
if (si) {
|
||||
const span = Math.floor((new Date(si.period.end).getTime() - new Date(si.period.start).getTime())/(1000 * 60) / 30)
|
||||
si.span = span
|
||||
const span = Math.floor(
|
||||
(new Date(si.period.end).getTime() -
|
||||
new Date(si.period.start).getTime()) /
|
||||
(1000 * 60) /
|
||||
30
|
||||
);
|
||||
si.span = span;
|
||||
if (span > 1) {
|
||||
rowspans[stage.id] = span - 1
|
||||
rowspans[stage.id] = span - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
arr.push({ title: format(time, 'HH:mm'), stages })
|
||||
time = new Date(time.getTime() + (30 * 60 * 1000))
|
||||
arr.push({ title: format(time, "HH:mm"), stages });
|
||||
time = new Date(time.getTime() + 30 * 60 * 1000);
|
||||
}
|
||||
return arr
|
||||
return arr;
|
||||
}
|
||||
|
||||
function showSpeakers(bundle, ev) {
|
||||
return ev.speakers.map(sId => {
|
||||
const sp = bundle.spec.speakers.find(s => s.id === sId)
|
||||
return sp.name + (sp.nickname ? ` (${sp.nickname})` : '')
|
||||
}).join(', ')
|
||||
return ev.speakers
|
||||
.map((sId) => {
|
||||
const sp = bundle.spec.speakers.find((s) => s.id === sId);
|
||||
return sp.name + (sp.nickname ? ` (${sp.nickname})` : "");
|
||||
})
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
function showEventDetail(bundle, ev) {
|
||||
if (ev.type === 'lightning-series') {
|
||||
return bundle.spec.events.filter(e => e.parent === ev.id).map(e => `<span class="font-semibold"><a href="/udalosti?id=${e.id}">${e.name}</a></span> - ${showSpeakers(bundle, e) || 'TBD'}`).join('<br>')
|
||||
if (ev.type === "lightning-series") {
|
||||
return bundle.spec.events
|
||||
.filter((e) => e.parent === ev.id)
|
||||
.map(
|
||||
(e) =>
|
||||
`<span class="font-semibold"><a href="/udalosti?id=${e.id}">${
|
||||
e.name
|
||||
}</a></span> - ${showSpeakers(bundle, e) || "TBD"}`
|
||||
)
|
||||
.join("<br>");
|
||||
}
|
||||
return showSpeakers(bundle, ev)
|
||||
return showSpeakers(bundle, ev);
|
||||
}
|
||||
|
||||
function parsePeriod(bundle, str) {
|
||||
const [ dayNumber, times ] = str.split('/')
|
||||
const [ start, end ] = times.split('-')
|
||||
const date = bundle.dates[dayNumber-1]
|
||||
const [dayNumber, times] = str.split("/");
|
||||
const [start, end] = times.split("-");
|
||||
const date = bundle.dates[dayNumber - 1];
|
||||
return {
|
||||
date,
|
||||
period: {
|
||||
start: new Date(`${date}T${start}`),
|
||||
end: new Date(`${date}T${end}`)
|
||||
}
|
||||
}
|
||||
end: new Date(`${date}T${end}`),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function scheduleTimes(bundle) {
|
||||
return bundle.scheduleTimes.map(item => {
|
||||
return parsePeriod(bundle, item)
|
||||
})
|
||||
return bundle.scheduleTimes.map((item) => {
|
||||
return parsePeriod(bundle, item);
|
||||
});
|
||||
}
|
||||
|
||||
function eventTrackClasses(bundle, ev, selectedTracks) {
|
||||
if (!selectedTracks.includes(ev.track || '')) {
|
||||
return 'opacity-20'
|
||||
if (!selectedTracks.includes(ev.track || "")) {
|
||||
return "opacity-20";
|
||||
}
|
||||
return 'border border-blue-web/50'
|
||||
return "border border-blue-web/50";
|
||||
}
|
||||
|
||||
function findEvent(bundle, eventId) {
|
||||
const ev = bundle.spec.events.find(ev => ev.id === eventId)
|
||||
const ev = bundle.spec.events.find((ev) => ev.id === eventId);
|
||||
if (!ev) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
switch (ev.type) {
|
||||
case 'panel':
|
||||
ev.color = 'bg-orange-400/20 hover:bg-orange-400/40'
|
||||
break
|
||||
case 'talk':
|
||||
ev.color = 'bg-custom-green/20 hover:bg-custom-green/40'
|
||||
break
|
||||
case 'workshop':
|
||||
ev.color = 'bg-custom-blue/20 hover:bg-custom-blue/40'
|
||||
break
|
||||
case 'campfire':
|
||||
ev.color = 'bg-purple-400/20 hover:bg-purple-400/40'
|
||||
break
|
||||
case 'lightning-series':
|
||||
ev.color = 'bg-yellow-400/20 hover:bg-yellow-400/40'
|
||||
break
|
||||
case "panel":
|
||||
ev.color = "bg-orange-400/20 hover:bg-orange-400/40";
|
||||
break;
|
||||
case "talk":
|
||||
ev.color = "bg-custom-green/20 hover:bg-custom-green/40";
|
||||
break;
|
||||
case "workshop":
|
||||
ev.color = "bg-custom-blue/20 hover:bg-custom-blue/40";
|
||||
break;
|
||||
case "campfire":
|
||||
ev.color = "bg-purple-400/20 hover:bg-purple-400/40";
|
||||
break;
|
||||
case "lightning-series":
|
||||
ev.color = "bg-yellow-400/20 hover:bg-yellow-400/40";
|
||||
break;
|
||||
default:
|
||||
ev.color = 'bg-white hover:bg-gray-500/10'
|
||||
ev.color = "bg-white hover:bg-gray-500/10";
|
||||
}
|
||||
return ev
|
||||
return ev;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Časová osa | UTXO.22</title>
|
||||
</svelte:head>
|
||||
|
||||
<section class="relative mx-auto pt-6 sm:pt-10 pb-6 px-6 max-w-6xl text-blue-web">
|
||||
<section
|
||||
class="relative mx-auto pt-6 sm:pt-10 pb-6 px-6 max-w-6xl text-blue-web"
|
||||
>
|
||||
<h1 class="uppercase text-2xl font-bold mb-6">Časová osa</h1>
|
||||
{#if $bundle}
|
||||
<div class="font-semibold uppercase mb-1">Plán (řešení)</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<select class="border border-blue-web rounded-md p-1.5 text-blue-web bg-white" bind:value={planNumber}>
|
||||
{#each $bundle.spec['schedule-candidates'] as p, i}
|
||||
<option value={i}>#{i} [{[ 'score', 'thc:themeCrossing', 'tgc:tagsCrossing' ].map(key => { const [title,rkey] = key.split(':'); return `${title}:${Math.round(p.metrics[rkey || title]*1000)/1000}` }).join(', ')}]</option>
|
||||
<select
|
||||
class="border border-blue-web rounded-md p-1.5 text-blue-web bg-white"
|
||||
bind:value={planNumber}
|
||||
>
|
||||
{#each $bundle.spec["schedule-candidates"] as p, i}
|
||||
<option value={i}
|
||||
>#{i} [{["score", "thc:themeCrossing", "tgc:tagsCrossing"]
|
||||
.map((key) => {
|
||||
const [title, rkey] = key.split(":");
|
||||
return `${title}:${
|
||||
Math.round(p.metrics[rkey || title] * 1000) / 1000
|
||||
}`;
|
||||
})
|
||||
.join(", ")}]</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -167,35 +199,82 @@
|
|||
<div class="">
|
||||
<div class="font-semibold uppercase mb-1">Sál / Místo</div>
|
||||
<div class="flex gap-1 flex-wrap">
|
||||
<div class="m-0.5"><a href="#" class="hover:underline" on:click={() => $schedulePref.stages = $bundle.spec.stages.map(s => s.id)}>Všechny sály</a></div>
|
||||
<div class="m-0.5">
|
||||
<a
|
||||
href="#"
|
||||
class="hover:underline"
|
||||
on:click={() =>
|
||||
($schedulePref.stages = $bundle.spec.stages.map((s) => s.id))}
|
||||
>Všechny sály</a
|
||||
>
|
||||
</div>
|
||||
{#each $bundle.spec.stages as et}
|
||||
<div class="u-choose-div m-0.5"><label class="cursor-pointer"><input type="checkbox" bind:group={$schedulePref.stages} value={et.id} /></label> <span class="cursor-pointer" on:click={() => $schedulePref.stages = [et.id]}>{et.name}</span></div>
|
||||
<div class="u-choose-div m-0.5">
|
||||
<label class="cursor-pointer"
|
||||
><input
|
||||
type="checkbox"
|
||||
bind:group={$schedulePref.stages}
|
||||
value={et.id}
|
||||
/></label
|
||||
>
|
||||
<span
|
||||
class="cursor-pointer"
|
||||
on:click={() => ($schedulePref.stages = [et.id])}
|
||||
>{et.name}</span
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="font-semibold uppercase mb-1">Kategorie</div>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
<div class="m-0.5"><a href="#" class="hover:underline" on:click={() => $schedulePref.tracks = $bundle.spec.tracks.map(s => s.id)}>Všechny kategorie</a></div>
|
||||
<div class="m-0.5">
|
||||
<a
|
||||
href="#"
|
||||
class="hover:underline"
|
||||
on:click={() =>
|
||||
($schedulePref.tracks = $bundle.spec.tracks.map((s) => s.id))}
|
||||
>Všechny kategorie</a
|
||||
>
|
||||
</div>
|
||||
{#each $bundle.spec.tracks as et}
|
||||
<div class="u-choose-div m-0.5"><label class="cursor-pointer"><input type="checkbox" bind:group={$schedulePref.tracks} value={et.id} /></label> <span class="cursor-pointer" on:click={() => $schedulePref.tracks = [et.id]}> {et.shortname || et.name}</span></div>
|
||||
<div class="u-choose-div m-0.5">
|
||||
<label class="cursor-pointer"
|
||||
><input
|
||||
type="checkbox"
|
||||
bind:group={$schedulePref.tracks}
|
||||
value={et.id}
|
||||
/></label
|
||||
>
|
||||
<span
|
||||
class="cursor-pointer"
|
||||
on:click={() => ($schedulePref.tracks = [et.id])}
|
||||
>
|
||||
{et.shortname || et.name}</span
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#each scheduleTimes($bundle) as st}
|
||||
<div class="max-w-6xl mx-auto px-6 mb-4">
|
||||
<h2 class="uppercase text-xl font-bold">{format(new Date(st.date), 'iiii d.M.y', { locale: cs })}</h2>
|
||||
<h2 class="uppercase text-xl font-bold">
|
||||
{format(new Date(st.date), "iiii d.M.y", { locale: cs })}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<div class="mt-4 mb-10 overflow-scroll sm:overflow-clip">
|
||||
<table width="100%" class="table table-auto xl:table-fixed relative">
|
||||
<thead class="">
|
||||
<tr>
|
||||
<th class="xl:w-16"></th>
|
||||
<th class="xl:w-16" />
|
||||
{#each $bundle.spec.stages as stage}
|
||||
{#if $schedulePref && $schedulePref.stages.includes(stage.id)}
|
||||
<th class="text-md py-1.5 px-1 sticky top-0 bg-white">{stage.name}</th>
|
||||
<th class="text-md py-1.5 px-1 sticky top-0 bg-white"
|
||||
>{stage.name}</th
|
||||
>
|
||||
{/if}
|
||||
{/each}
|
||||
</tr>
|
||||
|
@ -203,22 +282,45 @@
|
|||
<tbody>
|
||||
{#each dateSlots(plan, st.period, $bundle, $schedulePref) as ds}
|
||||
<tr class="bg-gray-100">
|
||||
<th valign="top" class="w-auto pl-2 pr-2 pt-1 text-sm sticky left-0 bg-white" height="110">{ds.title}</th>
|
||||
<th
|
||||
valign="top"
|
||||
class="w-auto pl-2 pr-2 pt-1 text-sm sticky left-0 bg-white"
|
||||
height="110">{ds.title}</th
|
||||
>
|
||||
{#each $bundle.spec.stages as stage}
|
||||
{#if $schedulePref && $schedulePref.stages.includes(stage.id)}
|
||||
{#if ds.stages[stage.id] === undefined}
|
||||
<td></td>
|
||||
<td />
|
||||
{:else if ds.stages[stage.id] !== null}
|
||||
{#each [[ds.stages[stage.id], findEvent($bundle, ds.stages[stage.id].event)]] as [si, event]}
|
||||
<td class="text-sm h-full {event.color} {eventTrackClasses($bundle, event, $schedulePref.tracks)}" valign="top" rowspan={ds.stages[stage.id].span}>
|
||||
<td
|
||||
class="text-sm h-full {event.color} {eventTrackClasses(
|
||||
$bundle,
|
||||
event,
|
||||
$schedulePref.tracks
|
||||
)}"
|
||||
valign="top"
|
||||
rowspan={ds.stages[stage.id].span}
|
||||
>
|
||||
<div class="px-2 py-1 mb-1 mt-1">
|
||||
<div class="text-xs">{format(new Date(si.period.start), 'HH:mm')}-{format(new Date(si.period.end), 'HH:mm')} {#if event.track}[{#each [$bundle.spec.tracks.find(t => t.id === event.track)] as track}{track.shortname || track.name}{/each}]{/if}</div>
|
||||
<div class="font-semibold mt-1"><a href="/udalosti?id={event.id}">{event.name}</a></div>
|
||||
<div class="text-xs">
|
||||
{format(
|
||||
new Date(si.period.start),
|
||||
"HH:mm"
|
||||
)}-{format(new Date(si.period.end), "HH:mm")}
|
||||
{#if event.track}[{#each [$bundle.spec.tracks.find((t) => t.id === event.track)] as track}{track.shortname ||
|
||||
track.name}{/each}]{/if}
|
||||
</div>
|
||||
<div class="font-semibold mt-1">
|
||||
<a href="/udalosti?id={event.id}"
|
||||
>{event.name}</a
|
||||
>
|
||||
</div>
|
||||
<div class="text-xs mt-1">
|
||||
{@html showEventDetail($bundle, event)}
|
||||
</div>
|
||||
<div class="text-xs mt-2 text-blue-web/50">
|
||||
{event.tags.map(t => `#${t}`).join(', ')}
|
||||
{event.tags.map((t) => `#${t}`).join(", ")}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -21,7 +21,11 @@
|
|||
? [
|
||||
{ name: "Hlavní přednášející (" + leadSpeakersCount + ")", id: "top" },
|
||||
{ name: "Vše", id: null },
|
||||
].concat(currentBundle.spec.tracks.filter(t => t.hidden === undefined || t.hidden !== true))
|
||||
].concat(
|
||||
currentBundle.spec.tracks.filter(
|
||||
(t) => t.hidden === undefined || t.hidden !== true
|
||||
)
|
||||
)
|
||||
: null;
|
||||
|
||||
function changeTrack(tId) {
|
||||
|
@ -41,8 +45,8 @@
|
|||
});
|
||||
}
|
||||
|
||||
function statsIcon (ico = 'fa-regular fa-calendar') {
|
||||
return `<i class="${ico} inline-block align-baseline pr-1.5 sm:pr-2.5 text-blue-web/40"></i>`
|
||||
function statsIcon(ico = "fa-regular fa-calendar") {
|
||||
return `<i class="${ico} inline-block align-baseline pr-1.5 sm:pr-2.5 text-blue-web/40"></i>`;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -54,10 +58,21 @@
|
|||
{#if $apiStatus}
|
||||
<section class="bg-blue-web-light">
|
||||
<div class="pb-4 pt-4 lg:pt-4 lg:pb-4 mx-auto sm:px-2 lg:px-6 2xl:px-16">
|
||||
<div class="text-center text-xl sm:text-xl lg:text-2xl text-blue-web px-8 flex flex-wrap gap-4 lg:gap-8 justify-center">
|
||||
<div>{@html statsIcon('fa-solid fa-user-check')} <b>{$bundle.spec.speakers.length}</b> přednášejících</div>
|
||||
<div>{@html statsIcon('fa-regular fa-calendar')} <b>{$bundle.spec.events.length}</b> událostí</div>
|
||||
<div>{@html statsIcon('fa-solid fa-users')} <b>{$apiStatus.global.tickets}</b> návstěvníků</div>
|
||||
<div
|
||||
class="text-center text-xl sm:text-xl lg:text-2xl text-blue-web px-8 flex flex-wrap gap-4 lg:gap-8 justify-center"
|
||||
>
|
||||
<div>
|
||||
{@html statsIcon("fa-solid fa-user-check")}
|
||||
<b>{$bundle.spec.speakers.length}</b> přednášejících
|
||||
</div>
|
||||
<div>
|
||||
{@html statsIcon("fa-regular fa-calendar")}
|
||||
<b>{$bundle.spec.events.length}</b> událostí
|
||||
</div>
|
||||
<div>
|
||||
{@html statsIcon("fa-solid fa-users")}
|
||||
<b>{$apiStatus.global.tickets}</b> návstěvníků
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -94,7 +94,10 @@
|
|||
<div class="flex gap-2">
|
||||
<Avatar speaker={s} size="semi-small" />
|
||||
<div class="m-auto">
|
||||
<a href="/lide?id={s.id}" class="text-xl">{s.name} {#if s.nickname} ({s.nickname}){/if}</a>
|
||||
<a href="/lide?id={s.id}" class="text-xl"
|
||||
>{s.name}
|
||||
{#if s.nickname} ({s.nickname}){/if}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
Načítá se…
Odkázat v novém úkolu