Skip to content
Snippets Groups Projects
Verified Commit 9f7e54b7 authored by Miniontoby's avatar Miniontoby :writing_hand_tone1:
Browse files

Added more routes

parent c418dcf8
Branches
Tags
No related merge requests found
Showing
with 360 additions and 19 deletions
import fs from 'node:fs/promises';
import OriginalSong from 'plaintext-song-parser';
export class Song extends OriginalSong {
static async loadFromFile(filepath: string): Promise<Song> {
const content = await fs.readText(filepath, { encoding: 'utf8' });
return new Song(content);
}
}
export default Song;
\ No newline at end of file
......@@ -18,7 +18,7 @@
export let value = '';
const onInput = (/** @type {any} */ e) => {
value = e.target.value;
//value = e.target.value;
}
</script>
<div>
......@@ -26,6 +26,17 @@
<label for={id} class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{labelText}</label>
{/if}
{#if type === 'textarea'}
<textarea
name={id}
id={id}
class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 fr-remove-flowbite-border h-full"
placeholder={placeholder}
required={required}
on:input={onInput}
value={value}
></textarea>
{:else}
<input
type={type}
name={id}
......@@ -36,6 +47,7 @@
on:input={onInput}
value={value}
>
{/if}
</div>
<style>
.fr-remove-flowbite-border:focus {
......
......@@ -104,9 +104,10 @@ export async function create(teamId: number, name: string, date: Date) {
* @param id
* @param name
* @param date
* @param songs
* @returns { object } edited playlist
*/
export async function edit(id: number, name: string, date: Date) {
export async function edit(id: number, name: string, date: Date, songs: string[] = []) {
return await db.playlist.update({
where: {
id,
......@@ -114,6 +115,9 @@ export async function edit(id: number, name: string, date: Date) {
data: {
name,
date: date.toISOString(),
songs: {
connect: songs
},
}
})
}
......
......@@ -71,7 +71,7 @@ export async function findByName(name: string) {
name: true,
lyrics: true,
sheets: true,
team: true,
// team: true,
}
})
}
......@@ -79,15 +79,13 @@ export async function findByName(name: string) {
/**
* Create song
*
* @param teamId
* @param name
* @param description
* @returns { object } created song
*/
export async function create(teamId: number, name: string, lyrics: string|undefined = undefined) {
export async function create(name: string, lyrics: string|undefined = undefined) {
return await db.song.create({
data: {
teamId,
name,
lyrics,
}
......
......@@ -4,9 +4,9 @@
import { enhance, applyAction } from '$app/forms';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { getContext } from 'svelte';
import { slide } from 'svelte/transition';
import { _ } from 'svelte-i18n';
import { getContext } from 'svelte';
/** @type {import('./$types').PageData} */
export let data;
......@@ -23,7 +23,7 @@
<h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white mb-6">{$_('page.users.list.title')}</h1>
<p>Here is a list of all the users</p>
{#if (authUser.roleId <= Roles.Admin)}
<button class="text-white bg-green-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-2.5 py-1.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" on:click={() => goto(location?.href + '/create', { replaceState: false })}>Add User</button>
<button class="text-white bg-green-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-2.5 py-1.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" on:click={() => goto(location?.pathname + '/create', { replaceState: false })}>Add User</button>
{/if}
<table>
<tr>
......
import * as Playlist from '$lib/server/models/Playlist';
import * as Song from '$lib/server/models/Song';
import { error, fail } from '@sveltejs/kit';
import { parseAndCheckFormData } from '$lib/utils';
/** @type {import('./$types').PageServerLoad} */
export async function load({ params, parent }) {
const playlist = await Playlist.findById(Number(params.id));
const songs = (await Song.getAll()) ?? [];
if (!playlist) throw error(404);
if (!playlist.songs) playlist.songs = [];
return {
...await parent(),
playlist,
songs,
};
}
/** @type {import('./$types').Actions} */
export const actions = {
edit: async ({ request, params }) => {
const data = await request.formData();
const formData = parseAndCheckFormData(data, [
{ name: 'name', required: true, type: 'string' },
{ name: 'date', required: true, type: 'date' },
{ name: 'songs', required: true, type: 'string' },
]);
if (!formData.success) return formData.data;
const { name, date } = formData.data;
const songs = JSON.parse(formData.data.songs);
const playlist = await Playlist.findById(Number(params.id));
if (playlist.name !== name && await Playlist.findByName(name))
return fail(400, { unique: true });
const teamId = 1;
const playlistNew = await Playlist.edit(teamId, name, date, songs);
return { success: true, playlist: playlistNew };
}
}
<script>
import TextField from "$lib/components/inputs/TextField.svelte";
import Error from "$lib/components/alerts/Error.svelte";
import Success from "$lib/components/alerts/Success.svelte";
import { enhance, applyAction } from '$app/forms';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { slide } from 'svelte/transition';
import { _ } from 'svelte-i18n';
import { dndzone } from "svelte-dnd-action";
import { getContext } from 'svelte';
/** @type {import('./$types').PageData} */
export let data;
$: playlist = data.playlist;
$: songs = new Array(...data.songs).filter(i => !playlist.songs.includes(i.id));
/** @type {import('./$types').ActionData} */
$: form = $page.form;
/** @type {string} */ $: name = playlist?.name;
/** @type {string} */ $: date = playlist?.date?.toJSON()?.split('T')[0];
/** @type {string} */ $: songsString = JSON.stringify(playlist?.songs.map(s=>{ return { id: s.id }; }));
let working = false;
/** @type {import('svelte/store').Writable<string>} */
getContext('title').set('playlists.info');
function handleDndConsiderSongs(e) {
songs = e.detail.items;
}
function handleDndFinalizeSongs(e) {
songs = e.detail.items;
}
function handleDndConsiderPlaylist(e) {
playlist.songs = e.detail.items;
}
function handleDndFinalizePlaylist(e) {
playlist.songs = e.detail.items;
}
</script>
<div transition:slide>
<h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white mb-6">Edit {$_('page.playlists.info.title')}</h1>
{#if !working && form?.success}
<div class="mt-4" transition:slide>
<Success message={$_('messages.successfully.updated')} />
</div>
{/if}
{#if !working && form?.success === false}
<div class="mt-4" transition:slide>
<Error message={$_('messages.not_successfully.updated')} />
</div>
{/if}
{#if !working && form?.required}
<div class="mt-4" transition:slide>
<Error message={$_('messages.required.all')} />
</div>
{/if}
{#if !working && form?.unique}
<div class="mt-4" transition:slide>
<Error message={$_('messages.unique.specific', { values: { attribute: 'name' } })} />
</div>
{/if}
<form class="space-y-4 md:space-y-6" action="?/edit" method="POST" use:enhance={() => { if (!working) { working = true; return async ({ result }) => { working = false; await applyAction(result); if (result.type === 'redirect') { goto(result.location); } }; } }}>
<TextField
id="name"
type="text"
placeholder={$_('fields.playlist.name.placeholder')}
label={true}
labelText={$_('fields.playlist.name.label')}
required={true}
bind:value={name}
/>
<TextField
id="date"
type="date"
placeholder={$_('fields.playlist.date.placeholder')}
label={true}
labelText={$_('fields.playlist.date.label')}
required={true}
bind:value={date}
/>
<hr />
<h2 class="text-2xl font-bold">{$_('page.songs.list.title')}</h2>
<h1>Adding and removing songs</h1>
<h2>To add a song, drag one from the left container to the right</h2>
<div class="flex">
<ol class="dropbox grey" use:dndzone="{{ items: songs }}" on:consider="{handleDndConsiderSongs}" on:finalize="{handleDndFinalizeSongs}">
{#each songs as song(song.id)}
<li>{song.name}</li>
{/each}
</ol>
<ol class="dropbox green" use:dndzone="{{ items: playlist?.songs }}" on:consider="{handleDndConsiderPlaylist}" on:finalize="{handleDndFinalizePlaylist}">
{#each playlist?.songs as song(song.id)}
<li><a href={"/songs/" + song.id}>{song.name}</a></li>
{/each}
</ol>
</div>
<input id="songs" name="songs" type="text" hidden bind:value={songsString} />
<button class="w-full text-white bg-green-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" disabled={working}>{$_('actions.edit')}</button>
</form>
</div>
<style>
.flex {
width: 96%;
gap: 10px;
}
.dropbox {
width: 100%;
min-height: 200px;
}
.dropbox.grey {
background-color: grey;
}
.dropbox.green {
background-color: green;
}
</style>
\ No newline at end of file
import * as Song from '$lib/server/models/Song';
import { error, fail } from '@sveltejs/kit';
import { parseAndCheckFormData } from '$lib/utils';
/** @type {import('./$types').PageServerLoad} */
export async function load({ params, parent }) {
const song = await Song.findById(Number(params.id));
if (!song) throw error(404);
return {
...await parent(),
song,
};
}
/** @type {import('./$types').Actions} */
export const actions = {
edit: async ({ request, params }) => {
const data = await request.formData();
const formData = parseAndCheckFormData(data, [
{ name: 'name', required: true, type: 'string' },
{ name: 'lyrics', required: true, type: 'string' },
]);
if (!formData.success) return formData.data;
const { name, lyrics } = formData.data;
const song = await Song.findById(Number(params.id));
if (song.name !== name && await Song.findByName(name))
return fail(400, { unique: true });
const songNew = await Song.edit(song.id, name, lyrics);
return { success: true, song: songNew };
}
}
<script>
import TextField from "$lib/components/inputs/TextField.svelte";
import Error from "$lib/components/alerts/Error.svelte";
import Success from "$lib/components/alerts/Success.svelte";
import { enhance, applyAction } from '$app/forms';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { slide } from 'svelte/transition';
import { _ } from 'svelte-i18n';
import { getContext } from 'svelte';
/** @type {import('./$types').PageData} */
export let data;
$: song = data.song;
/** @type {import('./$types').ActionData} */
$: form = $page.form;
/** @type {string} */ $: name = song?.name;
/** @type {string} */ $: lyrics = song?.lyrics;
let working = false;
/** @type {import('svelte/store').Writable<string>} */
getContext('title').set('songs.info');
</script>
<div transition:slide>
<h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white mb-6">Edit {$_('page.songs.info.title')}</h1>
{#if !working && form?.success}
<div class="mt-4" transition:slide>
<Success message={$_('messages.successfully.updated')} />
</div>
{/if}
{#if !working && form?.success === false}
<div class="mt-4" transition:slide>
<Error message={$_('messages.not_successfully.updated')} />
</div>
{/if}
{#if !working && form?.required}
<div class="mt-4" transition:slide>
<Error message={$_('messages.required.all')} />
</div>
{/if}
{#if !working && form?.unique}
<div class="mt-4" transition:slide>
<Error message={$_('messages.unique.specific', { values: { attribute: 'name' } })} />
</div>
{/if}
<form class="space-y-4 md:space-y-6" action="?/edit" method="POST" use:enhance={() => { if (!working) { working = true; return async ({ result }) => { working = false; await applyAction(result); if (result.type === 'redirect') { goto(result.location); } }; } }}>
<TextField
id="name"
type="text"
placeholder={$_('fields.song.name.placeholder')}
label={true}
labelText={$_('fields.song.name.label')}
required={true}
bind:value={name}
/>
<TextField
id="lyrics"
type="textarea"
placeholder={$_('fields.song.lyrics.placeholder')}
label={true}
labelText={$_('fields.song.lyrics.label')}
required={true}
bind:value={lyrics}
/>
<button class="w-full text-white bg-green-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" disabled={working}>{$_('actions.save')}</button>
</form>
</div>
......@@ -7,10 +7,11 @@ export async function load({ parent }) {
/** @type {any} */
let songs = [];
const user = await User.findById(data.authUser.id);
for (const team of user.teams) {
const tsongs = await Song.getAllByTeam(team.id);
songs = [...songs, ...tsongs];
}
songs = await Song.getAll();
//for (const team of user.teams) {
// const tsongs = await Song.getAllByTeam(team.id);
// songs = [...songs, ...tsongs];
//}
return {
...data,
......
......@@ -14,7 +14,9 @@
<div transition:slide>
<h1 class="text-3xl font-bold">{$_('page.songs.list.title')}</h1>
<p>{$_('page.songs.list.description')}</p>
{#each songs as song}
<a href={$page.url.pathname + "/" + song.id}>{song.name}</a>
{/each}
<ol>
{#each songs as song}
<li><a href={$page.url.pathname + "/" + song.id}>{song.name}</a></li>
{/each}
</ol>
</div>
......@@ -6,7 +6,7 @@ export async function load({ params, parent }) {
const data = await parent();
const song = await Song.findById(Number(params.id));
if (!song || !song.team.users.include(data.authUser.id)) throw error(404);
// if (!song || !song.team?.users?.include(data.authUser.id)) throw error(404);
return {
...data,
......
<script>
import { getContext } from 'svelte';
import { goto } from '$app/navigation';
import { slide } from 'svelte/transition';
import { _ } from 'svelte-i18n';
import { Song } from '$lib/Song';
import { getContext } from 'svelte';
/** @type {import('./$types').PageData} */
export let data;
$: song = data.song;
$: songObject = song && new Song(song.lyrics, song.id);
/** @type {import('svelte/store').Writable<string>} */
getContext('title').set('songs.info');
</script>
<div transition:slide>
<h1 class="text-2xl font-bold">{$_('page.songs.info.title')}</h1>
{song.name}
{song.lyrics}
{#if data.authUser}
<button class="text-white bg-green-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-2.5 py-1.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" on:click={() => goto(location?.pathname + '/edit', { replaceState: false })}>Edit</button>
{/if}
<p><strong>Name:</strong> {song.name}</p>
<br>
<h3 class="text-xl font-bold">Lyrics:</h3>
<div class="song_container">
{#each songObject.couplets as couplet, i}
<div class="couplet">
{#each couplet as text, j}<pre class="songtext">{text}</pre>{#if j < couplet.length - 1}<br>{/if}{/each}
</div>
{#if i < songObject.couplets.length - 1}<br>{/if}
{/each}
</div>
</div>
<style>
.song_container {
width: max-content;
background-color: grey;
}
.couplet {
background-color: green;
padding: 10px;
}
.songtext {
width: 100%;
max-inline-size: 100%;
background-color: blue;
padding: 0px 5px;
}
</style>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment