Fix modalCard
This commit is contained in:
parent
5fa653f401
commit
7aee5b03bb
|
@ -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) {
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)}>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue