Reformat project frontend
This commit is contained in:
parent
10b11f10dd
commit
16996de532
|
@ -18,7 +18,7 @@ export async function newCardApi(projectId: number): Promise<Card> {
|
|||
|
||||
return {
|
||||
id: id,
|
||||
project_id: projectId,
|
||||
projectId: projectId,
|
||||
title: 'Untitled',
|
||||
content: '',
|
||||
tags: []
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 |
|
@ -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 |
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
import { writable } from 'svelte/store';
|
||||
|
||||
export default writable(-1);
|
|
@ -1,4 +0,0 @@
|
|||
import { writable } from "svelte/store";
|
||||
import type { View } from "./interfaces";
|
||||
|
||||
export default writable(null as View | null);
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
})();
|
Loading…
Reference in New Issue