Fix modalCard

This commit is contained in:
Brieuc Dubois 2024-01-09 02:06:11 +01:00
parent 5fa653f401
commit 7aee5b03bb
13 changed files with 173 additions and 144 deletions

View File

@ -17,11 +17,16 @@ async function create(projectId: number): Promise<number | null> {
return response.data.id;
}
async function update(card: Card): Promise<boolean> {
const response = await api.put(`/v1/cards/${card.id}`, {
project_id: card.project_id,
title: card.title,
content: card.content
async function update(
cardId: number,
projectId: number,
title: string,
content: string
): Promise<boolean> {
const response = await api.put(`/v1/cards/${cardId}`, {
project_id: projectId,
title: title,
content: content
});
if (response.status !== status.NoContent) {

View File

@ -4,9 +4,9 @@ import TagOption from '$lib/types/TagOption';
import api, { processError } from '$lib/utils/api';
import status from '$lib/utils/status';
async function create(project: Project, title: string, type: number): Promise<number | null> {
async function create(projectId: number, title: string, type: number): Promise<number | null> {
const response = await api.post(`/v1/tags/`, {
project_id: project.id,
project_id: projectId,
title,
type
});
@ -19,8 +19,11 @@ async function create(project: Project, title: string, type: number): Promise<nu
return response.data.id;
}
async function update(tag: ProjectTag): Promise<boolean> {
const response = await api.put(`/v1/tags/${tag.id}`, tag);
async function update(tagId: number, title: string, type: number): Promise<boolean> {
const response = await api.put(`/v1/tags/${tagId}`, {
title,
type
});
if (response.status !== status.NoContent) {
processError(response, 'Failed to update tag');

View File

@ -2,7 +2,7 @@
import currentDraggedCard from '$lib/stores/currentDraggedCard';
import currentModalCard from '$lib/stores/currentModalCard';
import type Card from '$lib/types/Card';
// import ModalCard from './ModalCard.svelte';
import ModalCard from './ModalCard.svelte';
export let card: Card;
</script>
@ -34,7 +34,7 @@
</div>
</div>
<!-- <ModalCard bind:card /> -->
<ModalCard {card} />
<style lang="less">
.card {

View File

@ -2,9 +2,9 @@
import CloseIcon from '$lib/components/icons/CloseIcon.svelte';
import TrashIcon from '$lib/components/icons/TrashIcon.svelte';
import ModalTags from '$lib/components/tags/ModalTags.svelte';
import cards from '$lib/stores/cards';
import currentModalCard from '$lib/stores/currentModalCard';
import type Card from '$lib/types/Card';
import { cards } from '$lib/types/Card';
export let card: Card;
@ -13,19 +13,11 @@
async function save(closeModal: boolean = true) {
if (card.title !== newTitle || card.content !== newContent) {
console.log('saving');
if (
await cards.edit({
...card,
title: newTitle,
content: newContent
})
) {
card.title = newTitle;
card.content = newContent;
}
if (!(await card.update(newTitle, newContent))) return;
}
if (closeModal) currentModalCard.set(null);
cards.reload();
}
</script>
@ -37,7 +29,7 @@
<div class="header">
<input class="title" bind:value={newTitle} on:blur={() => save(false)} />
<div class="buttons">
<button on:click={() => cards.remove(card)}>
<button on:click={() => card.delete()}>
<TrashIcon />
</button>
<button on:click={() => currentModalCard.set(null)}>

View File

@ -1,12 +1,12 @@
<script lang="ts">
import AddIcon from '$lib/components/icons/AddIcon.svelte';
import Menu from '$lib/components/menu/Menu.svelte';
import project_tags from '$lib/stores/projectTags';
import { toastAlert } from '$lib/utils/toasts';
import { tick } from 'svelte';
import ModalTagTypes from './ModalTagTypes.svelte';
import type Project from '$lib/types/Project';
import ProjectTag from '$lib/types/ProjectTag';
export let projectId: number;
export let project: Project;
let isOpen = false;
@ -23,11 +23,10 @@
async function createTag() {
if (title == '') return;
if (!projectId) {
toastAlert('Failed to create tag', `ProjectId is ${projectId}`);
return;
}
await project_tags.add(projectId, title, typeId);
const res = await ProjectTag.create(project, title, typeId);
if (!res) return;
isOpen = false;
}
</script>

View File

@ -1,18 +1,16 @@
<script lang="ts">
import type Card from '$lib/types/Card';
import type MeTag from '$lib/types/MeTag';
import type TagValue from '$lib/types/TagValue';
import type CardTag from '$lib/types/CardTag';
import type ProjectTag from '$lib/types/ProjectTag';
import ModalTagTitle from './ModalTagTitle.svelte';
import ModalTagValue from './ModalTagValue.svelte';
export let projectTag: MeTag;
export let tagValue: TagValue | undefined;
export let projectTag: ProjectTag;
export let cardTag: CardTag | undefined;
export let card: Card;
</script>
{#if projectTag}
<tr>
<ModalTagTitle {projectTag} />
<ModalTagValue {projectTag} {tagValue} {card} />
</tr>
{/if}
<tr>
<ModalTagTitle {projectTag} />
<ModalTagValue {projectTag} {cardTag} {card} />
</tr>

View File

@ -1,18 +1,18 @@
<script lang="ts">
import Menu from '$lib/components/menu/Menu.svelte';
import project_tags from '$lib/stores/projectTags';
import type MeTag from '$lib/types/MeTag';
import { toastAlert } from '$lib/utils/toasts';
import { tick } from 'svelte';
import ModalTagTypes from './ModalTagTypes.svelte';
import type ProjectTag from '$lib/types/ProjectTag';
export let projectTag: MeTag;
export let projectTag: ProjectTag;
let askConfirm: boolean = false;
let titleInput: HTMLInputElement;
let isMenuOpen: boolean = false;
let lastTitle: string = projectTag.title;
let newTitle: string = projectTag.title;
let newType: number = projectTag.type;
async function openMenu() {
isMenuOpen = !isMenuOpen;
@ -21,16 +21,14 @@
}
async function saveProjectTag() {
if (projectTag.title === lastTitle) return;
if (newTitle === projectTag.title) return;
if (projectTag.title === '') {
if (newTitle === '') {
toastAlert('Tag title cannot be empty');
return;
}
await project_tags.update(projectTag);
lastTitle = projectTag.title;
return await projectTag.update(newTitle, newType);
}
</script>
@ -48,17 +46,11 @@
>
{projectTag.title}
</div>
<Menu
bind:isOpen={isMenuOpen}
onLeave={() => {
askConfirm = false;
projectTag.title = lastTitle;
}}
>
<Menu bind:isOpen={isMenuOpen} onLeave={() => (askConfirm = false)}>
<div class="menu-items">
<input
bind:this={titleInput}
bind:value={projectTag.title}
bind:value={newTitle}
on:blur={saveProjectTag}
on:keydown={(e) => {
if (e.key === 'Enter') {
@ -67,11 +59,9 @@
}}
/>
<ModalTagTypes
type={projectTag.type}
type={newType}
onChoice={async (id) => {
projectTag.type = id;
await project_tags.update(projectTag);
saveProjectTag();
}}
/>
{#if askConfirm}
@ -79,8 +69,8 @@
<span>Confirm?</span>
<div>
<button
on:click={() => {
project_tags.delete(projectTag.id);
on:click={async () => {
if (!(await projectTag.delete())) return;
isMenuOpen = false;
}}>✓</button
>

View File

@ -1,12 +1,12 @@
<script lang="ts">
import type Card from '$lib/types/Card';
import type MeTag from '$lib/types/MeTag';
import type TagValue from '$lib/types/TagValue';
import type CardTag from '$lib/types/CardTag';
import type ProjectTag from '$lib/types/ProjectTag';
import { getTagTypeFromId } from '$lib/utils/tagTypes';
import SelectTags from './SelectTags.svelte';
export let projectTag: MeTag;
export let tagValue: TagValue | undefined;
export let projectTag: ProjectTag;
export let cardTag: CardTag | undefined;
export let card: Card;
let tagType = getTagTypeFromId(projectTag.type);
@ -15,7 +15,7 @@
{#if tagType}
<td>
{#if tagType?.hasOptions}
<SelectTags multiple={false} {projectTag} {card} {tagValue} />
<SelectTags multiple={false} {projectTag} {card} {cardTag} />
{:else if !tagType?.hasOptions}
<input />
{/if}

View File

@ -1,6 +1,6 @@
<script lang="ts">
import ProjectTags from '$lib/stores/projectTags';
import type Card from '$lib/types/Card';
import { projectTags } from '$lib/types/ProjectTag';
import ModalNewTag from './ModalNewTag.svelte';
import ModalTag from './ModalTag.svelte';
@ -8,10 +8,12 @@
</script>
<table>
{#if card.tags}
{#each Object.values($ProjectTags) as projectTag}
<ModalTag tagValue={card.tags.find((t) => t.tag_id === projectTag.id)} {projectTag} {card} />
{/each}
{/if}
<ModalNewTag projectId={card.project_id} />
{#each $projectTags as projectTag}
<ModalTag
cardTag={card.cardTags.find((t) => t.projectTag === projectTag)}
{projectTag}
{card}
/>
{/each}
<ModalNewTag project={card.project} />
</table>

View File

@ -1,88 +1,42 @@
<script lang="ts">
import Menu from '$lib/components/menu/Menu.svelte';
import cards from '$lib/stores/cards';
import project_tags from '$lib/stores/projectTags';
import type Card from '$lib/types/Card';
import type MeTag from '$lib/types/MeTag';
import type TagValue from '$lib/types/TagValue';
import { cards } from '$lib/types/Card';
import CardTag from '$lib/types/CardTag';
import type ProjectTag from '$lib/types/ProjectTag';
import type TagOption from '$lib/types/TagOption';
import api, { processError } from '$lib/utils/api';
import status from '$lib/utils/status';
import { updatimport } from { updateCardTagApi };
import TrashIcon from '../icons/TrashIcon.svelte';
export const multiple: boolean = false;
export let card: Card;
export let projectTag: MeTag;
export let tagValue: TagValue | undefined;
export let projectTag: ProjectTag;
export let cardTag: CardTag | undefined;
let lastTagValue = { ...tagValue };
let newOption: string = '';
$: tagOption = projectTag.options.find((o) => o.id === tagValue?.option_id);
let isOpen = false;
async function selectOption(option_id: number | null) {
if (lastTagValue.option_id === option_id) {
async function selectOption(option: TagOption | null) {
if (cardTag?.option === option) {
isOpen = false;
return;
}
if (tagValue) {
await updateCardTagApi(card.id, projectTag.id, option_id, tagValue.value);
card.tags = card.tags.map((t) => {
if (t.tag_id === projectTag.id) {
t.option_id = option_id;
}
return t;
});
tagValue = { ...tagValue, option_id };
if (cardTag) {
if (option) await cardTag.update(option, null);
else await cardTag.delete();
} else {
const response = await api.post(`/v1/cards/${card.id}/tags/${projectTag.id}`, {
option_id,
value: ''
});
if (response.status !== status.Created) {
processError(response, 'Failed to create tag');
return;
}
tagValue = {
card_id: card.id,
tag_id: projectTag.id,
option_id,
value: ''
};
card.tags.push(tagValue);
if (option) await CardTag.create(card, projectTag, option, null);
}
lastTagValue = { ...tagValue };
isOpen = false;
cards.reload();
}
async function deleteOption() {
const response = await api.delete(`/v1/cards/${card.id}/tags/${projectTag.id}`);
if (response.status !== status.NoContent) {
processError(response, 'Failed to delete tag');
return;
}
tagValue = undefined;
card.tags = card.tags.filter((t) => t.tag_id !== projectTag.id);
cards.reload();
}
function createOption() {
async function createOption() {
if (!newOption) return;
project_tags.addOption(projectTag.id, newOption);
if (!(await projectTag.addOption(newOption))) return;
newOption = '';
}
</script>
@ -99,10 +53,10 @@
}}
>
<div class="tags">
{#if tagValue}
{#if cardTag}
<span class="tag">
{tagOption?.value}
<button class="real" on:click={() => deleteOption()}>✗</button>
{cardTag.value}
<button class="real" on:click={() => selectOption(null)}>✗</button>
</span>
{/if}
</div>
@ -116,19 +70,19 @@
{#each projectTag.options as option}
<div
class="option"
on:click={() => selectOption(option.id)}
on:click={() => selectOption(option)}
tabindex="0"
role="button"
on:keydown={(e) => {
if (e.key === 'Enter') {
selectOption(option.id);
selectOption(option);
}
}}
>
<span class="value">{option.value}</span>
<button
on:click|stopPropagation={() => {
project_tags.deleteOption(projectTag.id, option.id);
projectTag.deleteOption(option);
}}><TrashIcon size={16} /></button
>
</div>

View File

@ -120,6 +120,37 @@ export default class Card {
return await cardTag.update(tagOption, value);
}
async updateTitle(title: string): Promise<boolean> {
const res = await cardsApi.update(this.id, this.project.id, title, this.content);
if (!res) return false;
this._title = title;
return true;
}
async updateContent(content: string): Promise<boolean> {
const res = await cardsApi.update(this.id, this.project.id, this.title, content);
if (!res) return false;
this._content = content;
return true;
}
async update(title: string, content: string): Promise<boolean> {
const res = await cardsApi.update(this.id, this.project.id, title, content);
if (!res) return false;
this._title = title;
this._content = content;
return true;
}
static parse(json: any): Card | null;
static parse(json: any, project: Project | null | undefined): Card | null;

View File

@ -3,9 +3,12 @@
import { get, writable } from 'svelte/store';
import TagOption from './TagOption';
import Project from './Project';
import projectTagsApi from '$lib/api/projectTagsApi';
export const projectTags = writable([] as ProjectTag[]);
export const ProjectTagTypes = {};
export default class ProjectTag {
private _id: number;
private _project: Project;
@ -58,6 +61,46 @@ export default class ProjectTag {
return null;
}
static async create(project: Project, title: string, type: number): Promise<ProjectTag | null> {
const response = await projectTagsApi.create(project.id, title, type);
if (!response) return null;
const projectTag = new ProjectTag(0, project, title, type, []);
projectTags.update((projectTags) => [...projectTags, projectTag]);
return projectTag;
}
async delete(): Promise<boolean> {
return await projectTagsApi.delete(this.id);
}
async update(title: string, type: number): Promise<boolean> {
return await projectTagsApi.update(this.id, title, type);
}
async addOption(title: string): Promise<TagOption | null> {
const option = await TagOption.create(this, title);
if (!option) return null;
this._options = [...this._options, option];
return option;
}
async deleteOption(option: TagOption): Promise<boolean> {
const success = await option.delete();
if (!success) return false;
this._options = this._options.filter((o) => o.id !== option.id);
return true;
}
static parse(json: any): ProjectTag | null;
static parse(json: any, project: Project | null | undefined): ProjectTag | null;

View File

@ -24,6 +24,18 @@ export default class TagOption {
return this._value;
}
static async create(projectTag: ProjectTag, value: string): Promise<TagOption | null> {
const id = await tagsOptions.create(projectTag.id, value);
if (!id) return null;
return new TagOption(id, projectTag, value);
}
async delete(): Promise<boolean> {
return await tagsOptions.delete(this._id, this.projectTag.id);
}
async setValue(value: string): Promise<boolean> {
const res = await tagsOptions.update(this._id, this.projectTag.id, value);