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
No related branches found
No related tags found
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.
Finish editing this message first!
Please register or to comment