Initial commit

This commit is contained in:
marc
2025-03-09 20:00:54 +01:00
commit efd26ce19d
118 changed files with 78086 additions and 0 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 56 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,106 @@
const pdfjsLib = await import('./pdf/pdf.mjs');
const pdfjsWorker = await import('./pdf/pdf.worker.mjs');
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;
export function displayPdf(options) {
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
ctx = options.canvas.getContext('2d');
/**
* Get page info from document, resize canvas accordingly, and render page.
* @param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var canvasWidth = options.canvas.getBoundingClientRect().width,
pageWidth = page.getViewport({scale: 1.0}).width,
scale = canvasWidth/pageWidth,
viewport = page.getViewport({scale: scale});
options.canvas.height = viewport.height;
options.canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function() {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
// Update page counters
options.pageNum.textContent = num;
}
/**
* If another page rendering in progress, waits until the rendering is
* finised. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
/**
* Re-render page (e.g. on resize)
*/
function reRender() {
queueRenderPage(pageNum);
}
document.defaultView.addEventListener('resize', reRender)
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
options.prevPage.addEventListener('click', onPrevPage);
/**
* Displays next page.
*/
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
options.nextPage.addEventListener('click', onNextPage);
/**
* Asynchronously downloads PDF.
*/
pdfjsLib.getDocument(options.url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
options.pageCount.textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
});
}

View File

@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -0,0 +1,7 @@
<div id="content">
<div hx-get="{{ content }}"
hx-trigger="load"
hx-target="#content"
hx-swap="innerHTML">
</div>
</div>

View File

@@ -0,0 +1,10 @@
<div id="footer">
<hr class="h-px my-3 bg-beige border-0 ">
<div id="nota" class="flex items-center justify-center">
<div hx-post="/api/nota"
hx-trigger="load"
hx-target="#nota"
hx-swap="innerHTML">
</div>
</div>
</div>

View File

@@ -0,0 +1,5 @@
<div class="h-4/5 min-h-[400px] flex flex-col items-center justify-center">
<img src = "{{ url_for('static', path='img/folkugat.svg') }}" class="{% if animate %} opacity-0 animate-fade-in-one {% endif %} m-3" width="100" alt="Folkugat"/>
<h1 class="text-3xl sm:text-8xl text-beige m-3 {% if animate %} opacity-0 animate-fade-in-one {% endif %}">{{ page_title }}</h1>
<h2 class="text-center sm:text-3xl m-6 {% if animate %} opacity-0 animate-fade-in-two {% endif %}">Sessions de folk a Sant Cugat</h2>
</div>

View File

@@ -0,0 +1,22 @@
{% if session %}
<div class="bg-beige text-white mt-2 py-2 text-xl overflow-hidden">
<a href="/sessio/{{ session.id }}">
<div class="animate-marquee whitespace-nowrap">
{% for _ in (1, 2, 3) %}
<span class="inline-block mx-4">
Sessió en directe
</span>
<span class="inline-block mx-4">
</span>
<span class="inline-block mx-4">
Està sonant <i>Pasdoble de Muntanya</i>
</span>
<span class="inline-block mx-4">
</span>
{% endfor %}
</div>
</a>
</div>
{% endif %}

View File

@@ -0,0 +1,35 @@
<hr class="h-px bg-beige border-0">
<ul class="flex flex-col min-[450px]:flex-row items-center justify-center">
{% if menu_selected_id == Pages.Sessions %}
<li class="p-2 text-beige">
<button>
{% else %}
<li class="p-2">
<button hx-get="/api/content/sessions"
hx-replace-url="/sessions"
hx-target="#content">
{% endif %}
Sessions
</button>
</li>
{% if menu_selected_id == Pages.Temes %}
<li class="p-2 text-beige">
<button>
{% else %}
<li class="p-2">
<button hx-get="/api/content/temes"
hx-replace-url="/temes"
hx-target="#content">
{% endif %}
Temes
</button>
</li>
</ul>
<hr class="h-px bg-beige border-0">
<div hx-get="/api/sessions/live"
hx-swap="innerHTML"
hx-trigger="load, every 20s, reload-marquee from:body">
</div>

View File

@@ -0,0 +1,32 @@
{% if logged_in %}
<button class="p-12"
hx-post="/api/logout"
hx-target="#nota">
😎
</button>
{% else %}
<button class="p-12"
hx-get="/api/nota?value={{value or ''}}"
hx-target="#nota"
hx-swap="innerHTML"
hx-include="#nota-input">
</button>
{% if value %}
<div class="p-12">
{{ value }}
</div>
<button class="p-12"
hx-get="/api/nota?value={{value or ''}}"
hx-target="#nota"
hx-swap="innerHTML"
hx-include="#nota-input">
</button>
{% endif %}
{% endif %}

View File

@@ -0,0 +1,20 @@
<button class="p-12"
hx-post="/api/nota"
hx-target="#nota"
hx-swap="innerHTML"
hx-include="#nota-input"
hx-trigger="clicked, keyup[keyCode==13] from:#nota-input">
</button>
<input id="nota-input" type="text" name="value" value="{{value or ''}}"
class="rounded border border-yellow-50 focus:outline-none
text-yellow-50 text-center
animate-grow max-w-xs
bg-brown p-1 m-1">
<button class="p-12"
hx-post="/api/nota"
hx-target="#nota"
hx-swap="innerHTML"
hx-include="#nota-input">
</button>

View File

@@ -0,0 +1,5 @@
<div hx-post="/api/nota"
hx-trigger="load"
hx-target="#nota"
hx-swap="innerHTML">
</div>

View File

@@ -0,0 +1,40 @@
<!-- PDF Viewer -->
<script type="module">
const pdfViewer = await import("{{ url_for('static', path='js/pdf_viewer.js') }}");
const options = {
url: '{{ pdf_url }}',
// PDF Canvas (where the pdf will be displayed)
canvas: document.getElementById('the-canvas'),
// Navigation elements
prevPage: document.getElementById('prev'),
nextPage: document.getElementById('next'),
// Page elements
pageCount: document.getElementById('page_count'),
pageNum: document.getElementById('page_num'),
}
pdfViewer.displayPdf(options);
</script>
<div class="flex flex-col items-center justify-center">
<!-- <div class="text-beige"> -->
<!-- <a href="{{ pdf_url }}" -->
<!-- target="_blank"> -->
<!-- <i class="fa fa-file-pdf" aria-hidden="true"></i> -->
<!-- obrir -->
<!-- </a> -->
<!-- </div> -->
<div class="flex flex-row flex-nowrap items-center justify-center w-full">
<button id="prev" class="flex-none text-xl text-beige mx-3">
<i class="fa fa-chevron-left" aria-hidden="true"></i>
</button>
<canvas class="flex-auto m-2 w-1/2 max-w-3xl"
id="the-canvas">
</canvas>
<button id="next" class="flex-none text-xl text-beige mx-3">
<i class="fa fa-chevron-right" aria-hidden="true"></i>
</button>
</div>
<div>
<span><span id="page_num">?</span> / <span id="page_count">?</span></span>
</div>
</div>

View File

@@ -0,0 +1,6 @@
<button title="Inicia una sessió"
class="text-beige mx-1"
hx-put="/api/sessio/{{ session.id }}/live"
hx-swap="outerHTML">
<i class="fa fa-play" aria-hidden="true"></i>
</button>

View File

@@ -0,0 +1,6 @@
<button title="Atura una sessió"
class="text-beige mx-1"
hx-delete="/api/sessio/{{ session.id }}/live"
hx-swap="outerHTML">
<i class="fa fa-stop" aria-hidden="true"></i>
</button>

View File

@@ -0,0 +1,25 @@
{% include "fragments/menu.html" %}
<div class="p-12 text-center">
<h3 class="text-3xl text-beige p-4">Calendari</h3>
{% if logged_in %}
<button title="Afegeix una sessió"
class="text-beige m-2"
hx-post="/api/sessions/editor/"
hx-target="#{{ calendar_list_id }}"
hx-swap="beforeend transition:true">
<i class="fa fa-plus" aria-hidden="true"></i>
Afegeix una sessió
</button>
{% endif %}
<div hx-get="/api/sessions/upcoming"
hx-trigger="load"
hx-target="this"
hx-swap="innerHTML">
</div>
<h3 class="text-3xl text-beige p-4">Historial</h3>
<div hx-get="/api/sessions/history"
hx-trigger="load"
hx-target="this"
hx-swap="innerHTML">
</div>
</div>

View File

@@ -0,0 +1,33 @@
{% include "fragments/menu.html" %}
<div class="flex justify-center">
<div class="m-12 grow max-w-4xl text-center">
<h3 class="text-3xl p-4">
{% set dn = date_names(session.date) %}
{{ dn.day_name }} {{ dn.day }} {{ dn.month_name }} de {{ dn.year }}
</h3>
{% if logged_in %}
{% if session.is_live %}
{% include "fragments/sessions/live/stop.html" %}
{% else %}
{% include "fragments/sessions/live/start.html" %}
{% endif %}
{% endif %}
<div class="text-left">
<h4 class="text-xl text-beige">Horari i lloc</h4>
De {{ session.start_time.strftime("%H:%M") }}
a {{ session.end_time.strftime("%H:%M") }}
{% if session.venue.name %}
a
{% if session.venue.url %}
<a href="{{ session.venue.url }}"
class="text-beige"
target="_blank">
{{ session.venue.name }}
</a>
{% else %}
{{ session.venue.name }}
{% endif %}
{% endif %}
</div>
</div>
</div>

View File

@@ -0,0 +1,24 @@
<p>
{% if session == None %}
<a> L'últim Dimecres de cada mes </a>
{% else %}
<a>
{% set dn = date_names(session.date) %}
{{ dn.day_name }} {{ dn.day }} {{ dn.month_name }}
</a>
de {{ session.start_time.strftime("%H:%M") }}
a {{ session.end_time.strftime("%H:%M") }}
{% if session.venue.name %}
a {{ session.venue.name }}
<!-- {% if session.venue.url %} -->
<!-- <a href="{{ session.venue.url }}" -->
<!-- class="text-beige" -->
<!-- target="_blank"> -->
<!-- {{ session.venue.name }} -->
<!-- </a> -->
<!-- {% else %} -->
<!-- {{ session.venue.name }} -->
<!-- {% endif %} -->
{% endif %}
{% endif %}
</p>

View File

@@ -0,0 +1,28 @@
<ol id="{{ session_list_id }}"
class="flex flex-col items-center justify-center">
{% for session in sessions %}
{% include "fragments/sessions/session_row.html" %}
{% endfor %}
{% if more_sessions or less_sessions %}
<li>
{% if more_sessions %}
<button title="Mostra més sessions"
class="text-beige mx-2"
hx-get="{{ get_sessions_url }}?limit={{ more_sessions }}"
hx-target="#{{ session_list_id }}"
hx-swap="outerHTML">
<i class="fa fa-caret-down" aria-hidden="true"></i>
</button>
{% endif %}
{% if less_sessions %}
<button title="Mostra menys sessions"
class="text-beige mx-2"
hx-get="{{ get_sessions_url }}?limit={{ less_sessions }}"
hx-target="#{{ session_list_id }}"
hx-swap="outerHTML">
<i class="fa fa-caret-up" aria-hidden="true"></i>
</button>
{% endif %}
</li>
{% endif %}
</ol>

View File

@@ -0,0 +1,44 @@
<li class="border rounded border-beige
flex flex-row grow
p-2 m-2 w-full max-w-xl
relative"
id="session-row-{{session.id}}">
<a href="/session/{{session.id}}">
<div class="flex flex-row grow justify-center pl-10">
<div class="flex-1">
{% include "fragments/sessions/session_date.html" %}
</div>
<div class="ml-auto">
<a title="Més informació"
class="text-beige mx-1"
href="/sessio/{{session.id}}/">
<i class="fa fa-info-circle" aria-hidden="true"></i>
</a>
{% if logged_in %}
<button title="Edita la sessió"
class="text-beige mx-1"
hx-get="/api/sessions/editor/{{session.id}}/edita"
hx-target="#session-row-{{session.id}}"
hx-swap="outerHTML">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
<button title="Esborra la sessió"
class="text-beige mx-1"
hx-delete="/api/sessions/editor/{{session.id}}/"
hx-target="#session-row-{{session.id}}"
hx-swap="outerHTML swap:0.5s">
<i class="fa fa-times" aria-hidden="true"></i>
</button>
{% endif %}
</div>
</div>
{% if session.id == next_session_id %}
<div class="absolute -top-5 -left-6
bg-beige text-xs font-bold
px-2 py-1
transition-opacity duration-200
rounded">
Propera<br>sessió
</div>
{% endif %}
</li>

View File

@@ -0,0 +1,96 @@
<li id="session-editor-row-{{session.id}}"
class="border rounded border-beige
flex flex-col grow
py-2 px-10 m-2 w-full max-w-xl">
<form>
<div class="flex flex-row justify-end items-center">
<button title="Aplica els canvis"
class="text-beige mx-2"
hx-put="/api/sessions/editor/{{session.id}}/"
hx-target="#session-editor-row-{{session.id}}"
hx-swap="outerHTML">
<i class="fa fa-check" aria-hidden="true"></i>
</button>
<button title="Descarta els canvis"
class="text-beige mx-2"
hx-get="/api/sessions/editor/{{session.id}}/"
hx-target="#session-editor-row-{{session.id}}"
hx-swap="outerHTML">
<i class="fa fa-times" aria-hidden="true"></i>
</button>
</div>
<div class="flex flex-col items-start pl-5">
<div>
<label for="session-editor-row-{{session.id}}-date">
Data:
</label>
<input type="date" name="date"
value="{{ session.date.strftime('%Y-%m-%d') }}"
min="2022-01-01"
max="2050-12-31"
class="border-none focus:outline-none
text-yellow-50 text-center
bg-brown p-0 m-0
session-editor-{{session.id}}"
/>
</div>
<div>
<label for="session-editor-row-{{session.id}}-start-time">
Hora d'inici:
</label>
<input type="time" name="start_time"
value="{{ session.start_time.strftime('%H:%M') }}"
step="60"
class="border-none focus:outline-none
text-yellow-50 text-center
bg-brown p-0 m-0
session-editor-{{session.id}}"
/>
</div>
<div>
<label for="session-editor-row-{{session.id}}-end-time">
Hora final:
</label>
<input type="time" name="end_time"
value="{{ session.end_time.strftime('%H:%M') }}"
step="60"
class="border-none focus:outline-none
text-yellow-50 text-center
bg-brown p-0 m-0
session-editor-{{session.id}}"
/>
</div>
<div>
<label for="session-editor-row-{{session.id}}-venue-name">
Lloc:
</label>
<input list="llocs" name="venue_name"
placeholder="nom del lloc"
value="{{ session.venue.name or "" }}"
class="border border-yellow-50 focus:outline-none
rounded
text-yellow-50 text-center
bg-brown p-0 m-0
session-editor-{{session.id}}"
/>
<datalist id="llocs">
<option value="la FEM">
<option value="Cal Temerari">
</datalist>
</div>
<div>
<label for="session-editor-row-{{session.id}}-venue-url">
Link:
</label>
<input placeholder="URL del lloc" name="venue_url"
value="{{ session.venue.url or "" }}"
class="border border-yellow-50 focus:outline-none
rounded
text-yellow-30 text-center
bg-brown p-0 m-0 my-1
session-editor-{{session.id}}"
/>
</div>
</div>
</form>
</li>

View File

@@ -0,0 +1,35 @@
<form id="tema-lyric-{{ lyric_idx }}">
<h5 class="text-sm text-beige text-right">
<input name="title"
placeholder="Nom de la lletra"
value="{{ lyric.title }}"
class="border border-beige focus:outline-none
rounded
bg-brown px-2 "
/>
<button title="Desa els canvis"
class="mx-1"
hx-put="/api/tema/{{ tema.id }}/lyric/{{ lyric_idx }}"
hx-target="#tema-lyric-{{ lyric_idx }}"
hx-swap="outerHTML">
<i class="fa fa-check" aria-hidden="true"></i>
</button>
<button title="Descarta els canvis"
class="mx-1"
hx-get="/api/tema/{{ tema.id }}/lyric/{{ lyric_idx }}"
hx-target="#tema-lyric-{{ lyric_idx }}"
hx-swap="outerHTML">
<i class="fa fa-times" aria-hidden="true"></i>
</button>
</h5>
<hr class="h-px mt-1 mb-3 bg-beige border-0">
<textarea name="lyric"
placeholder="Lletra"
rows="{{ lyric.content.count('\n') + 1 }}"
class="border border-beige focus:outline-none
w-full text-center
rounded
bg-brown p-2 m-0">
{{ lyric.content }}
</textarea>
</form>

View File

@@ -0,0 +1,26 @@
<div class="flex flex-row flex-wrap justify-center"
id="tema-title">
<form>
<input name="title"
placeholder="Nom del tema"
value="{{ tema.title }}"
class="border border-beige focus:outline-none
rounded text-3xl
bg-brown p-2 m-0"
/>
<button title="Desa"
class="text-beige text-3xl mx-1"
hx-put="/api/tema/{{ tema.id }}/title"
hx-target="#tema-title"
hx-swap="outerHTML">
<i class="fa fa-check" aria-hidden="true"></i>
</button>
<button title="Descarta"
class="text-beige text-3xl mx-1"
hx-get="/api/tema/{{ tema.id }}/title"
hx-target="#tema-title"
hx-swap="outerHTML">
<i class="fa fa-times" aria-hidden="true"></i>
</button>
</form>
</div>

View File

@@ -0,0 +1,21 @@
<li class="flex flex-row items-start">
<div class="p-2 m-2 text-beige border border-beige rounded-md">
<a href="{{ link.url }}" target="_blank">
{% if link.subtype == LinkSubtype.SPOTIFY %}
{% include "icons/spotify.svg" %}
{% elif link.subtype == LinkSubtype.YOUTUBE %}
{% include "icons/youtube.svg" %}
{% elif link.subtype == LinkSubtype.PDF %}
{% include "icons/pdf.svg" %}
{% elif link.type == LinkType.AUDIO %}
{% include "icons/notes.svg" %}
{% else %}
{% include "icons/link.svg" %}
{% endif %}
</a>
</div>
<div class="my-2">
<p class="text-sm text-beige">Partitura</p>
<p>Hola</p>
</div>
</li>

View File

@@ -0,0 +1,27 @@
{% if logged_in or tema.links %}
<h4 class="text-xl text-beige">Enllaços</h4>
<hr class="h-px mt-1 mb-3 bg-beige border-0">
{% endif %}
{% if tema.links %}
{% for link in tema.links %}
<ul class="flex flex-col justify-center"
id="new-link-target">
{% set link_idx = loop.index0 %}
{% include "fragments/tema/link.html" %}
</ul>
{% endfor %}
{% endif %}
{% if logged_in %}
<div class="flex flex-row my-2 justify-end">
<button title="Afegeix una lletra"
class="text-sm text-beige text-right"
hx-post="/api/tema/{{ tema.id }}/link"
hx-target="#new-link-target"
hx-swap="beforebegin">
<i class="fa fa-plus" aria-hidden="true"></i>
Afegeix un enllaç
</button>
</div>
{% endif %}

View File

@@ -0,0 +1,25 @@
<div id="tema-lyric-{{ lyric_idx }}">
<h5 class="text-sm text-beige text-right">
{{ lyric.title }}
{% if logged_in %}
<button title="Modifica la lletra"
class="mx-1"
hx-get="/api/tema/{{ tema.id }}/editor/lyric/{{ lyric_idx }}"
hx-target="#tema-lyric-{{ lyric_idx }}"
hx-swap="outerHTML">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
<button title="Esborra la lletra"
class="mx-1"
hx-delete="/api/tema/{{ tema.id }}/lyric/{{ lyric_idx }}"
hx-target="#tema-lyric-{{ lyric_idx }}"
hx-swap="outerHTML">
<i class="fa fa-times" aria-hidden="true"></i>
</button>
{% endif %}
</h5>
<hr class="h-px mt-1 mb-3 bg-beige border-0">
<div class="text-center">
{{ lyric.content.replace('\n', '<br>') | safe }}
</div>
</div>

View File

@@ -0,0 +1,24 @@
{% if logged_in or tema.lyrics %}
<h4 class="text-xl text-beige">Lletra</h4>
{% endif %}
{% if tema.lyrics %}
{% for lyric in tema.lyrics %}
{% set lyric_idx = loop.index0 %}
{% include "fragments/tema/lyric.html" %}
{% endfor %}
{% endif %}
{% if logged_in %}
<div id="new-lyric-target"></div>
<div class="flex flex-row my-2 justify-end">
<button title="Afegeix una lletra"
class="text-sm text-beige text-right"
hx-post="/api/tema/{{ tema.id }}/lyric"
hx-target="#new-lyric-target"
hx-swap="beforebegin">
<i class="fa fa-plus" aria-hidden="true"></i>
Afegeix una lletra
</button>
</div>
{% endif %}

View File

@@ -0,0 +1,34 @@
{% include "fragments/menu.html" %}
<div class="flex justify-center">
<div class="m-12 grow max-w-4xl text-center">
{% include "fragments/tema/title.html" %}
<div class="text-left">
<!-- {% if tema.scores() %} -->
<!-- temes -->
<!-- <h4 class="text-xl text-beige">Tema</h4> -->
<!-- <hr class="h-px mt-1 mb-3 bg-beige border-0"> -->
<!-- {% for file in tema.files %} -->
<!-- {% if file.type == FileType.PDF %} -->
<!-- {% set pdf_url = file.path %} -->
<!-- {% include "fragments/pdf_viewer.html" %} -->
<!-- {% endif %} -->
<!-- {% endfor %} -->
<!-- {% endif %} -->
{% include "fragments/tema/lyrics.html" %}
{% include "fragments/tema/links.html" %}
<!-- PROPERTIES -->
{% if tema.properties %}
<h4 class="text-xl mt-3 text-beige">Informació</h4>
<hr class="h-px mt-1 mb-3 bg-beige border-0">
{% for property in tema.properties %}
<p>
<i>{{ property.field.value.capitalize() }}</i>: {{ property.value }}
</p>
{% endfor %}
{% endif %}
</div>
</div>
</div>

View File

@@ -0,0 +1,13 @@
<div class="flex flex-row flex-wrap justify-center"
id="tema-title">
<h3 class="text-3xl p-4">{{ tema.title }}</h3>
{% if logged_in %}
<button title="Canvia el títol"
class="text-beige text-2xl"
hx-get="/api/tema/{{ tema.id }}/editor/title"
hx-target="#tema-title"
hx-swap="outerHTML">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
{% endif %}
</div>

View File

@@ -0,0 +1,24 @@
{% include "fragments/menu.html" %}
<div class="p-12 text-center flex flex-col items-center justify-center">
<h3 class="text-3xl text-beige p-4">Temes</h3>
<button title="Afegeix un tema"
class="text-beige m-2">
<i class="fa fa-plus" aria-hidden="true"></i>
Afegeix un tema
</button>
<input type="text" name="query" value=""
placeholder="Busca una tema..."
class="rounded
text-yellow-50 bg-brown
border border-yellow-50
p-2 m-2"
hx-get="/api/temes/busca"
hx-trigger="revealed, keyup delay:500ms changed"
hx-target="#search-results">
<div class="flex justify-center">
<div id="search-results"
class="m-4 max-w-5xl
flex flex-wrap items-center justify-center">
</div>
</div>
</div>

View File

@@ -0,0 +1,44 @@
<div class="flex-none px-4 py-2 w-80 h-80 m-2 border-solid border rounded-md bg-yellow-50 text-brown">
<div class="flex flex-col h-full">
<div class="text-xl text-beige">
<a href="/tema/{{ tema.id }}">
{{ tema.title }}
</a>
</div>
<div class="grow"></div>
<div class="flex flex-row">
</div>
<ul class="flex flex-row items-center justify-center">
{% if tema.links %}
{% for link in tema.links %}
<li class="p-2 m-2 text-beige border border-beige rounded-md">
<a href="{{ link.url }}" target="_blank">
{% if link.type == LinkType.AUDIO %}
{% if link.subtype == LinkSubtype.SPOTIFY %}
{% include "icons/spotify.svg" %}
{% elif link.subtype == LinkSubtype.YOUTUBE %}
{% include "icons/youtube.svg" %}
{% else %}
{% include "icons/notes.svg" %}
{% endif %}
{% elif link.type == LinkType.SCORE %}
{% if link.subtype == LinkSubtype.PDF %}
{% include "icons/pdf.svg" %}
{% else %}
{% include "icons/link.svg" %}
{% endif %}
{% else %}
{% include "icons/link.svg" %}
{% endif %}
</a>
</li>
{% endfor %}
{% endif %}
</ul>
<!-- {% if logged_in %} -->
<!-- <div> -->
<!-- Edita -->
<!-- </div> -->
<!-- {% endif %} -->
</div>
</div>

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-link-45deg" viewBox="0 0 16 16">
<path d="M4.715 6.542 3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 5.5L8 6.086a1.002 1.002 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4.018 4.018 0 0 1-.128-1.287z"/>
<path d="M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 1 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 1 0-4.243-4.243z"/>
</svg>

After

Width:  |  Height:  |  Size: 531 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-music-note-beamed" viewBox="0 0 16 16">
<path d="M6 13c0 1.105-1.12 2-2.5 2S1 14.105 1 13c0-1.104 1.12-2 2.5-2s2.5.896 2.5 2m9-2c0 1.105-1.12 2-2.5 2s-2.5-.895-2.5-2 1.12-2 2.5-2 2.5.895 2.5 2"/>
<path fill-rule="evenodd" d="M14 11V2h1v9zM6 3v10H5V3z"/>
<path d="M5 2.905a1 1 0 0 1 .9-.995l8-.8a1 1 0 0 1 1.1.995V3L5 4z"/>
</svg>

After

Width:  |  Height:  |  Size: 426 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-file-earmark-pdf-fill" viewBox="0 0 16 16">
<path d="M5.523 12.424c.14-.082.293-.162.459-.238a7.878 7.878 0 0 1-.45.606c-.28.337-.498.516-.635.572a.266.266 0 0 1-.035.012.282.282 0 0 1-.026-.044c-.056-.11-.054-.216.04-.36.106-.165.319-.354.647-.548m2.455-1.647c-.119.025-.237.05-.356.078a21.148 21.148 0 0 0 .5-1.05 12.045 12.045 0 0 0 .51.858c-.217.032-.436.07-.654.114m2.525.939a3.881 3.881 0 0 1-.435-.41c.228.005.434.022.612.054.317.057.466.147.518.209a.095.095 0 0 1 .026.064.436.436 0 0 1-.06.2.307.307 0 0 1-.094.124.107.107 0 0 1-.069.015c-.09-.003-.258-.066-.498-.256M8.278 6.97c-.04.244-.108.524-.2.829a4.86 4.86 0 0 1-.089-.346c-.076-.353-.087-.63-.046-.822.038-.177.11-.248.196-.283a.517.517 0 0 1 .145-.04c.013.03.028.092.032.198.005.122-.007.277-.038.465z"/>
<path fill-rule="evenodd" d="M4 0h5.293A1 1 0 0 1 10 .293L13.707 4a1 1 0 0 1 .293.707V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2m5.5 1.5v2a1 1 0 0 0 1 1h2zM4.165 13.668c.09.18.23.343.438.419.207.075.412.04.58-.03.318-.13.635-.436.926-.786.333-.401.683-.927 1.021-1.51a11.651 11.651 0 0 1 1.997-.406c.3.383.61.713.91.95.28.22.603.403.934.417a.856.856 0 0 0 .51-.138c.155-.101.27-.247.354-.416.09-.181.145-.37.138-.563a.844.844 0 0 0-.2-.518c-.226-.27-.596-.4-.96-.465a5.76 5.76 0 0 0-1.335-.05 10.954 10.954 0 0 1-.98-1.686c.25-.66.437-1.284.52-1.794.036-.218.055-.426.048-.614a1.238 1.238 0 0 0-.127-.538.7.7 0 0 0-.477-.365c-.202-.043-.41 0-.601.077-.377.15-.576.47-.651.823-.073.34-.04.736.046 1.136.088.406.238.848.43 1.295a19.697 19.697 0 0 1-1.062 2.227 7.662 7.662 0 0 0-1.482.645c-.37.22-.699.48-.897.787-.21.326-.275.714-.08 1.103z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-spotify" viewBox="0 0 16 16">
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0m3.669 11.538a.498.498 0 0 1-.686.165c-1.879-1.147-4.243-1.407-7.028-.77a.499.499 0 0 1-.222-.973c3.048-.696 5.662-.397 7.77.892a.5.5 0 0 1 .166.686zm.979-2.178a.624.624 0 0 1-.858.205c-2.15-1.321-5.428-1.704-7.972-.932a.625.625 0 0 1-.362-1.194c2.905-.881 6.517-.454 8.986 1.063a.624.624 0 0 1 .206.858m.084-2.268C10.154 5.56 5.9 5.419 3.438 6.166a.748.748 0 1 1-.434-1.432c2.825-.857 7.523-.692 10.492 1.07a.747.747 0 1 1-.764 1.288z"/>
</svg>

After

Width:  |  Height:  |  Size: 614 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-youtube" viewBox="0 0 16 16">
<path d="M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 0 1 1.415 1.42c.101.38.172.883.22 1.402l.01.104.022.26.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105-.009.104c-.05.572-.124 1.14-.235 1.558a2.007 2.007 0 0 1-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006-.087-.004-.171-.007-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.007 2.007 0 0 1-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31.4 31.4 0 0 1 0 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103.003-.052.008-.104.022-.26.01-.104c.048-.519.119-1.023.22-1.402a2.007 2.007 0 0 1 1.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007.172-.006.086-.003.171-.007A99.788 99.788 0 0 1 7.858 2h.193zM6.4 5.209v4.818l4.157-2.408z"/>
</svg>

After

Width:  |  Height:  |  Size: 913 B

View File

@@ -0,0 +1,38 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"/>
<title>{{ page_title }}</title>
<meta name="description" content="{{ page_description }}"/>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA=="
crossorigin="anonymous"
referrerpolicy="no-referrer" />
<!-- Taiwind CSS -->
<link rel="stylesheet" href="{{ url_for('static', path='css/main.css') }}" type="text/css" />
<!-- HTMX -->
<script src="{{ url_for('static', path='js/htmx.min.js') }}"></script>
<!-- Favicon! -->
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', path='favicon/apple-touch-icon.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', path='favicon/favicon-32x32.png') }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ url_for('static', path='favicon/favicon-16x16.png') }}">
<link rel="manifest" href="/site.webmanifest">
</head>
<body class="bg-brown text-yellow-50">
<div id="header">
{% include "fragments/header.html" %}
</div>
<div class="{% if animate %} animate-fade-in-three opacity-0 {% endif %}">
{% include "fragments/content.html" %}
{% include "fragments/footer.html" %}
</div>
</body>
</html>