Drag & Drop

This commit is contained in:
Brieuc Dubois 2024-01-01 17:31:17 +01:00
parent 16996de532
commit cc9ddc21a0
6 changed files with 113 additions and 20 deletions

View File

@ -26,10 +26,27 @@ export async function newCardApi(projectId: number): Promise<Card> {
}
export async function deleteCardApi(cardID: number): Promise<void> {
const response = await api.delete(`/v2/cards/${cardID}`);
const response = await api.delete(`/v1/cards/${cardID}`);
if (response.status !== status.NoContent) {
processError(response, 'Failed to delete card');
return Promise.reject();
}
}
export async function updateCardTagApi(
cardID: number,
tagID: number,
option_id: number,
value: string
): Promise<void> {
const response = await api.put(`/v1/cards/${cardID}/tags/${tagID}`, {
option_id: option_id,
value: value
});
if (response.status !== status.NoContent) {
processError(response, 'Failed to update card tag');
return Promise.reject();
}
}

View File

@ -1,16 +1,18 @@
<script lang="ts">
import type { Card } from '../stores/interfaces';
import projectTags from '../stores/projectTags';
import { currentModalCard } from '../stores/smallStore';
import { currentDraggedCard, currentModalCard } from '../stores/smallStore';
import ModalCard from './modal_card.svelte';
export let card: Card;
</script>
<!-- on:dragend={() => currentDraggedCard.set(null)} -->
<div
class="card"
tabindex="0"
draggable={true}
on:dragstart={() => currentDraggedCard.set(card)}
on:click={() => ($currentModalCard = card.id)}
role="button"
on:keydown={(e) => {

View File

@ -1,15 +1,76 @@
<script lang="ts">
import { updateCardTagApi } from '../api/cards';
import type { Card, TagOption } from '../stores/interfaces';
import { cards, currentDraggedCard } from '../stores/smallStore';
import api, { processError } from '../utils/api';
import status from '../utils/status';
import CardC from './card.svelte';
export let title: string;
export let cards: Card[] = [];
export let option: TagOption;
export let columnCards: Card[] = [];
async function onDrop(e: DragEvent) {
e.preventDefault();
if ($currentDraggedCard && $currentDraggedCard.tags) {
for (let tag of $currentDraggedCard.tags) {
if (tag.tag_id == option.tag_id) {
try {
if (tag.option_id == option.id) return;
// DELETE
if (tag.option_id !== -1 && option.id === -1) {
const response = await api.delete(`/v1/cards/${tag.card_id}/tags/${tag.tag_id}`);
if (response.status !== status.NoContent) {
processError(response, 'Failed to delete tag');
return;
}
}
// CREATE
else if (tag.option_id == -1 && option.id !== -1) {
const response = await api.post(`/v1/cards/${tag.card_id}/tags/${tag.tag_id}`, {
value: tag.value,
option_id: option.id
});
if (response.status !== status.Created) {
processError(response, 'Failed to create tag');
return;
}
}
// UPDATE
else {
const response = await api.put(`/v1/cards/${tag.card_id}/tags/${tag.tag_id}`, {
value: tag.value,
option_id: option.id
});
if (response.status !== status.NoContent) {
processError(response, 'Failed to update tag');
return;
}
}
tag.option_id = option.id;
cards.reload();
} catch (e) {}
break;
}
}
currentDraggedCard.set(null);
}
}
</script>
<div class="column">
<h3>{title}</h3>
<div
class="column"
role="listbox"
tabindex="-1"
on:drop={onDrop}
on:dragover={(e) => {
e.preventDefault();
}}
>
<h3>{option.value}</h3>
<ul>
{#each cards as card}
{#each columnCards as card}
<CardC {card} />
{/each}
</ul>
@ -21,7 +82,8 @@
}
.column {
width: 200px;
margin: 0 10px;
width: 200px;
height: 100%;
}
</style>

View File

@ -1,11 +1,10 @@
<script lang="ts">
import { onMount } from 'svelte';
import { type Project, type Card, parseCards, type View } from '../stores/interfaces';
import { type Project, type Card, type TagOption, type View } from '../stores/interfaces';
import projectTags from '../stores/projectTags';
import { deleteCardApi, newCardApi } from '../api/cards';
import { getProjectAPI, getProjectCardsAPI } from '../api/projects';
import { getProjectAPI } from '../api/projects';
import Column from './column.svelte';
import { cards, currentModalCard, currentView } from '../stores/smallStore';
import { cards, currentView } from '../stores/smallStore';
export let projectId: number;
@ -69,7 +68,7 @@
</svelte:head>
{#if project}
<div id="project">
<section>
<header>
<h2>{project.title}</h2>
<button on:click={newCard}>New card</button>
@ -78,22 +77,32 @@
<div class="grid">
{#each $projectTags[view.primary_tag_id].options as option}
<Column
title={option.value}
cards={$cards.filter((c) => c.tags.map((t) => t.option_id).includes(option.id))}
{option}
columnCards={$cards.filter((c) => c.tags.map((t) => t.option_id).includes(option.id))}
/>
{/each}
<Column
title={`No ${$projectTags[view.primary_tag_id]?.title || 'tag'}`}
cards={$cards.filter((c) => c.tags.find((t) => t.tag_id)?.option_id == -1 || false)}
option={{
id: -1,
tag_id: view.primary_tag_id,
value: `No ${$projectTags[view.primary_tag_id].title}`
}}
columnCards={$cards.filter((c) => c.tags.find((t) => t.tag_id)?.option_id == -1 || false)}
/>
</div>
{/if}
</div>
</section>
{/if}
<style>
section {
display: flex;
flex-direction: column;
}
.grid {
display: flex;
flex-direction: row;
flex: 1;
}
</style>

View File

@ -1,5 +1,3 @@
import { toastAlert } from '../utils/toasts';
export interface Project {
id: number;
title: string;

View File

@ -7,6 +7,8 @@ export const currentView = writable(null as View | null);
export const currentModalCard = writable(-1);
export const currentDraggedCard = writable(null as Card | null);
export const cards = (() => {
const { subscribe, set, update } = writable([] as Card[]);
@ -28,6 +30,9 @@ export const cards = (() => {
update((cards) => cards.filter((c) => c.id !== card.id));
currentModalCard.set(-1);
});
},
reload: () => {
update((cards) => cards);
}
};
})();