Reformat project frontend

This commit is contained in:
Brieuc Dubois 2023-12-31 17:50:04 +01:00
parent 10b11f10dd
commit 16996de532
13 changed files with 152 additions and 174 deletions

View File

@ -18,7 +18,7 @@ export async function newCardApi(projectId: number): Promise<Card> {
return {
id: id,
project_id: projectId,
projectId: projectId,
title: 'Untitled',
content: '',
tags: []

View File

@ -1,35 +1,23 @@
<script lang="ts">
import currentModalCard from '../stores/currentModalCard';
import type { Card } from '../stores/interfaces';
import projectTags from '../stores/projectTags';
import { currentModalCard } from '../stores/smallStore';
import ModalCard from './modal_card.svelte';
export let card: Card;
let showModal: boolean = $currentModalCard == card.id;
export let onDelete: () => void;
function editCard() {
showModal = true;
}
function cancelEdit() {
showModal = false;
}
function editCardHandler(event: KeyboardEvent) {
if (event.key === 'Enter') {
editCard();
}
}
</script>
<div
class="card"
tabindex="0"
draggable={true}
on:click={editCard}
on:click={() => ($currentModalCard = card.id)}
role="button"
on:keydown={editCardHandler}
on:keydown={(e) => {
if (e.key === 'Enter') {
$currentModalCard = card.id;
}
}}
>
<div class="title">{card.title}</div>
{#if card.tags}
@ -49,4 +37,4 @@
{/if}
</div>
<ModalCard bind:show={showModal} bind:card onCancel={cancelEdit} {onDelete} />
<ModalCard bind:card />

View File

@ -2,21 +2,15 @@
import type { Card, TagOption } from '../stores/interfaces';
import CardC from './card.svelte';
export let tag_id: number;
export let option: TagOption;
export let title: string;
export let cards: Card[] = [];
export let deleteCard: (id: number) => void;
let columnCards = cards.filter(
(card) => card.tags.find((t) => t.tag_id === tag_id)?.option_id === option.id
);
</script>
<div class="column">
<h3>{option.value}</h3>
<h3>{title}</h3>
<ul>
{#each columnCards as card}
<CardC {card} onDelete={async () => await deleteCard(card.id)} />
{#each cards as card}
<CardC {card} />
{/each}
</ul>
</div>

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="white" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 12 12z"
/>
</svg>

After

Width:  |  Height:  |  Size: 260 B

View File

@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M3 6h18"></path>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m2-2h10a2 2 0 0 1 2 2v2H5V6a2 2 0 0 1 2-2z"
></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>

After

Width:  |  Height:  |  Size: 417 B

View File

@ -3,22 +3,22 @@
import type { Card } from '../stores/interfaces';
import api, { processError } from '../utils/api';
import status from '../utils/status';
import { cards, currentModalCard } from '../stores/smallStore';
import TrashIcon from './icons/trashIcon.svelte';
import CloseIcon from './icons/closeIcon.svelte';
export let show: boolean;
export let card: Card;
export let onCancel: () => void;
export let onDelete: () => void;
let tempCard: Card = { ...card };
async function save(closeModal: boolean = true) {
if (
card.project_id != tempCard.project_id ||
card.projectId != tempCard.projectId ||
card.title !== tempCard.title ||
card.content !== tempCard.content
) {
const response = await api.put(`/v1/cards/${card.id}`, {
project_id: tempCard.project_id,
project_id: tempCard.projectId,
title: tempCard.title,
content: tempCard.content
});
@ -30,11 +30,11 @@
card = { ...tempCard };
}
if (closeModal) show = false;
if (closeModal) currentModalCard.set(-1);
}
</script>
{#if show}
{#if $currentModalCard == card.id}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="modal" on:click={() => save(true)}>
@ -42,39 +42,11 @@
<div class="header">
<input class="title" bind:value={tempCard.title} on:blur={() => save(false)} />
<div class="buttons">
<button on:click={onDelete}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M3 6h18"></path>
<path
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m2-2h10a2 2 0 0 1 2 2v2H5V6a2 2 0 0 1 2-2z"
></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
<button on:click={() => cards.remove(card)}>
<TrashIcon />
</button>
<button on:click={onCancel}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="white"
viewBox="0 0 24 24"
>
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 12 12z"
/>
</svg>
<button on:click={() => currentModalCard.set(-1)}>
<CloseIcon />
</button>
</div>
</div>

View File

@ -12,7 +12,7 @@
if (newTagName === '') return;
const response = await api.post(`/v1/tags`, {
project_id: card.project_id,
project_id: card.projectId,
title: newTagName,
type: 0
});

View File

@ -1,18 +1,16 @@
<script lang="ts">
import { onMount } from 'svelte';
import CardC from './card.svelte';
import { type Project, type Card, parseCards, type View } from '../stores/interfaces';
import projectTags from '../stores/projectTags';
import currentView from '../stores/currentView';
import { deleteCardApi, newCardApi } from '../api/cards';
import { getProjectAPI, getProjectCardsAPI } from '../api/projects';
import Column from './column.svelte';
import currentModalCard from '../stores/currentModalCard';
import { cards, currentModalCard, currentView } from '../stores/smallStore';
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[] }[] = [];
@ -21,8 +19,9 @@
project = p;
});
getProjectCardsAPI(projectId).then((c) => {
cards = parseCards(c);
cards.init(projectId);
cards.subscribe((c) => {
loadColumns();
});
@ -36,21 +35,6 @@
});
});
function newCard() {
newCardApi(projectId).then((card) => {
cards = [...cards, card];
currentModalCard.set(card.id);
loadColumns();
});
}
function deleteCard(id: number) {
deleteCardApi(id).then(() => {
cards = cards.filter((card) => card.id !== id);
loadColumns();
});
}
function loadColumns() {
if (!view) return;
let primary_tag_id = view.primary_tag_id;
@ -59,18 +43,23 @@
return {
id: o.id,
title: o.value,
cards: cards.filter((c) => c.tags.map((t) => t.option_id).includes(o.id))
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;
})
cards:
$cards?.filter((c) => {
const tag = c.tags.find((t) => t.tag_id === primary_tag_id);
return tag?.option_id == -1;
}) || []
});
}
async function newCard() {
await cards.add(projectId);
}
</script>
<svelte:head>
@ -85,40 +74,26 @@
<h2>{project.title}</h2>
<button on:click={newCard}>New card</button>
</header>
{#if view && $projectTags[view.primary_tag_id]}
{#if view && $projectTags[view.primary_tag_id] && $cards}
<div class="grid">
{#each $projectTags[view.primary_tag_id].options as option}
<Column tag_id={view.primary_tag_id} {option} bind:cards deleteCard />
<Column
title={option.value}
cards={$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)}
/>
</div>
{:else}
<!-- <ul>
{#if cards}
{#each cards as card}
<CardC
{card}
showModal={modalID === card.id}
onDelete={async () => await deleteCard(card.id)}
/>
{/each}
{/if}
</ul> -->
{/if}
</div>
{/if}
<style>
#project .grid {
.grid {
display: flex;
flex-direction: row;
}
#project .column {
width: 200px;
margin: 0 10px;
}
#project .column h3 {
text-align: center;
}
</style>

View File

@ -2,7 +2,7 @@
import { onMount } from 'svelte';
import api, { processError } from '../utils/api';
import type { View } from '../stores/interfaces';
import currentView from '../stores/currentView';
import { currentView } from '../stores/smallStore';
export let projectID: number;
let views: View[];

View File

@ -1,3 +0,0 @@
import { writable } from 'svelte/store';
export default writable(-1);

View File

@ -1,4 +0,0 @@
import { writable } from "svelte/store";
import type { View } from "./interfaces";
export default writable(null as View | null);

View File

@ -1,68 +1,68 @@
import { toastAlert } from "../utils/toasts";
import { toastAlert } from '../utils/toasts';
export interface Project {
id: number;
title: string;
id: number;
title: string;
}
export interface Card {
id: number;
project_id: number;
title: string;
content: string;
tags: TagValue[];
id: number;
projectId: number;
title: string;
content: string;
tags: TagValue[];
}
export interface TagValue {
card_id: number;
tag_id: number;
option_id: number;
value: string;
card_id: number;
tag_id: number;
option_id: number;
value: string;
}
export interface MeTag {
id: number;
project_id: number;
title: string;
type: number;
options: TagOption[];
id: number;
project_id: number;
title: string;
type: number;
options: TagOption[];
}
export interface TagOption {
id: number;
tag_id: number;
value: string;
id: number;
tag_id: number;
value: string;
}
export interface View {
id: number;
project_id: number;
primary_tag_id: number;
secondary_tag_id: number;
title: string;
id: number;
project_id: number;
primary_tag_id: number;
secondary_tag_id: number;
title: string;
}
export function parseCard (c: any) {
let card: Card = c;
if (card.tags == null) card.tags = [];
return card;
};
export function parseCards (cards: any) {
if (cards == null) return [];
return cards.map((c: any) => parseCard(c));
export function parseCard(c: any) {
let card: Card = c;
if (card.tags == null) card.tags = [];
return card;
}
export function parseMeTag (t: any) {
let tag: MeTag = t;
if (tag.options == null) tag.options = [];
return tag;
export function parseCards(cards: any) {
if (cards == null) return [];
return cards.map((c: any) => parseCard(c));
}
export function parseMeTags (tags: any) {
if (tags == null) return {};
return tags.map(parseMeTag).reduce((acc: any, tag: MeTag) => {
acc[tag.id] = tag;
return acc;
});
}
export function parseMeTag(t: any) {
let tag: MeTag = t;
if (tag.options == null) tag.options = [];
return tag;
}
export function parseMeTags(tags: any) {
if (tags == null) return {};
return tags.map(parseMeTag).reduce((acc: any, tag: MeTag) => {
acc[tag.id] = tag;
return acc;
});
}

View File

@ -0,0 +1,33 @@
import { writable } from 'svelte/store';
import { parseCards, type Card, type View } from './interfaces';
import { deleteCardApi, newCardApi } from '../api/cards';
import { getProjectCardsAPI } from '../api/projects';
export const currentView = writable(null as View | null);
export const currentModalCard = writable(-1);
export const cards = (() => {
const { subscribe, set, update } = writable([] as Card[]);
return {
subscribe,
init: async (projectId: number) => {
getProjectCardsAPI(projectId).then((c) => {
set(parseCards(c));
});
},
add: async (projectId: number) => {
await newCardApi(projectId).then((card) => {
currentModalCard.set(card.id);
update((cards) => [...cards, card]);
});
},
remove: async (card: Card) => {
await deleteCardApi(card.id).then(() => {
update((cards) => cards.filter((c) => c.id !== card.id));
currentModalCard.set(-1);
});
}
};
})();