diff --git a/frontend/.prettierrc b/frontend/.prettierrc index 9573023..6d696aa 100644 --- a/frontend/.prettierrc +++ b/frontend/.prettierrc @@ -4,5 +4,10 @@ "trailingComma": "none", "printWidth": 100, "plugins": ["prettier-plugin-svelte"], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] + "overrides": [ + { "files": "*.svelte", "options": { "parser": "svelte" } }, + { "files": "*.ts", "options": { "parser": "typescript" } }, + { "files": "*.js", "options": { "parser": "babel" } }, + { "files": "*.css", "options": { "parser": "css" } } + ] } diff --git a/frontend/src/api/cards.ts b/frontend/src/api/cards.ts new file mode 100644 index 0000000..4857f1a --- /dev/null +++ b/frontend/src/api/cards.ts @@ -0,0 +1,35 @@ +import type { Card } from '../stores/interfaces'; +import api, { processError } from '../utils/api'; +import status from '../utils/status'; + +export async function newCardApi(projectId: number): Promise { + const response = await api.post(`/v1/cards`, { + project_id: projectId, + title: 'Untitled', + content: '' + }); + + if (response.status !== status.Created) { + processError(response, 'Failed to create card'); + return Promise.reject(); + } + + const id: number = response.data.id; + + return { + id: id, + project_id: projectId, + title: 'Untitled', + content: '', + tags: [] + }; +} + +export async function deleteCardApi(cardID: number): Promise { + const response = await api.delete(`/v2/cards/${cardID}`); + + if (response.status !== status.NoContent) { + processError(response, 'Failed to delete card'); + return Promise.reject(); + } +} diff --git a/frontend/src/api/projects.ts b/frontend/src/api/projects.ts new file mode 100644 index 0000000..85bfde9 --- /dev/null +++ b/frontend/src/api/projects.ts @@ -0,0 +1,25 @@ +import { parseCards, type Card, type Project } from '../stores/interfaces'; +import api, { processError } from '../utils/api'; +import status from '../utils/status'; + +export async function getProjectAPI(projectId: number): Promise { + const response = await api.get(`/v1/projects/${projectId}`); + + if (response.status !== status.OK) { + processError(response, 'Failed to fetch project'); + return Promise.reject(); + } + + return response.data; +} + +export async function getProjectCardsAPI(projectId: number): Promise { + const response = await api.get(`/v1/projects/${projectId}/cards`); + + if (response.status !== status.OK) { + processError(response, 'Failed to fetch cards'); + return Promise.reject(); + } + + return parseCards(response.data); +} diff --git a/frontend/src/components/card.svelte b/frontend/src/components/card.svelte index b4a9158..61a3613 100644 --- a/frontend/src/components/card.svelte +++ b/frontend/src/components/card.svelte @@ -1,10 +1,11 @@ + +
+

{option.value}

+
    + {#each columnCards as card} + await deleteCard(card.id)} /> + {/each} +
+
+ + diff --git a/frontend/src/components/project.svelte b/frontend/src/components/project.svelte index 3d22e3c..72b85d3 100644 --- a/frontend/src/components/project.svelte +++ b/frontend/src/components/project.svelte @@ -2,105 +2,75 @@ import { onMount } from 'svelte'; import CardC from './card.svelte'; import { type Project, type Card, parseCards, type View } from '../stores/interfaces'; - import status from '../utils/status'; - import api, { processError } from '../utils/api'; import projectTags from '../stores/projectTags'; import currentView from '../stores/currentView'; - import Card from './card.svelte'; + import { deleteCardApi, newCardApi } from '../api/cards'; + import { getProjectAPI, getProjectCardsAPI } from '../api/projects'; + import Column from './column.svelte'; + import currentModalCard from '../stores/currentModalCard'; export let projectId: number; let project: Project; - let cards: Card[]; + let cards: Card[] = []; + let view: View | null = null; + let columns: { id: number; title: string; cards: Card[] }[] = []; onMount(async () => { - let response = await api.get(`/v1/projects/${projectId}`); + getProjectAPI(projectId).then((p) => { + project = p; + }); - if (response.status !== status.OK) { - processError(response, 'Failed to fetch project'); - return; - } - - project = response.data; - - response = await api.get(`/v1/projects/${projectId}/cards`); - - if (response.status === status.OK) { - cards = parseCards(response.data); - } else { - cards = []; - processError(response, 'Failed to fetch cards'); - return; - } + getProjectCardsAPI(projectId).then((c) => { + cards = parseCards(c); + loadColumns(); + }); if (!(await projectTags.init(projectId))) { return; } + + currentView.subscribe((v) => { + view = v; + loadColumns(); + }); }); - let modalID = -1; - - async function newCard() { - const response = await api.post(`/v1/cards`, { - project_id: projectId, - title: 'Untitled', - content: '' + function newCard() { + newCardApi(projectId).then((card) => { + cards = [...cards, card]; + currentModalCard.set(card.id); + loadColumns(); }); - - if (response.status !== status.Created) { - processError(response, 'Failed to create card'); - return; - } - - const id: number = response.data.id; - - let card: Card = { - id: id, - project_id: projectId, - title: 'Untitled', - content: '', - tags: [] - }; - - cards = [...cards, card]; - modalID = id; } - async function deleteCard(cardID: number) { - const response = await api.delete(`/v2/cards/${cardID}`); - - if (response.status !== status.NoContent) { - processError(response, 'Failed to delete card'); - return; - } - - cards = cards.filter((card) => card.id !== cardID); + function deleteCard(id: number) { + deleteCardApi(id).then(() => { + cards = cards.filter((card) => card.id !== id); + loadColumns(); + }); } - let view: View | null = null; - let columns: { id: number; title: string; cards: Card[] }[] = []; - - currentView.subscribe((v) => { - view = v; - if (!v) return; - let primary_tag_id = v.primary_tag_id; - columns = $projectTags[primary_tag_id].options.map((o) => { - return { - id: o.id, - title: o.value, - cards: cards.filter((c) => c.tags.map((t) => t.option_id).includes(o.id)) - }; - }); + function loadColumns() { + if (!view) return; + let primary_tag_id = view.primary_tag_id; + columns = + $projectTags[primary_tag_id]?.options.map((o) => { + return { + id: o.id, + title: o.value, + cards: cards.filter((c) => c.tags.map((t) => t.option_id).includes(o.id)) + }; + }) || []; columns.push({ id: -1, title: 'No tag', cards: cards.filter((c) => { const tag = c.tags.find((t) => t.tag_id === primary_tag_id); - return tag?.option_id == -1; }) }); - }); + } @@ -115,25 +85,14 @@

{project.title}

- {#if view} + {#if view && $projectTags[view.primary_tag_id]}
- {#each columns as column} -
-

{column.title}

-
    - {#each column.cards as card} - await deleteCard(card.id)} - /> - {/each} -
-
+ {#each $projectTags[view.primary_tag_id].options as option} + {/each}
{:else} -
    + {/if} {/if} diff --git a/frontend/src/components/sidebar.svelte b/frontend/src/components/sidebar.svelte index 2a686c5..18897e9 100644 --- a/frontend/src/components/sidebar.svelte +++ b/frontend/src/components/sidebar.svelte @@ -16,12 +16,12 @@ } views = response.data; + + if (views.length > 0) currentView.set(views[0]); }); - - - +