This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
identity/identity-web/src/routes/entry/new/+page.svelte
2024-06-30 22:05:46 +02:00

355 lines
12 KiB
Svelte

<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/. -->
<script lang="ts">
import { createForm } from 'felte';
import EntryKind from '../../dashboard/utils/EntryKind.svelte';
import {
FEELINGS,
TITLED_ENTRIES,
type AlbumEntry,
type IdlessEntry,
type KnownFeeling,
type SongEntry
} from '$lib/entry';
import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome';
import { faSpotify, faYoutube } from '@fortawesome/free-brands-svg-icons';
import { faChevronDown, faLink, faPlus, faXmark } from '@fortawesome/free-solid-svg-icons';
import FeelingPill from '../../dashboard/utils/FeelingPill.svelte';
import { addEntry, uploadAsset } from '$lib/api';
import { credentials, session_key } from '$lib/stores';
import FeelingsChooser from '$lib/components/FeelingsChooser.svelte';
credentials.subscribe(
(v) => v == null && setTimeout(() => (window.location.pathname = '/auth/login'), 200)
);
let kind: EntryKind | null;
const { form, errors } = createForm({
onSubmit: async (values) => {
let feelings = Object.keys(values)
.filter((v) => v.startsWith('feeling__'))
.map((v) => v.replaceAll('feeling__', '')) as KnownFeeling[];
let base;
if (values.kind === 'song' || values.kind === 'album') {
base = {
kind: values.kind,
artist: values.artist,
title: values.musicTitle,
link: [values.spotify, values.yt, values.otherProvider].filter(
(v) => v != null && v.length > 0
),
// FIXME: Infer Universal IDs (Spotify URL, etc)
id: []
};
} else if (values.kind === 'environment') {
base = {
kind: values.kind,
location:
values.location != null && values.location.length > 0 ? values.location : undefined
};
} else if (values.kind === 'date') {
base = {
kind: values.kind,
referencedDate: values.date
};
} else {
base = {
kind: values.kind
};
}
let asset_id;
if (values.asset != null && typeof values.asset === 'object') {
asset_id = await uploadAsset($session_key!, values.asset);
}
let entry: IdlessEntry = {
base,
creationDate: new Date().toISOString(),
assets: [asset_id].filter((v) => v != null) as string[],
feelings,
title: TITLED_ENTRIES.includes(values.kind) ? values.title : undefined,
description: values.description
};
await addEntry($credentials!, entry);
window.location.pathname = '/dashboard';
},
validate: (values) => {
let errors = {};
if (values.kind == null || values.kind.length === 0) {
errors['kind'] = 'Must choose an entry kind';
return errors;
}
if (values.kind === 'song' || values.kind === 'album') {
if (values.artist == null || values.artist.length === 0) {
errors['artist'] = 'Must not be empty';
}
if (values.musicTitle == null || values.musicTitle.length === 0) {
errors['musicTitle'] = 'Must not be empty';
}
if (
values.spotify.length === 0 &&
values.yt.length === 0 &&
values.otherProvider.length === 0 &&
(values.asset == null || typeof values.asset !== 'object')
) {
errors['links'] = 'You must add at least one link or upload an audio asset';
}
} else if (values.kind === 'date') {
if (values.date == null || values.date.length === 0) {
errors['date'] = 'Must choose a date';
}
} else if (values.kind === 'feeling') {
if (Object.keys(values).filter((v) => v.startsWith('feeling__')).length === 0) {
errors['feelings'] = 'Must choose at least one feeling';
}
} else {
if (values.title == null || values.title.length === 0) {
errors['title'] = 'Must not be empty';
}
}
return errors;
}
});
</script>
<div class="mt-3.5 flex justify-center">
<div class="flex w-[60%] flex-col">
<h1 class="pb-3.5 text-2xl">Add an entry</h1>
<form use:form>
<div class="mb-5">
<label for="add-entry__kind" class="mb-2 block text-sm font-medium text-gray-900">
Entry kind
</label>
<select
bind:value={kind}
id="add-entry__kind"
name="kind"
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-violet-500 focus:ring-violet-500"
>
<option value="" selected>Choose an entry kind</option>
<option value="song">Song</option>
<option value="album">Album</option>
<option value="event">Event</option>
<option value="memory">Memory</option>
<option value="feeling">Feeling</option>
<option value="environment">Environment</option>
<option value="date">Date</option>
</select>
{#if $errors.kind != null}
<p class="mt-2 text-sm text-red-600">
<span class="font-medium">{$errors.kind[0]}</span>
</p>
{/if}
</div>
{#if TITLED_ENTRIES.includes(kind)}
<div class="mb-5">
<label for="add-entry__title" class="mb-2 block text-sm font-medium text-gray-900">
Title
</label>
<input
id="add-entry__title"
type="text"
name="title"
placeholder="At the sunflower field with my friends"
class="text-greay-900 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm focus:border-violet-500 focus:ring-violet-500"
/>
{#if $errors.title != null}
<p class="mt-2 text-sm text-red-600">
<span class="font-medium">{$errors.title[0]}</span>
</p>
{/if}
</div>
{/if}
{#if ['song', 'album'].includes(kind)}
<div class="mb-5 flex flex-col gap-5 md:flex-row">
<div class="w-full">
<label for="add-entry__artist" class="mb-2 block text-sm font-medium text-gray-900">
Artist name
</label>
<input
id="add-entry__artist"
type="text"
name="artist"
placeholder="Claude Debussy"
class="text-greay-900 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm focus:border-violet-500 focus:ring-violet-500"
/>
{#if $errors.artist != null}
<p class="mt-2 text-sm text-red-600">
<span class="font-medium">{$errors.artist[0]}</span>
</p>
{/if}
</div>
<div class="w-full">
<label
for="add-entry__music-title"
class="mb-2 block text-sm font-medium text-gray-900"
>
<span class="capitalize">{kind}</span>
title
</label>
<input
id="add-entry__music-title"
type="text"
name="musicTitle"
placeholder="Clair de Lune"
class="text-greay-900 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm focus:border-violet-500 focus:ring-violet-500"
/>
{#if $errors.musicTitle != null}
<p class="mt-2 text-sm text-red-600">
<span class="font-medium">{$errors.musicTitle[0]}</span>
</p>
{/if}
</div>
</div>
<div class="mb-5">
<label for="add-entry__spotify" class="mb-2 block text-sm font-medium text-gray-900">
Spotify link
</label>
<div class="flex">
<span
class="rounded-e-0 inline-flex items-center rounded-s-md border border-e-0 border-gray-300 bg-gray-200 px-2.5 text-sm text-gray-900"
>
<FontAwesomeIcon size="lg" icon={faSpotify} />
</span>
<input
type="text"
id="add-entry__spotify"
name="spotify"
class="block w-full min-w-0 flex-1 rounded-none rounded-e-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-violet-500 focus:ring-violet-500"
placeholder={kind === 'song'
? 'https://open.spotify.com/track/...'
: 'https://open.spotify.com/album/...'}
/>
</div>
</div>
<div class="mb-5">
<label for="add-entry__yt" class="mb-2 block text-sm font-medium text-gray-900">
YouTube link
</label>
<div class="flex">
<span
class="rounded-e-0 inline-flex items-center rounded-s-md border border-e-0 border-gray-300 bg-gray-200 px-2.5 text-sm text-gray-900"
>
<FontAwesomeIcon size="lg" icon={faYoutube} />
</span>
<input
type="text"
id="add-entry__yt"
name="yt"
class="block w-full min-w-0 flex-1 rounded-none rounded-e-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-violet-500 focus:ring-violet-500"
placeholder="https://www.youtube.com/watch..."
/>
</div>
</div>
<div class="mb-5">
<label for="add-entry__other" class="mb-2 block text-sm font-medium text-gray-900">
Link to other provider
</label>
<div class="flex">
<span
class="rounded-e-0 inline-flex items-center rounded-s-md border border-e-0 border-gray-300 bg-gray-200 px-2.5 text-sm text-gray-900"
>
<FontAwesomeIcon size="lg" icon={faLink} />
</span>
<input
type="text"
name="otherProvider"
id="add-entry__other"
class="block w-full min-w-0 flex-1 rounded-none rounded-e-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-violet-500 focus:ring-violet-500"
placeholder="https://www.music.tld/play/..."
/>
</div>
</div>
{#if $errors.links != null}
<p class="mb-3.5 mt-2.5 text-sm text-red-600">
<span class="font-medium">{$errors.links[0]}</span>
</p>
{/if}
{:else if kind === 'environment'}
<div class="mb-5 w-full">
<label for="add-entry__location" class="mb-2 block text-sm font-medium text-gray-900">
Location
</label>
<input
id="add-entry__location"
type="text"
name="location"
placeholder="South of Almond Park"
class="text-greay-900 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm focus:border-violet-500 focus:ring-violet-500"
/>
</div>
{:else if kind === 'date'}
<div class="mb-5 w-full">
<label for="add-entry__date" class="mb-2 block text-sm font-medium text-gray-900">
Referenced date
</label>
<input
id="add-entry__date"
type="date"
name="date"
class="text-greay-900 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm focus:border-violet-500 focus:ring-violet-500"
/>
{#if $errors.date != null}
<p class="mt-2 text-sm text-red-600">
<span class="font-medium">{$errors.date[0]}</span>
</p>
{/if}
</div>
{/if}
{#if kind != null && kind.length > 0}
<div class="mb-5">
<label for="add-entry__description" class="mb-2 block text-sm font-medium text-gray-900">
Description
</label>
<textarea
name="description"
id="add-entry__description"
rows="7"
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-violet-500 focus:ring-violet-500"
placeholder="Write your thoughts here..."
></textarea>
</div>
<div class="mb-5">
<label for="add-entry__assets" class="mb-2 block text-sm font-medium text-gray-900">
Linked assets (max 5MB)
</label>
<input
name="asset"
id="add-entry__assets"
class="block w-full rounded-lg border border-gray-300 bg-gray-50 text-sm text-gray-900 file:me-4 file:border-0 file:border-gray-300 file:bg-gray-200 file:px-4 file:py-2.5 hover:cursor-pointer hover:file:bg-gray-300 focus:border-violet-500 focus:ring-violet-500"
type="file"
/>
</div>
<div class="mb-5">
<FeelingsChooser required={kind === 'feeling'} />
{#if $errors.feelings != null}
<p class="mt-1.5 text-sm text-red-600">
<span class="font-medium">{$errors.feelings[0]}</span>
</p>
{/if}
</div>
<button
class="focust:outline-none mt-2 rounded-lg bg-violet-700 px-5 py-2.5 text-center font-medium text-white hover:bg-violet-800 focus:ring-4 focus:ring-violet-300"
type="submit"
>
Add new entry
</button>
{/if}
</form>
</div>
</div>