commit 236d8fe42552a48d6df8111ace06716ec493f31b
parent ab1eaf62053e72c356247d38d0b06a7324e6496d
Author: mokou <mokou@posteo.de>
Date: Thu, 1 Oct 2020 20:52:32 +0200
feat: Rename event, add half of times
Diffstat:
4 files changed, 321 insertions(+), 158 deletions(-)
diff --git a/src/App.svelte b/src/App.svelte
@@ -2,6 +2,8 @@
import navaid from 'navaid'
import { is24Hrs, isDarkMode } from './stores'
import Index from './pages/Index.svelte'
+ import CreateEvent from './pages/CreateEvent.svelte'
+ import CreateTimes from './pages/CreateTimes.svelte'
import Event from './pages/Event.svelte'
import CreatePoll from './pages/CreatePoll.svelte'
import Poll from './pages/Poll.svelte'
@@ -12,6 +14,8 @@
let routeParams
router.on('/', setRoute(Index))
+ router.on('/event', setRoute(CreateEvent))
+ router.on('/times', setRoute(CreateTimes))
router.on('/poll', setRoute(CreatePoll))
router.on('/updates', setRoute(Log))
router.on('/p/:id', setRoute(Poll))
diff --git a/src/pages/CreateEvent.svelte b/src/pages/CreateEvent.svelte
@@ -0,0 +1,153 @@
+<script>
+ import autocomplete from 'accessible-autocomplete'
+ import search from 'fuzzysearch'
+ import day from 'dayjs'
+ import utc from 'dayjs/plugin/utc'
+ import isOnline from 'is-online'
+ import { nanoid } from 'nanoid'
+ import { onMount } from 'svelte'
+ import { isDarkMode } from '../stores'
+ import { pouch, replicateToCouch } from '../couch'
+ import DateTimeGrid from '../components/DateTimeGrid.svelte'
+ day.extend(utc)
+
+ document.title = 'Create Event - Koro'
+ let submitting = false
+ let success = false
+ let errorFlash = ''
+ let durationInput = null
+ let durationsList = [
+ '15 minutes',
+ '30 minutes',
+ '45 minutes',
+ '1 hour',
+ '2 hours',
+ '3 hours',
+ '4 hours',
+ '5 hours'
+ ]
+ let event = {
+ _id: nanoid(),
+ name: '',
+ duration: '',
+ times: []
+ }
+ let eventLink = ''
+
+ async function submitForm() {
+ errorFlash = ''
+ submitting = true
+ if (!event.name) {
+ errorFlash = 'Please set a name for your event!'
+ submitting = false
+ return
+ }
+
+ if (!event.duration) {
+ errorFlash = 'Please set a duration for your event!'
+ submitting = false
+ return
+ }
+
+ if (!durationsList.includes(event.duration)) {
+ errorFlash = 'The duration must be picked from the provided list!'
+ submitting = false
+ return
+ }
+
+ if (event.times.length === 0) {
+ errorFlash = 'Please pick at least one event time!'
+ submitting = false
+ return
+ }
+
+ const db = await pouch()
+ try {
+ const res = await db.put({
+ times: event.times.map((e) => day(e).utc().format()),
+ ...event
+ })
+ await replicateToCouch()
+ success = true
+ eventLink = `https://koro.moe/${res.id}`
+ } catch (e) {
+ console.error(e)
+ }
+ }
+
+ function durationSource(query, cb) {
+ if (durationsList.includes(query)) cb([])
+ const filtered = durationsList.filter((d) => search(query, d))
+ cb(filtered)
+ }
+
+ onMount(async () => {
+ if (!(await isOnline())) {
+ errorFlash = `You're offline! This means you can still create an event, but you'll have to be online before you can share the link with others.`
+ }
+ autocomplete({
+ element: document.querySelector('#duration-mount'),
+ id: 'duration',
+ source: durationSource,
+ confirmOnBlur: false,
+ onConfirm: (opt) => {
+ event.duration = opt
+ }
+ })
+ })
+</script>
+
+<h1 class="text-5xl font-serif font-extrabold">Create Event</h1>
+{#if success}
+ <p class="text-2xl font-medium max-w-xl leading-normal">
+ Your event has successfully been created! You can share the link below with
+ anyone you want to invite, and they can set their available times from the
+ ones you picked out. Don't forget to fill it out yourself!
+ </p>
+
+ <div
+ class="text-2xl font-mono bg-black text-white max-w-xl w-full px-3 py-2 mt-5
+ text-center">
+ {eventLink}
+ </div>
+{:else}
+ <form class="max-w-xl mt-6" on:submit|preventDefault={submitForm}>
+ {#if errorFlash}
+ <div
+ class="w-full bg-red-100 px-3 py-2 border rounded border-red-400
+ text-red-600 my-5">
+ {errorFlash}
+ </div>
+ {/if}
+ <label
+ for="name"
+ class="font-bold text-gray-300"
+ class:text-gray-600={!$isDarkMode}>
+ Your event's name
+ </label>
+ <input
+ id="name"
+ type="text"
+ bind:value={event.name}
+ class:dark={$isDarkMode}
+ placeholder="Raid with the boys" />
+ <label
+ for="duration"
+ class="font-bold text-gray-300 inline-block mt-4"
+ class:text-gray-600={!$isDarkMode}>
+ Duration of the event
+ </label>
+ <div id="duration-mount" class="mb-5" class:dark={$isDarkMode} />
+ <label class="font-bold text-gray-300" class:text-gray-600={!$isDarkMode}>
+ Event start times
+ </label>
+ </form>
+ <DateTimeGrid startDate={new Date()} bind:selected={event.times} />
+ <button
+ class="btn mt-5"
+ type="submit"
+ on:click|preventDefault={submitForm}
+ disabled={submitting}>
+ Create event
+ </button>
+{/if}
diff --git a/src/pages/CreateTimes.svelte b/src/pages/CreateTimes.svelte
@@ -0,0 +1,151 @@
+<script>
+ import autocomplete from 'accessible-autocomplete'
+ import search from 'fuzzysearch'
+ import { nanoid } from 'nanoid'
+ import isOnline from 'is-online'
+ import { rawTimeZones } from '@vvo/tzdb'
+ import day from 'dayjs'
+ import customParseFormat from 'dayjs/plugin/customParseFormat'
+ import { onMount } from 'svelte'
+ import { pouch, replicateToCouch } from '../couch'
+ import { isDarkMode, is24Hrs } from '../stores'
+
+ document.title = 'Create Times - Koro'
+ day.extend(customParseFormat)
+ let times = {
+ _id: `t:${nanoid()}`,
+ times: [
+
+ ]
+ }
+ let currentTime = {
+ time: null,
+ timezone: ''
+ }
+
+ let timeInput = ''
+ let timezoneInput = ''
+ let errorFlash = ''
+ let success = false
+ let isValidTime = false
+ const tzs = rawTimeZones
+ const tzlist = tzs.map(tz => {
+ return { str: `${tz.name} (${tz.abbreviation})`, name: tz.name }
+ })
+
+ function parseTime (evt) {
+ const time = evt.target.value
+ const match = day(time, ['YYYY MM DD HH:mm', 'MM DD HH:mm', 'YYYY MM DD hh:mm', 'MM DD hh:mm'], true)
+ if (match.isValid()) {
+ currentTime.time = match.toISOString()
+ isValidTime = true
+ } else {
+ isValidTime = false
+ }
+ }
+
+ function addTime () {
+ const copiedCurrentTime = Object.assign({}, currentTime)
+ times.times = [...times.times, copiedCurrentTime]
+ }
+
+ function removeTime (i) {
+ return () => {
+ times.times.splice(i, 1)
+ times = times
+ }
+ }
+
+ function submitForm () {
+
+ }
+
+ function timezoneSource (query, cb) {
+ if (tzlist.includes(query)) db([])
+ const filtered = tzlist.filter(d => search(query.toLowerCase(), d.str.toLowerCase()))
+ cb(filtered)
+ }
+
+ onMount(async () => {
+ if (!(await isOnline())) {
+ errorFlash = `You're offline! This means you can still create times, but you'll have to be online before you can share the link with others.`
+ }
+
+ autocomplete({
+ element: document.querySelector('#timezone-mount'),
+ id: 'timezone',
+ source: timezoneSource,
+ confirmOnBlur: false,
+ onConfirm: (opt) => {
+ currentTime.timezone = opt.name
+ },
+ templates: {
+ inputValue (opt) {
+ return opt && opt.str
+ },
+ suggestion (s) {
+ return s.str
+ }
+ }
+ })
+ })
+</script>
+
+
+<h1 class="text-5xl font-serif font-extrabold">Create Times</h1>
+{#if success}
+ blah
+{:else}
+ <form class="max-w-xl mt-6" on:submit|preventDefault={submitForm}>
+ {#if errorFlash}
+ <div
+ class="w-full bg-red-100 px-3 py-2 border rounded border-red-400
+ text-red-600 my-5">
+ {errorFlash}
+ </div>
+ {/if}
+
+ <label
+ for="time"
+ class="font-bold text-gray-300 inline-block mt-4"
+ class:text-gray-600={!$isDarkMode}>
+ Time and Date
+ </label>
+ <input
+ id="time"
+ on:keyup={parseTime}
+ class:dark={$isDarkMode}
+ placeholder="(YYYY) MM DD hh:mm"
+ type="text">
+ <div class:text-red-400={!isValidTime} class:text-green-400={isValidTime}>
+ {#if isValidTime}
+ Looks good!
+ {:else}
+ Check that you entered the date correctly!
+ {/if}
+ </div>
+
+ <label
+ for="timezone"
+ class="font-bold text-gray-300 inline-block mt-4"
+ class:text-gray-600={!$isDarkMode}>
+ Source Timezone (type to see options)
+ </label>
+ <div id="timezone-mount" class="mb-5" class:dark={$isDarkMode} />
+ <button
+ class="btn mt-5"
+ on:click|preventDefault={addTime}
+ type="submit">
+ Add time
+ </button>
+ </form>
+
+ <ul class="list-disc my-3">
+ {#each times.times as time, i}
+ <li>
+ <span class="hover:cursor-pointer" on:click={removeTime(i)}>❌</span>
+ <span class="font-bold">{day(time.time).format(`DD/MM/YYYY ${$is24Hrs ? 'HH:mm' : 'hh:mma'}`)}</span> in <span class="font-bold">{tzs.find(tz => tz.name === time.timezone).abbreviation}</span>
+ </li>
+ {/each}
+ </ul>
+{/if}
diff --git a/src/pages/Index.svelte b/src/pages/Index.svelte
@@ -1,164 +1,19 @@
<script>
- import autocomplete from 'accessible-autocomplete'
- import search from 'fuzzysearch'
- import day from 'dayjs'
- import utc from 'dayjs/plugin/utc'
- import isOnline from 'is-online'
- import { nanoid } from 'nanoid'
- import { onMount } from 'svelte'
import { isDarkMode } from '../stores'
- import { pouch, replicateToCouch } from '../couch'
- import DateTimeGrid from '../components/DateTimeGrid.svelte'
- day.extend(utc)
document.title = 'Koro'
- let submitting = false
- let success = false
- let errorFlash = ''
- let durationInput = null
- let durationsList = [
- '15 minutes',
- '30 minutes',
- '45 minutes',
- '1 hour',
- '2 hours',
- '3 hours',
- '4 hours',
- '5 hours'
- ]
- let event = {
- _id: nanoid(),
- name: '',
- duration: '',
- times: []
- }
- let eventLink = ''
-
- async function submitForm() {
- errorFlash = ''
- submitting = true
- if (!event.name) {
- errorFlash = 'Please set a name for your event!'
- submitting = false
- return
- }
-
- if (!event.duration) {
- errorFlash = 'Please set a duration for your event!'
- submitting = false
- return
- }
-
- if (!durationsList.includes(event.duration)) {
- errorFlash = 'The duration must be picked from the provided list!'
- submitting = false
- return
- }
-
- if (event.times.length === 0) {
- errorFlash = 'Please pick at least one event time!'
- submitting = false
- return
- }
-
- const db = await pouch()
- try {
- const res = await db.put({
- times: event.times.map((e) => day(e).utc().format()),
- ...event
- })
- await replicateToCouch()
- success = true
- eventLink = `https://koro.moe/${res.id}`
- } catch (e) {
- console.error(e)
- }
- }
-
- function durationSource(query, cb) {
- if (durationsList.includes(query)) cb([])
- const filtered = durationsList.filter((d) => search(query, d))
- cb(filtered)
- }
-
- onMount(async () => {
- if (!(await isOnline())) {
- errorFlash = `You're offline! This means you can still create an event, but you'll have to be online before you can share the link with others.`
- }
- autocomplete({
- element: document.querySelector('#duration-mount'),
- id: 'duration',
- source: durationSource,
- confirmOnBlur: false,
- onConfirm: (opt) => {
- event.duration = opt
- }
- })
- })
</script>
-<h1 class="text-5xl font-serif font-extrabold">koro</h1>
-{#if success}
- <p class="text-2xl font-medium max-w-xl leading-normal">
- Your event has successfully been created! You can share the link below with
- anyone you want to invite, and they can set their available times from the
- ones you picked out. Don't forget to fill it out yourself!
- </p>
-
- <div
- class="text-2xl font-mono bg-black text-white max-w-xl w-full px-3 py-2 mt-5
- text-center">
- {eventLink}
- </div>
-{:else}
- <p class="text-2xl font-medium max-w-xl leading-normal">
- An event time planning site for the rest of us. Use this to find a timeslot
- for your next meeting, party, raid, whatever you want. It automatically
- supports the user's timezone and works locally, too.
- </p>
-
- <p class="text-2xl my-4 font-medium max-w-xl leading-normal">
- You can also create a
- <a class="text-purple-400" href="/poll">regular poll</a>.
- </p>
-
- <form class="max-w-xl mt-6" on:submit|preventDefault={submitForm}>
- {#if errorFlash}
- <div
- class="w-full bg-red-100 px-3 py-2 border rounded border-red-400
- text-red-600 my-5">
- {errorFlash}
- </div>
- {/if}
- <label
- for="name"
- class="font-bold text-gray-300"
- class:text-gray-600={!$isDarkMode}>
- Your event's name
- </label>
- <input
- id="name"
- type="text"
- bind:value={event.name}
- class:dark={$isDarkMode}
- placeholder="Raid with the boys" />
- <label
- for="duration"
- class="font-bold text-gray-300 inline-block mt-4"
- class:text-gray-600={!$isDarkMode}>
- Duration of the event
- </label>
- <div id="duration-mount" class="mb-5" class:dark={$isDarkMode} />
- <label class="font-bold text-gray-300" class:text-gray-600={!$isDarkMode}>
- Event start times
- </label>
- </form>
- <DateTimeGrid startDate={new Date()} bind:selected={event.times} />
- <button
- class="btn mt-5"
- type="submit"
- on:click|preventDefault={submitForm}
- disabled={submitting}>
- Create event
- </button>
-{/if}
+<h1 class="text-5xl font-serif font-extrabold">Koro</h1>
+<p class="text-2xl font-medium max-w-xl leading-normal">
+ An event time planning site for the rest of us. You can plan event
+ times with as many people as you like, run polls, or simply display a bunch
+ of times in the user's timezone. It also (kind of) works when you're offline,
+ and it's free!
+</p>
+
+<ul class="list-disc text-2xl mt-4">
+ <li><a class="text-purple-400" href="/event">/event</a> - Flexibly schedule an event time with other people</li>
+ <li><a class="text-purple-400" href="/poll">/poll</a> - Create a poll</li>
+ <li><a class="text-purple-400" href="/times">/times</a> - Display times in the user's timezone</li>
+</ul>