tasks: add individual completion deletion API and UI
All checks were successful
CI / update (push) Successful in 2m23s
All checks were successful
CI / update (push) Successful in 2m23s
This commit is contained in:
22
src/routes/api/tasks/completions/[id]/+server.ts
Normal file
22
src/routes/api/tasks/completions/[id]/+server.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
import { TaskCompletion } from '$models/TaskCompletion';
|
||||||
|
import { dbConnect } from '$utils/db';
|
||||||
|
import { error, json } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const DELETE: RequestHandler = async ({ params, locals }) => {
|
||||||
|
const auth = await locals.auth();
|
||||||
|
if (!auth?.user?.nickname) throw error(401, 'Not logged in');
|
||||||
|
|
||||||
|
await dbConnect();
|
||||||
|
|
||||||
|
const completion = await TaskCompletion.findById(params.id);
|
||||||
|
if (!completion) throw error(404, 'Completion not found');
|
||||||
|
|
||||||
|
if (completion.completedBy !== auth.user.nickname) {
|
||||||
|
throw error(403, 'You can only delete your own completions');
|
||||||
|
}
|
||||||
|
|
||||||
|
await completion.deleteOne();
|
||||||
|
|
||||||
|
return json({ success: true });
|
||||||
|
};
|
||||||
@@ -62,6 +62,15 @@
|
|||||||
.slice(0, 20)
|
.slice(0, 20)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** @param {string} id */
|
||||||
|
async function deleteCompletion(id) {
|
||||||
|
const res = await fetch(`/api/tasks/completions/${id}`, { method: 'DELETE' });
|
||||||
|
if (res.ok) {
|
||||||
|
const statsRes = await fetch('/api/tasks/stats');
|
||||||
|
if (statsRes.ok) stats = await statsRes.json();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function clearHistory() {
|
async function clearHistory() {
|
||||||
if (!confirm('Deinen gesamten Verlauf und alle Sticker wirklich löschen? Das kann nicht rückgängig gemacht werden.')) return;
|
if (!confirm('Deinen gesamten Verlauf und alle Sticker wirklich löschen? Das kann nicht rückgängig gemacht werden.')) return;
|
||||||
const res = await fetch('/api/tasks/stats', { method: 'DELETE' });
|
const res = await fetch('/api/tasks/stats', { method: 'DELETE' });
|
||||||
@@ -135,6 +144,11 @@
|
|||||||
{completion.completedBy} · {formatDistanceToNow(new Date(completion.completedAt), { locale: de, addSuffix: true })}
|
{completion.completedBy} · {formatDistanceToNow(new Date(completion.completedAt), { locale: de, addSuffix: true })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{#if completion.completedBy === currentUser}
|
||||||
|
<button class="btn-delete-completion" title="Eintrag löschen" onclick={() => deleteCompletion(completion._id)}>
|
||||||
|
<Trash2 size={14} />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -306,6 +320,27 @@
|
|||||||
border: 1px solid var(--color-border, #e8e4dd);
|
border: 1px solid var(--color-border, #e8e4dd);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
.btn-delete-completion {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text-secondary, #ccc);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 150ms;
|
||||||
|
}
|
||||||
|
.recent-item:hover .btn-delete-completion { opacity: 1; }
|
||||||
|
.btn-delete-completion:hover {
|
||||||
|
color: var(--nord11);
|
||||||
|
background: rgba(191, 97, 106, 0.08);
|
||||||
|
}
|
||||||
.recent-img {
|
.recent-img {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
|
|||||||
Reference in New Issue
Block a user