|
|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
import Card from '$lib/ui/layout/Card.svelte';
|
|
|
|
|
import Badge from '$lib/ui/atoms/Badge.svelte';
|
|
|
|
|
import { success, error as toastError } from '$lib/stores/toasts';
|
|
|
|
|
|
|
|
|
|
export type Counts = { open: number; unassigned: number };
|
|
|
|
|
export type TaskPreview = { id: number; description: string; due_date: string | null; display_code: number | null };
|
|
|
|
|
@ -9,6 +10,27 @@
|
|
|
|
|
export let name: string | null = null;
|
|
|
|
|
export let counts: Counts = { open: 0, unassigned: 0 };
|
|
|
|
|
export let previews: TaskPreview[] = [];
|
|
|
|
|
|
|
|
|
|
let busyTaskId: number | null = null;
|
|
|
|
|
|
|
|
|
|
async function claim(taskId: number) {
|
|
|
|
|
if (busyTaskId) return;
|
|
|
|
|
busyTaskId = taskId;
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch(`/api/tasks/${taskId}/claim`, { method: 'POST' });
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
success('Tarea reclamada');
|
|
|
|
|
location.reload();
|
|
|
|
|
} else {
|
|
|
|
|
const txt = await res.text();
|
|
|
|
|
toastError(txt || 'No se pudo reclamar');
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
toastError('Error de red');
|
|
|
|
|
} finally {
|
|
|
|
|
busyTaskId = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
@ -25,9 +47,14 @@
|
|
|
|
|
<em class="title">Sin responsable (hasta 3):</em>
|
|
|
|
|
<ul class="list">
|
|
|
|
|
{#each previews as t}
|
|
|
|
|
<li>
|
|
|
|
|
<span>#{t.display_code ?? t.id} — {t.description}</span>
|
|
|
|
|
{#if t.due_date}<small class="muted"> (vence: {t.due_date})</small>{/if}
|
|
|
|
|
<li class="row">
|
|
|
|
|
<div class="info">
|
|
|
|
|
<span>#{t.display_code ?? t.id} — {t.description}</span>
|
|
|
|
|
{#if t.due_date}<small class="muted"> (vence: {t.due_date})</small>{/if}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="actions">
|
|
|
|
|
<button class="btn" on:click|preventDefault={() => claim(t.id)} disabled={busyTaskId === t.id}>Reclamar</button>
|
|
|
|
|
</div>
|
|
|
|
|
</li>
|
|
|
|
|
{/each}
|
|
|
|
|
</ul>
|
|
|
|
|
@ -49,4 +76,31 @@
|
|
|
|
|
.list { margin: 6px 0 0 18px; padding: 0; }
|
|
|
|
|
.list li { margin: 4px 0; }
|
|
|
|
|
.muted { color: var(--color-text-muted); }
|
|
|
|
|
|
|
|
|
|
.row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
}
|
|
|
|
|
.info {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.actions {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
.btn {
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
background: var(--color-surface);
|
|
|
|
|
color: var(--color-text);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.btn[disabled] { opacity: .6; cursor: not-allowed; }
|
|
|
|
|
</style>
|
|
|
|
|
|