Sanitize lib paths
This commit is contained in:
parent
df31c4dcd1
commit
e9a906fbfb
43 changed files with 68 additions and 66 deletions
76
ui/src/lib/components/ActionList.svelte
Normal file
76
ui/src/lib/components/ActionList.svelte
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<script>
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { actions } from '$lib/stores/actions';
|
||||
|
||||
export let flush = false;
|
||||
|
||||
export { className as class };
|
||||
let className = '';
|
||||
|
||||
let refreshInProgress = false;
|
||||
function refresh_actions() {
|
||||
refreshInProgress = true;
|
||||
actions.refresh().then(() => {
|
||||
refreshInProgress = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center" class:px-2={flush}>
|
||||
<h2>
|
||||
Actions
|
||||
</h2>
|
||||
<div>
|
||||
{#if !flush}
|
||||
<Button
|
||||
href="routines/actions"
|
||||
color="outline-info"
|
||||
size="sm"
|
||||
>
|
||||
<Icon name="pencil" />
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
color="outline-dark"
|
||||
size="sm"
|
||||
title="Rafraîchir la liste des actions"
|
||||
on:click={refresh_actions}
|
||||
disabled={refreshInProgress}
|
||||
>
|
||||
{#if !refreshInProgress}
|
||||
<Icon name="arrow-clockwise" />
|
||||
{:else}
|
||||
<Spinner color="dark" size="sm" />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group {className}" class:list-group-flush={flush}>
|
||||
{#if $actions.list}
|
||||
{#each $actions.list as action (action.id)}
|
||||
<a
|
||||
href="routines/actions/{action.id}"
|
||||
class="list-group-item list-group-item-action"
|
||||
class:active={$page.url.pathname.indexOf('/actions/') !== -1 && $page.params.aid == action.id}
|
||||
aria-current="true"
|
||||
>
|
||||
<span class:fw-bold={action.enabled}>{action.name}</span>
|
||||
</a>
|
||||
{/each}
|
||||
{:else}
|
||||
{#await actions.refresh()}
|
||||
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||
<Spinner color="primary" /> Chargement en cours…
|
||||
</div>
|
||||
{:then}
|
||||
test
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
53
ui/src/lib/components/AlarmExceptionList.svelte
Normal file
53
ui/src/lib/components/AlarmExceptionList.svelte
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<script>
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import DateRangeFormat from '$lib/components/DateRangeFormat.svelte';
|
||||
import { alarmsExceptions } from '$lib/stores/alarmexceptions';
|
||||
|
||||
export let flush = false;
|
||||
</script>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center" class:mx-2={flush}>
|
||||
<h2>
|
||||
Exceptions
|
||||
</h2>
|
||||
<Button
|
||||
href="alarms/exceptions/new"
|
||||
color="outline-primary"
|
||||
size="sm"
|
||||
class="float-end {($page.params.kind === 'exceptions' && $page.url.pathname.endsWith('/new'))?'active':''}"
|
||||
>
|
||||
<Icon name="plus-lg" />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{#if $alarmsExceptions.list !== null}
|
||||
{#if $alarmsExceptions.list.length}
|
||||
<div class="list-group" class:list-group-flush={flush}>
|
||||
{#each $alarmsExceptions.list as alarm (alarm.id)}
|
||||
<a
|
||||
href="alarms/exceptions/{alarm.id}"
|
||||
class="list-group-item list-group-item-action"
|
||||
class:active={$page.params.kind === "exceptions" && $page.params.aid === alarm.id}
|
||||
>
|
||||
Du <DateRangeFormat startDate={alarm._start()} endDate={alarm._end()} dateStyle="long" />
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="fst-italic">Pas d'exception programmée</p>
|
||||
{/if}
|
||||
{:else}
|
||||
{#await alarmsExceptions.refresh()}
|
||||
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||
<Spinner color="primary" /> Chargement en cours…
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
53
ui/src/lib/components/AlarmRepeatedList.svelte
Normal file
53
ui/src/lib/components/AlarmRepeatedList.svelte
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<script>
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { weekdayStr } from '$lib/alarmrepeated';
|
||||
import { alarmsRepeated } from '$lib/stores/alarmrepeated';
|
||||
|
||||
export let flush = false;
|
||||
</script>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center" class:mx-2={flush}>
|
||||
<h2>
|
||||
Réveils habituels
|
||||
</h2>
|
||||
<Button
|
||||
href="alarms/repeated/new"
|
||||
color="outline-primary"
|
||||
size="sm"
|
||||
class="float-end {($page.params.kind === 'repeated' && $page.url.pathname.endsWith('/new'))?'active':''}"
|
||||
>
|
||||
<Icon name="plus-lg" />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{#if $alarmsRepeated.list !== null}
|
||||
{#if $alarmsRepeated.list.length}
|
||||
<div class="list-group" class:list-group-flush={flush}>
|
||||
{#each $alarmsRepeated.list as alarm (alarm.id)}
|
||||
<a
|
||||
href="alarms/repeated/{alarm.id}"
|
||||
class="list-group-item list-group-item-action"
|
||||
class:active={$page.params.kind === "repeated" && $page.params.aid === alarm.id}
|
||||
>
|
||||
Les {weekdayStr(alarm.weekday)}s à {alarm.time}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="fst-italic">Pas de réveil habituel programmé</p>
|
||||
{/if}
|
||||
{:else}
|
||||
{#await alarmsRepeated.refresh()}
|
||||
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||
<Spinner color="primary" /> Chargement en cours…
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
53
ui/src/lib/components/AlarmSingleList.svelte
Normal file
53
ui/src/lib/components/AlarmSingleList.svelte
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<script>
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import DateFormat from '$lib/components/DateFormat.svelte';
|
||||
import { alarmsSingle } from '$lib/stores/alarmsingle';
|
||||
|
||||
export let flush = false;
|
||||
</script>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center" class:mx-2={flush}>
|
||||
<h2>
|
||||
Réveils manuels
|
||||
</h2>
|
||||
<Button
|
||||
href="alarms/single/new"
|
||||
color="outline-primary"
|
||||
size="sm"
|
||||
class="float-end {($page.params.kind === 'single' && $page.url.pathname.endsWith('/new'))?'active':''}"
|
||||
>
|
||||
<Icon name="plus-lg" />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{#if $alarmsSingle.list !== null}
|
||||
{#if $alarmsSingle.list.length}
|
||||
<div class="list-group" class:list-group-flush={flush}>
|
||||
{#each $alarmsSingle.list as alarm (alarm.id)}
|
||||
<a
|
||||
href="alarms/single/{alarm.id}"
|
||||
class="list-group-item list-group-item-action"
|
||||
class:active={$page.params.kind === "single" && $page.params.aid === alarm.id}
|
||||
>
|
||||
Le <DateFormat date={alarm.time} dateStyle="long" timeStyle="long" />
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="fst-italic">Pas de prochain réveil manuel programmé</p>
|
||||
{/if}
|
||||
{:else}
|
||||
{#await alarmsSingle.refresh()}
|
||||
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||
<Spinner color="primary" /> Chargement en cours…
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
62
ui/src/lib/components/CardRoutine.svelte
Normal file
62
ui/src/lib/components/CardRoutine.svelte
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<script>
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Col,
|
||||
Container,
|
||||
ListGroup,
|
||||
ListGroupItem,
|
||||
Row,
|
||||
Icon,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { actions_idx } from '$lib/stores/actions';
|
||||
|
||||
export let routine = {
|
||||
name: "Classique",
|
||||
steps: [],
|
||||
};
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Button
|
||||
color="outline-danger"
|
||||
size="sm"
|
||||
class="float-end ms-1"
|
||||
>
|
||||
<Icon name="trash" />
|
||||
</Button>
|
||||
<Button
|
||||
color="outline-info"
|
||||
size="sm"
|
||||
class="float-end ms-1"
|
||||
>
|
||||
<Icon name="pencil" />
|
||||
</Button>
|
||||
{routine.name}
|
||||
</CardHeader>
|
||||
{#if routine.steps}
|
||||
<ListGroup>
|
||||
{#each routine.steps as step}
|
||||
<ListGroupItem action>
|
||||
{#if $actions_idx && $actions_idx[step.action]}
|
||||
{$actions_idx[step.action].name}
|
||||
{:else}
|
||||
{step.action}
|
||||
{/if}
|
||||
<Badge class="float-end">
|
||||
{step.delay/60} min
|
||||
</Badge>
|
||||
</ListGroupItem>
|
||||
{/each}
|
||||
</ListGroup>
|
||||
{:else}
|
||||
<CardBody>
|
||||
Aucune action définie.
|
||||
</CardBody>
|
||||
{/if}
|
||||
</Card>
|
||||
27
ui/src/lib/components/CardStatAlarms.svelte
Normal file
27
ui/src/lib/components/CardStatAlarms.svelte
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<script>
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
ListGroup,
|
||||
ListGroupItem,
|
||||
Icon,
|
||||
} from 'sveltestrap';
|
||||
|
||||
export let awakingList = [
|
||||
{
|
||||
id: 1,
|
||||
date: new Date("2022-10-01T09:15:00.000Z"),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
Liste des réveils
|
||||
</CardHeader>
|
||||
<ListGroup>
|
||||
{#each awakingList as awaking (awaking.id)}
|
||||
<ListGroupItem>{awaking.date}</ListGroupItem>
|
||||
{/each}
|
||||
</ListGroup>
|
||||
</Card>
|
||||
34
ui/src/lib/components/CardStatRoutines.svelte
Normal file
34
ui/src/lib/components/CardStatRoutines.svelte
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<script>
|
||||
import {
|
||||
Badge,
|
||||
Card,
|
||||
CardHeader,
|
||||
ListGroup,
|
||||
ListGroupItem,
|
||||
Icon,
|
||||
} from 'sveltestrap';
|
||||
|
||||
export let routinesStats = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Classique",
|
||||
nb: 10,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
Routines favorites
|
||||
</CardHeader>
|
||||
<ListGroup numbered>
|
||||
{#each routinesStats as routine (routine.id)}
|
||||
<ListGroupItem>
|
||||
{routine.name}
|
||||
<Badge color="primary" class="float-end">
|
||||
{routine.nb}
|
||||
</Badge>
|
||||
</ListGroupItem>
|
||||
{/each}
|
||||
</ListGroup>
|
||||
</Card>
|
||||
19
ui/src/lib/components/CardStatTimeAwaking.svelte
Normal file
19
ui/src/lib/components/CardStatTimeAwaking.svelte
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<script>
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardBody,
|
||||
Icon,
|
||||
} from 'sveltestrap';
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
Temps moyen de éteindre le réveil
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p class="card-text">
|
||||
10 minutes
|
||||
</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
35
ui/src/lib/components/CycleCounter.svelte
Normal file
35
ui/src/lib/components/CycleCounter.svelte
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<script>
|
||||
import { createEventDispatcher, onMount, onDestroy } from 'svelte';
|
||||
|
||||
export let begins = null;
|
||||
export let ends;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let interval;
|
||||
onMount(() => {
|
||||
if (!begins) {
|
||||
interval = setInterval(() => {
|
||||
begins = new Date();
|
||||
if (begins > ends) {
|
||||
dispatch("reload")
|
||||
}
|
||||
}, 15000);
|
||||
begins = new Date();
|
||||
}
|
||||
});
|
||||
onDestroy(() => {
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
});
|
||||
|
||||
export { className as class };
|
||||
let className = 'text-muted';
|
||||
</script>
|
||||
|
||||
{#if begins && ends}
|
||||
<span class="{className}">
|
||||
(dans {Math.trunc((ends.getTime()-begins.getTime())/5400000)} cycles + {Math.trunc(((ends.getTime()-begins.getTime())%5400000)/60000)} min)
|
||||
</span>
|
||||
{/if}
|
||||
17
ui/src/lib/components/DateFormat.svelte
Normal file
17
ui/src/lib/components/DateFormat.svelte
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script>
|
||||
export let date;
|
||||
export let dateStyle;
|
||||
export let timeStyle;
|
||||
|
||||
function formatDate(input, dateStyle, timeStyle) {
|
||||
if (typeof input === 'string') {
|
||||
input = new Date(input);
|
||||
}
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle,
|
||||
timeStyle,
|
||||
}).format(input);
|
||||
}
|
||||
</script>
|
||||
|
||||
{formatDate(date, dateStyle, timeStyle)}
|
||||
18
ui/src/lib/components/DateRangeFormat.svelte
Normal file
18
ui/src/lib/components/DateRangeFormat.svelte
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<script>
|
||||
export let startDate;
|
||||
export let endDate;
|
||||
export let dateStyle;
|
||||
export let timeStyle;
|
||||
|
||||
function formatRange(startDate, endDate, dateStyle, timeStyle) {
|
||||
if (typeof input === 'string') {
|
||||
input = new Date(input);
|
||||
}
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle,
|
||||
timeStyle,
|
||||
}).formatRange(startDate, endDate);
|
||||
}
|
||||
</script>
|
||||
|
||||
{formatRange(startDate, endDate, dateStyle, timeStyle)}
|
||||
31
ui/src/lib/components/DateTimeInput.svelte
Normal file
31
ui/src/lib/components/DateTimeInput.svelte
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<script>
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import {
|
||||
Input,
|
||||
} from 'sveltestrap';
|
||||
|
||||
export let format = 'YYYY-MM-DD HH:mm';
|
||||
export let date = new Date();
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
|
||||
export let id = null;
|
||||
export let required = false;
|
||||
|
||||
let internal;
|
||||
|
||||
const input = (x) => (internal = dayjs(x).format(format));
|
||||
const output = (x) => {
|
||||
const d = dayjs(x, format).toDate();
|
||||
if (d) {
|
||||
date = d.toISOString();
|
||||
}
|
||||
};
|
||||
|
||||
$: input(date)
|
||||
$: output(internal)
|
||||
</script>
|
||||
|
||||
<Input type="datetime-local" class={className} id={id} {required} bind:value={internal} />
|
||||
96
ui/src/lib/components/GongsList.svelte
Normal file
96
ui/src/lib/components/GongsList.svelte
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { gongs } from '$lib/stores/gongs';
|
||||
|
||||
function chooseGong(gong) {
|
||||
gong.setDefault().then(() => {
|
||||
gongs.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
export let flush = false;
|
||||
export let edit = false;
|
||||
|
||||
export { className as class };
|
||||
let className = '';
|
||||
|
||||
let refreshInProgress = false;
|
||||
function refresh_gongs() {
|
||||
refreshInProgress = true;
|
||||
gongs.refresh().then(() => {
|
||||
refreshInProgress = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center" class:px-2={flush}>
|
||||
<h2>
|
||||
Gongs
|
||||
</h2>
|
||||
<div>
|
||||
{#if !edit}
|
||||
<Button
|
||||
href="musiks/gongs"
|
||||
color="outline-info"
|
||||
size="sm"
|
||||
>
|
||||
<Icon name="pencil" />
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
href="musiks/gongs/new"
|
||||
color="outline-primary"
|
||||
size="sm"
|
||||
>
|
||||
<Icon name="plus-lg" />
|
||||
</Button>
|
||||
<Button
|
||||
color="outline-dark"
|
||||
size="sm"
|
||||
title="Rafraîchir la liste des gongs"
|
||||
on:click={refresh_gongs}
|
||||
disabled={refreshInProgress}
|
||||
>
|
||||
{#if !refreshInProgress}
|
||||
<Icon name="arrow-clockwise" />
|
||||
{:else}
|
||||
<Spinner color="dark" size="sm" />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group {className}" class:list-group-flush={flush}>
|
||||
{#if $gongs.list}
|
||||
{#each $gongs.list as gong (gong.id)}
|
||||
<button
|
||||
type="button"
|
||||
class="list-group-item list-group-item-action"
|
||||
class:active={(edit && $page.url.pathname.indexOf('/gongs/') !== -1 && $page.params.gid == gong.id) || (!edit && gong.enabled)}
|
||||
aria-current="true"
|
||||
on:click={() => {
|
||||
if (edit) {
|
||||
goto('musiks/gongs/' + gong.id);
|
||||
} else {
|
||||
chooseGong(gong);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{gong.name}
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
{#await gongs.refresh()}
|
||||
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||
<Spinner color="primary" /> Chargement en cours…
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
99
ui/src/lib/components/Header.svelte
Normal file
99
ui/src/lib/components/Header.svelte
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<script>
|
||||
import { page } from '$app/stores'
|
||||
|
||||
import {
|
||||
Icon,
|
||||
Navbar,
|
||||
NavbarBrand,
|
||||
Nav,
|
||||
NavItem,
|
||||
NavLink,
|
||||
} from 'sveltestrap';
|
||||
|
||||
const version = fetch('api/version', {headers: {'Accept': 'application/json'}}).then((res) => res.json())
|
||||
|
||||
export let activemenu = "";
|
||||
$: {
|
||||
const path = $page.url.pathname.split("/");
|
||||
if (path.length > 1) {
|
||||
activemenu = path[1];
|
||||
}
|
||||
}
|
||||
export { className as class };
|
||||
let className = '';
|
||||
</script>
|
||||
|
||||
<Navbar container={false} class="{className} px-md-2" color="primary" dark expand="xs" style="overflow-x: auto">
|
||||
<NavbarBrand href="." class="d-none d-md-block" style="padding: 0; margin: -.5rem 0;">
|
||||
Réveil
|
||||
</NavbarBrand>
|
||||
<Nav navbar>
|
||||
<NavItem class="d-block d-md-none">
|
||||
<NavLink
|
||||
active={activemenu === ''}
|
||||
class="text-center"
|
||||
href="."
|
||||
>
|
||||
<Icon name="house-fill" /><br class="d-inline d-md-none">
|
||||
Accueil
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
active={activemenu === 'alarms'}
|
||||
class="text-center"
|
||||
href="alarms"
|
||||
>
|
||||
<Icon name="alarm-fill" /><br class="d-inline d-md-none">
|
||||
Réveils
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="musiks"
|
||||
class="text-center"
|
||||
active={activemenu === 'musiks'}
|
||||
>
|
||||
<Icon name="music-note-list" /><br class="d-inline d-md-none">
|
||||
Musiques
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="routines"
|
||||
class="text-center"
|
||||
active={activemenu === 'routines'}
|
||||
>
|
||||
<Icon name="activity" /><br class="d-inline d-md-none">
|
||||
Routines
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="history"
|
||||
class="text-center"
|
||||
active={activemenu === 'history'}
|
||||
>
|
||||
<Icon name="clipboard-pulse" /><br class="d-inline d-md-none">
|
||||
Historique
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="settings"
|
||||
class="text-center"
|
||||
active={activemenu === 'settings'}
|
||||
>
|
||||
<Icon name="gear-fill" /><br class="d-inline d-md-none">
|
||||
Paramètres
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
<Nav class="ms-auto text-light" navbar>
|
||||
<NavItem>
|
||||
{#await version then v}
|
||||
{v.version}
|
||||
{/await}
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</Navbar>
|
||||
32
ui/src/lib/components/MusiksLastPlayedList.svelte
Normal file
32
ui/src/lib/components/MusiksLastPlayedList.svelte
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<script>
|
||||
let musiks = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Hall Of Fame",
|
||||
artist: "The Script",
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Poker face",
|
||||
artist: "Lady Gaga",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Puisque tu m'aimes encore",
|
||||
artist: "Céline Dion",
|
||||
enabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
let tracks = [2,0,1];
|
||||
</script>
|
||||
|
||||
<h2>
|
||||
Dernières musiques jouées
|
||||
</h2>
|
||||
<ol class="list-group list-group-numbered">
|
||||
{#each tracks as track}
|
||||
<li class="list-group-item">{musiks[track].artist} – {musiks[track].title}</li>
|
||||
{/each}
|
||||
</ol>
|
||||
22
ui/src/lib/components/Toaster.svelte
Normal file
22
ui/src/lib/components/Toaster.svelte
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<script>
|
||||
import {
|
||||
Toast,
|
||||
ToastBody,
|
||||
ToastHeader,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { ToastsStore } from '$lib/stores/toasts';
|
||||
</script>
|
||||
|
||||
<div class="toast-container position-absolute top-0 end-0 p-3">
|
||||
{#each $ToastsStore.toasts as toast}
|
||||
<Toast>
|
||||
<ToastHeader toggle={toast.close} icon={toast.color}>
|
||||
{#if toast.title}{toast.title}{:else}Gustus{/if}
|
||||
</ToastHeader>
|
||||
<ToastBody>
|
||||
{toast.msg}
|
||||
</ToastBody>
|
||||
</Toast>
|
||||
{/each}
|
||||
</div>
|
||||
95
ui/src/lib/components/TrackList.svelte
Normal file
95
ui/src/lib/components/TrackList.svelte
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { tracks } from '$lib/stores/tracks';
|
||||
|
||||
export let flush = false;
|
||||
export let edit = false;
|
||||
|
||||
export { className as class };
|
||||
let className = '';
|
||||
|
||||
let refreshInProgress = false;
|
||||
function refresh_tracks() {
|
||||
refreshInProgress = true;
|
||||
tracks.refresh().then(() => {
|
||||
refreshInProgress = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center" class:px-2={flush}>
|
||||
<h2>
|
||||
Musiques {#if !flush}du réveil{/if}
|
||||
</h2>
|
||||
<div>
|
||||
{#if !edit}
|
||||
<Button
|
||||
href="musiks/tracks"
|
||||
color="outline-info"
|
||||
size="sm"
|
||||
>
|
||||
<Icon name="pencil" />
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
href="musiks/tracks/new"
|
||||
color="outline-primary"
|
||||
size="sm"
|
||||
>
|
||||
<Icon name="plus-lg" />
|
||||
</Button>
|
||||
<Button
|
||||
color="outline-dark"
|
||||
size="sm"
|
||||
title="Rafraîchir la liste des pistes"
|
||||
on:click={refresh_tracks}
|
||||
disabled={refreshInProgress}
|
||||
>
|
||||
{#if !refreshInProgress}
|
||||
<Icon name="arrow-clockwise" />
|
||||
{:else}
|
||||
<Spinner color="dark" size="sm" />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group {className}" class:list-group-flush={flush}>
|
||||
{#if $tracks.list}
|
||||
{#each $tracks.list as track (track.id)}
|
||||
<button
|
||||
type="button"
|
||||
class="list-group-item list-group-item-action"
|
||||
class:active={$page.url.pathname.indexOf('/tracks/') !== -1 && $page.params.tid == track.id}
|
||||
aria-current="true"
|
||||
on:click={() => {
|
||||
if (edit) {
|
||||
goto('musiks/tracks/' + track.id);
|
||||
} else {
|
||||
track = track.toggleEnable()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if !edit}
|
||||
<input class="form-check-input me-1" type="checkbox" checked={track.enabled}>
|
||||
{/if}
|
||||
<span class:fw-bold={!edit && track.enabled}>{track.name}</span>
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
{#await tracks.refresh()}
|
||||
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||
<Spinner color="primary" /> Chargement en cours…
|
||||
</div>
|
||||
{:then}
|
||||
test
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue