[FEAT] Fix some bug and css issues.
This commit is contained in:
@@ -4,8 +4,10 @@
|
||||
import { auth } from '$lib/stores/auth';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { INSTRUMENTS, getInstrument } from '$lib/instruments';
|
||||
import { INSTRUMENTS, getInstrument, getDefaultKey, getInstrumentFile } from '$lib/instruments';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
import BackLink from '$lib/components/BackLink.svelte';
|
||||
import Alert from '$lib/components/Alert.svelte';
|
||||
|
||||
let scoreId = $derived(page.params.id || '');
|
||||
let scoreName = $state('');
|
||||
@@ -41,6 +43,24 @@
|
||||
let uploadVariant = $state('');
|
||||
let uploadPart = $state('1');
|
||||
let uploadWatermark = $state(false);
|
||||
|
||||
// Combined parts (`sup`/`par`): list of instruments with their key/clef/part
|
||||
type CombinedPart = { instrument: string; key: string; clef: string; part: string };
|
||||
const KEYS = ['sib', 'mib', 'fa', 'do', 'ut', 'sol', 're', 'mi', 'si'];
|
||||
const CLEFS = ['clesol', 'clefa', 'cleut'];
|
||||
let combinedParts = $state<CombinedPart[]>([
|
||||
{ instrument: '', key: '', clef: '', part: '1' }
|
||||
]);
|
||||
let uploadCombined = $derived(
|
||||
combinedParts
|
||||
.filter((p) => p.instrument)
|
||||
.map((p) =>
|
||||
[getInstrumentFile(p.instrument), p.key, p.clef, p.part]
|
||||
.filter((v) => v !== '')
|
||||
.join('-')
|
||||
)
|
||||
.join('+')
|
||||
);
|
||||
let uploadWatermarkPosition = $state<'left' | 'right'>('left');
|
||||
|
||||
// Edit pieces
|
||||
@@ -128,13 +148,17 @@
|
||||
|
||||
function getExpectedFilename(): string {
|
||||
if (!uploadInstrument) return '';
|
||||
// sup/par: the file is named after the combined instruments (see NAMING.md)
|
||||
if (uploadInstrument === 'sup' || uploadInstrument === 'par') {
|
||||
const combined = uploadCombined.trim().toLowerCase();
|
||||
return combined ? `${combined}.pdf` : '';
|
||||
}
|
||||
const inst = getInstrument(uploadInstrument);
|
||||
const instName = inst ? inst.name.toLowerCase().replace(/ /g, '_') : uploadInstrument;
|
||||
const base = inst?.file || uploadInstrument;
|
||||
const key = uploadKey || inst?.defaultKey || '';
|
||||
const keyStr = key ? `_${key}` : '';
|
||||
const clef = uploadClef ? `_${uploadClef}` : '';
|
||||
const variant = uploadVariant ? `_${uploadVariant}` : '';
|
||||
return `${instName}${variant}${keyStr}${clef}_${uploadPart}.pdf`;
|
||||
const parts = [base, key, uploadVariant, uploadClef].filter((v) => v !== '');
|
||||
parts.push(uploadPart);
|
||||
return `${parts.join('-')}.pdf`;
|
||||
}
|
||||
|
||||
let expectedFilename = $derived(getExpectedFilename());
|
||||
@@ -195,7 +219,8 @@
|
||||
uploadVariant,
|
||||
uploadPart,
|
||||
uploadWatermark,
|
||||
uploadWatermarkPosition
|
||||
uploadWatermarkPosition,
|
||||
uploadCombined
|
||||
);
|
||||
if (result.success) {
|
||||
uploadSuccess = 'Fichier uploadé avec succès';
|
||||
@@ -269,16 +294,14 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="max-w-6xl mx-auto p-6">
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="mb-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<span class="text-2xl font-bold text-gray-400">№ {scoreId}</span>
|
||||
<a href="/admin" class="text-ohmj-primary hover:underline">← Retour à l'admin</a>
|
||||
</div>
|
||||
|
||||
<BackLink href="/admin" label="Retour à l'admin" class="mb-4" />
|
||||
|
||||
<!-- Info Card -->
|
||||
<div class="bg-white rounded-xl shadow-md border border-gray-200 overflow-hidden mb-6">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
||||
<div class="card mb-6 overflow-hidden">
|
||||
<div class="flex items-center gap-3 border-b border-gray-200 bg-gray-50 px-6 py-4">
|
||||
<span class="badge bg-ohmj-secondary/20 font-semibold text-ohmj-primary">№ {scoreId}</span>
|
||||
<h2 class="text-lg font-semibold text-ohmj-primary">Informations de la partition</h2>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
@@ -358,11 +381,14 @@
|
||||
</button>
|
||||
{:else}
|
||||
{#if scoreRessource}
|
||||
<button type="button" class="flex-1 text-left text-sm text-blue-600 cursor-pointer hover:text-blue-800 px-3 py-2 rounded-lg hover:bg-gray-50" onclick={() => startEdit('ressource', scoreRessource)} title="Cliquer pour modifier">
|
||||
🔗 {scoreRessource}
|
||||
<button type="button" class="flex flex-1 items-center gap-1.5 px-3 py-2 text-left text-sm text-ohmj-primary hover:underline" onclick={() => startEdit('ressource', scoreRessource)} title="Cliquer pour modifier">
|
||||
<svg class="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-7.5M10 14 20 4m0 0h-5m5 0v5" />
|
||||
</svg>
|
||||
{scoreRessource}
|
||||
</button>
|
||||
{:else}
|
||||
<button type="button" class="flex-1 text-left text-sm text-gray-400 cursor-pointer hover:text-gray-600 px-3 py-2 rounded-lg hover:bg-gray-50" onclick={() => startEdit('ressource', '')} title="Cliquer pour ajouter">
|
||||
<button type="button" class="flex-1 rounded-lg px-3 py-2 text-left text-sm text-gray-400 hover:bg-gray-50 hover:text-gray-600" onclick={() => startEdit('ressource', '')} title="Cliquer pour ajouter">
|
||||
Aucune ressource
|
||||
</button>
|
||||
{/if}
|
||||
@@ -375,11 +401,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Parties -->
|
||||
<div class="bg-white rounded-xl shadow-md border border-gray-200 overflow-hidden mb-6">
|
||||
<div class="card mb-6 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gray-50 flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold text-ohmj-primary">Parties ({selectedScorePieceCount})</h2>
|
||||
{#if !editingPieces}
|
||||
<button onclick={() => { piecesEdit = [...selectedScorePieces]; editingPieces = true; }} class="text-ohmj-primary hover:text-ohmj-secondary text-sm font-medium">
|
||||
<button onclick={() => { piecesEdit = [...selectedScorePieces]; editingPieces = true; }} class="btn btn-ghost">
|
||||
Modifier
|
||||
</button>
|
||||
{/if}
|
||||
@@ -393,7 +419,7 @@
|
||||
<input
|
||||
type="text"
|
||||
bind:value={piece.name}
|
||||
class="flex-1 px-3 py-2 border border-gray-300 rounded-md text-sm"
|
||||
class="input flex-1"
|
||||
placeholder="Nom de la partie"
|
||||
/>
|
||||
<button onclick={() => piecesEdit = piecesEdit.filter((_, idx) => idx !== i)} class="text-red-500 hover:text-red-700 p-2" title="Supprimer">
|
||||
@@ -401,12 +427,12 @@
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
<button onclick={() => piecesEdit = [...piecesEdit, {id: String(piecesEdit.length + 1), name: ''}]} class="text-ohmj-primary hover:text-ohmj-secondary text-sm font-medium">
|
||||
<button onclick={() => piecesEdit = [...piecesEdit, {id: String(piecesEdit.length + 1), name: ''}]} class="btn btn-ghost">
|
||||
+ Ajouter une partie
|
||||
</button>
|
||||
<div class="flex gap-2 pt-3 border-t">
|
||||
<button onclick={() => { piecesEdit = [...selectedScorePieces]; editingPieces = false; }} class="px-4 py-2 text-gray-600 hover:text-gray-800 text-sm">Annuler</button>
|
||||
<button onclick={savePieces} disabled={savingPieces} class="px-4 py-2 bg-ohmj-primary text-white rounded hover:bg-ohmj-secondary text-sm disabled:opacity-50">
|
||||
<button onclick={() => { piecesEdit = [...selectedScorePieces]; editingPieces = false; }} class="btn btn-ghost">Annuler</button>
|
||||
<button onclick={savePieces} disabled={savingPieces} class="btn btn-primary">
|
||||
{savingPieces ? 'Enregistrement...' : 'Enregistrer'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -423,18 +449,14 @@
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
|
||||
{error}
|
||||
</div>
|
||||
{/if}
|
||||
<Alert message={error} class="mb-4" />
|
||||
{/if}
|
||||
|
||||
{#if !isAdmin}
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
||||
Accès refusé. Vous devez être administrateur.
|
||||
</div>
|
||||
<Alert message="Accès refusé. Vous devez être administrateur." />
|
||||
{:else}
|
||||
<!-- Upload PDF -->
|
||||
<div class="mb-8 p-0 bg-white rounded-xl shadow-md border border-gray-200 overflow-hidden">
|
||||
<div class="card mb-8 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
||||
<h2 class="text-lg font-semibold text-ohmj-primary">Ajouter une partition</h2>
|
||||
</div>
|
||||
@@ -442,12 +464,12 @@
|
||||
<form onsubmit={(e) => { e.preventDefault(); uploadPdf(); }} class="space-y-4">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div>
|
||||
<label for="up-instrument" class="block text-sm font-medium text-gray-700">Instrument</label>
|
||||
<label for="up-instrument" class="label">Instrument</label>
|
||||
<select
|
||||
id="up-instrument"
|
||||
bind:value={uploadInstrument}
|
||||
required
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-white"
|
||||
class="input mt-1"
|
||||
>
|
||||
<option value="">Sélectionner...</option>
|
||||
{#each INSTRUMENTS as inst}
|
||||
@@ -457,26 +479,90 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if uploadInstrument === 'sup' || uploadInstrument === 'par'}
|
||||
<div>
|
||||
<span class="label">Parties combinées</span>
|
||||
<div class="mt-1 space-y-2">
|
||||
{#each combinedParts as p, i}
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<select
|
||||
bind:value={p.instrument}
|
||||
onchange={() => (p.key = getDefaultKey(p.instrument))}
|
||||
class="input w-44"
|
||||
aria-label="Instrument {i + 1}"
|
||||
>
|
||||
<option value="">Instrument…</option>
|
||||
{#each INSTRUMENTS.filter((x) => x.file) as inst}
|
||||
<option value={inst.code}>{inst.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<select bind:value={p.key} class="input w-28" aria-label="Tonalité {i + 1}">
|
||||
<option value="">-</option>
|
||||
{#each KEYS as k}<option value={k}>{k}</option>{/each}
|
||||
</select>
|
||||
<select bind:value={p.clef} class="input w-28" aria-label="Clé {i + 1}">
|
||||
<option value="">-</option>
|
||||
{#each CLEFS as c}<option value={c}>{c}</option>{/each}
|
||||
</select>
|
||||
<input
|
||||
bind:value={p.part}
|
||||
class="input w-20"
|
||||
placeholder="Partie"
|
||||
aria-label="Partie {i + 1}"
|
||||
/>
|
||||
{#if combinedParts.length > 1}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (combinedParts = combinedParts.filter((_, k) => k !== i))}
|
||||
class="rounded-full p-1.5 text-red-500 hover:bg-red-50 hover:text-red-700"
|
||||
title="Retirer"
|
||||
aria-label="Retirer l'instrument {i + 1}"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (combinedParts = [...combinedParts, { instrument: '', key: '', clef: '', part: '1' }])}
|
||||
class="btn btn-ghost mt-2 px-2 py-1"
|
||||
>
|
||||
+ Ajouter un instrument
|
||||
</button>
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
Chaque instrument avec sa tonalité/clé/partie, reliés par « + » dans le nom
|
||||
(ex. <code>contrebasse-sib-clefa-1+baryton-sib-clefa-1</code>).
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => { showAdvanced = !showAdvanced; if (showAdvanced && !uploadKey) { uploadKey = INSTRUMENTS.find(i => i.code === uploadInstrument)?.defaultKey || ''; } }}
|
||||
class="text-sm text-gray-600 hover:text-gray-800"
|
||||
class="btn btn-ghost"
|
||||
>
|
||||
{showAdvanced ? '▼ Masquer' : '▶ Options avancées'}
|
||||
<svg class="h-4 w-4 transition-transform {showAdvanced ? 'rotate-90' : ''}" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
{showAdvanced ? 'Masquer les options' : 'Options avancées'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if showAdvanced}
|
||||
<div class="grid grid-cols-2 md:grid-cols-7 gap-4 p-3 bg-gray-100 rounded">
|
||||
<div class="grid grid-cols-2 gap-4 rounded-md border border-gray-200 bg-gray-50 p-3 md:grid-cols-7">
|
||||
{#if selectedScorePieceCount > 1}
|
||||
<div>
|
||||
<label for="up-piece" class="block text-sm font-medium text-gray-700">Pièce</label>
|
||||
<label for="up-piece" class="label">Pièce</label>
|
||||
<select
|
||||
id="up-piece"
|
||||
bind:value={uploadPiece}
|
||||
required
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-white"
|
||||
class="input mt-1"
|
||||
>
|
||||
{#each selectedScorePieces as piece}
|
||||
<option value={piece.id}>{piece.name}</option>
|
||||
@@ -485,20 +571,20 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
<label for="up-version" class="block text-sm font-medium text-gray-700">Version</label>
|
||||
<label for="up-version" class="label">Version</label>
|
||||
<input
|
||||
id="up-version"
|
||||
type="number"
|
||||
min="1"
|
||||
bind:value={uploadVersion}
|
||||
required
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm"
|
||||
class="input mt-1"
|
||||
placeholder="1"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="up-key" class="block text-sm font-medium text-gray-700">Tonalité</label>
|
||||
<select id="up-key" bind:value={uploadKey} class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-white">
|
||||
<label for="up-key" class="label">Tonalité</label>
|
||||
<select id="up-key" bind:value={uploadKey} class="input mt-1">
|
||||
<option value="">-</option>
|
||||
<option value="sib">Si♭ (B♭)</option>
|
||||
<option value="mib">Mi♭ (E♭)</option>
|
||||
@@ -508,8 +594,8 @@
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="up-clef" class="block text-sm font-medium text-gray-700">Clé</label>
|
||||
<select id="up-clef" bind:value={uploadClef} class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-white">
|
||||
<label for="up-clef" class="label">Clé</label>
|
||||
<select id="up-clef" bind:value={uploadClef} class="input mt-1">
|
||||
<option value="">-</option>
|
||||
<option value="clesol">Sol</option>
|
||||
<option value="clefa">Fa</option>
|
||||
@@ -517,8 +603,8 @@
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="up-part" class="block text-sm font-medium text-gray-700">Partie</label>
|
||||
<select id="up-part" bind:value={uploadPart} class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-white">
|
||||
<label for="up-part" class="label">Partie</label>
|
||||
<select id="up-part" bind:value={uploadPart} class="input mt-1">
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
@@ -526,19 +612,15 @@
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="up-variant" class="block text-sm font-medium text-gray-700">Variante</label>
|
||||
<select id="up-variant" bind:value={uploadVariant} class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-white">
|
||||
<label for="up-variant" class="label">Variante</label>
|
||||
<select id="up-variant" bind:value={uploadVariant} class="input mt-1">
|
||||
<option value="">-</option>
|
||||
<option value="solo">Solo</option>
|
||||
<option value="default">Default</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => showAdvanced = false}
|
||||
class="text-sm text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
<button type="button" onclick={() => showAdvanced = false} class="btn btn-ghost">
|
||||
Masquer
|
||||
</button>
|
||||
</div>
|
||||
@@ -546,7 +628,7 @@
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<label for="file-input" class="block text-sm font-medium text-gray-700">Fichier PDF</label>
|
||||
<label for="file-input" class="label">Fichier PDF</label>
|
||||
<div class="flex items-center gap-3 mt-1">
|
||||
<label class="relative inline-flex items-center cursor-pointer whitespace-nowrap">
|
||||
<input type="checkbox" bind:checked={uploadWatermark} class="sr-only peer" />
|
||||
@@ -556,7 +638,7 @@
|
||||
{#if uploadWatermark}
|
||||
<select
|
||||
bind:value={uploadWatermarkPosition}
|
||||
class="text-sm border border-gray-300 rounded px-2 py-1"
|
||||
class="input w-auto"
|
||||
>
|
||||
<option value="left">Gauche</option>
|
||||
<option value="right">Droite</option>
|
||||
@@ -576,15 +658,17 @@
|
||||
<p class="text-sm text-gray-500">Nom attendu: <code class="bg-gray-100 px-1 rounded">{expectedFilename}</code></p>
|
||||
{/if}
|
||||
{#if uploadError}
|
||||
<p class="text-red-600 text-sm">{uploadError}</p>
|
||||
<Alert message={uploadError} />
|
||||
{/if}
|
||||
{#if uploadSuccess}
|
||||
<p class="text-green-600 text-sm">{uploadSuccess}</p>
|
||||
<p class="rounded-md border border-green-300 bg-green-50 px-4 py-3 text-sm text-green-700">
|
||||
{uploadSuccess}
|
||||
</p>
|
||||
{/if}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={uploading}
|
||||
class="bg-ohmj-primary text-white px-4 py-2 rounded hover:bg-ohmj-secondary disabled:opacity-50"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
{uploading ? 'Upload...' : 'Uploader'}
|
||||
</button>
|
||||
@@ -593,69 +677,81 @@
|
||||
</div>
|
||||
|
||||
<!-- File Tree -->
|
||||
<div class="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div class="p-4 border-b bg-gray-50">
|
||||
<h2 class="text-lg font-semibold text-ohmj-primary">Fichiers</h2>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
{#if files.length === 0}
|
||||
<p class="text-gray-500 text-center py-8">Aucun fichier uploadé</p>
|
||||
{:else}
|
||||
<div class="font-mono text-sm">
|
||||
{#each files as piece}
|
||||
<div class="mb-2">
|
||||
<span class="text-gray-700 font-semibold">📁 {piece.name}/</span>
|
||||
{#if piece.children}
|
||||
<div class="card overflow-hidden">
|
||||
<div class="border-b bg-gray-50 p-4">
|
||||
<h2 class="text-lg font-semibold text-ohmj-primary">Fichiers</h2>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
{#if files.length === 0}
|
||||
<p class="py-8 text-center text-gray-500">Aucun fichier uploadé</p>
|
||||
{:else}
|
||||
<div class="space-y-3 text-sm">
|
||||
{#each files as piece}
|
||||
<div>
|
||||
<div class="flex items-center gap-1.5 font-medium text-gray-700">
|
||||
<svg class="h-4 w-4 text-ohmj-secondary" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
</svg>
|
||||
{piece.name}/
|
||||
</div>
|
||||
{#if piece.children}
|
||||
<div class="ml-2 mt-1.5 space-y-2 border-l border-gray-200 pl-3">
|
||||
{#each piece.children as instrument}
|
||||
<div class="ml-4">
|
||||
<span class="text-gray-600">├─ 📁 {instrument.name}/</span>
|
||||
<div>
|
||||
<div class="flex items-center gap-1.5 text-gray-600">
|
||||
<svg class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
</svg>
|
||||
{instrument.name}/
|
||||
</div>
|
||||
{#if instrument.children}
|
||||
{#each instrument.children as version}
|
||||
<div class="ml-4">
|
||||
<span class="text-gray-600">├─ 📁 {version.name}/</span>
|
||||
{#if version.children}
|
||||
{#each version.children as file, fileIndex}
|
||||
{@const isLast = fileIndex === version.children.length - 1}
|
||||
<div class="ml-4">
|
||||
|
||||
{#if isLast}
|
||||
<span class="text-gray-500">└─ 📄 {file.name}</span>
|
||||
<button
|
||||
onclick={() => deleteFilePath = file.path}
|
||||
class="ml-2 text-red-500 hover:text-red-700 p-1 rounded-full hover:bg-red-50"
|
||||
title="Supprimer"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.997-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
{:else}
|
||||
<span class="text-gray-500">├─ 📄 {file.name}</span>
|
||||
<button
|
||||
onclick={() => deleteFilePath = file.path}
|
||||
class="ml-2 text-red-500 hover:text-red-700 p-1 rounded-full hover:bg-red-50"
|
||||
title="Supprimer"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.997-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="ml-2 mt-1.5 space-y-2 border-l border-gray-200 pl-3">
|
||||
{#each instrument.children as version}
|
||||
<div>
|
||||
<div class="flex items-center gap-1.5 text-gray-500">
|
||||
<svg class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
</svg>
|
||||
{version.name}/
|
||||
</div>
|
||||
{#if version.children}
|
||||
<ul class="ml-2 mt-1 space-y-0.5 border-l border-gray-200 pl-3">
|
||||
{#each version.children as file}
|
||||
<li class="flex items-center gap-1.5">
|
||||
<svg class="h-4 w-4 shrink-0 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14 2v6h6" />
|
||||
</svg>
|
||||
<span class="truncate font-mono text-gray-600">{file.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (deleteFilePath = file.path)}
|
||||
class="ml-1 rounded-full p-1 text-red-500 hover:bg-red-50 hover:text-red-700"
|
||||
title="Supprimer"
|
||||
aria-label="Supprimer"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.997-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Delete Confirmation Modal -->
|
||||
|
||||
Reference in New Issue
Block a user