diff --git a/frontend/src/components/icons/editIcon.svelte b/frontend/src/components/icons/editIcon.svelte new file mode 100644 index 0000000..d50b6d2 --- /dev/null +++ b/frontend/src/components/icons/editIcon.svelte @@ -0,0 +1,6 @@ + + + diff --git a/frontend/src/components/project/header.svelte b/frontend/src/components/project/header.svelte index 634224e..b873caf 100644 --- a/frontend/src/components/project/header.svelte +++ b/frontend/src/components/project/header.svelte @@ -53,8 +53,8 @@
- - + + diff --git a/frontend/src/components/project/project.svelte b/frontend/src/components/project/project.svelte index 35b8a7f..13e9c21 100644 --- a/frontend/src/components/project/project.svelte +++ b/frontend/src/components/project/project.svelte @@ -3,7 +3,7 @@ import Column from './column.svelte'; import type { Project, View } from '../../stores/interfaces'; import projectTags from '../../stores/projectTags'; - import { cards, currentView } from '../../stores/smallStore'; + import { cards, currentView, views } from '../../stores/smallStore'; import Header from './header.svelte'; export let project: Project; diff --git a/frontend/src/components/sidebar.svelte b/frontend/src/components/sidebar.svelte index db3c38c..da03d7d 100644 --- a/frontend/src/components/sidebar.svelte +++ b/frontend/src/components/sidebar.svelte @@ -2,24 +2,48 @@ import { onMount } from 'svelte'; import api, { processError } from '../utils/api'; import type { Project, View } from '../stores/interfaces'; - import { currentView } from '../stores/smallStore'; + import { currentView, views } from '../stores/smallStore'; import ViewIcon from './icons/viewIcon.svelte'; + import projectTags from '../stores/projectTags'; + import EditIcon from './icons/editIcon.svelte'; export let project: Project; - let views: View[]; + + let viewEditId: number; + let viewEditValue: string; onMount(async () => { - const response = await api.get(`/v1/projects/${project.id}/views`); + console.log('aaa'); + await views.init(project.id); - if (response.status !== 200) { - processError(response, 'Failed to fetch views'); - return; + if ($views.length > 0) currentView.set($views[0]); + }); + + async function newView() { + if (!$views) return; + + const primaryTagId = + $currentView?.primary_tag_id || Object.values($projectTags).find((t) => true)?.id || -1; + + const newView = await views.add(project.id, 'New view', primaryTagId); + + if (!newView) return; + + currentView.set(newView); + viewEditId = newView.id; + viewEditValue = newView.title; + document.getElementById(`viewTitle-${newView.id}`)?.focus(); + } + + async function saveView(view: View) { + if (!view || !$views.includes(view)) return; + if (viewEditId === view.id && viewEditValue !== view.title) { + if (!(await views.update(view))) return; } - views = response.data; - - if (views.length > 0) currentView.set(views[0]); - }); + viewEditId = -1; + viewEditValue = ''; + } @@ -93,9 +160,46 @@ padding: 10px; } + li { + cursor: pointer; + padding: 0 10px; + } + + input { + padding: 10px; + border-radius: 5px; + background-color: transparent; + border: none; + color: inherit; + font-size: 17px; + width: 60%; + + &:focus { + outline: 0; + } + + &.inEdit { + background-color: #fff5; + } + } + span { padding-left: 10px; } + + button { + background-color: transparent; + border: none; + color: inherit; + font-size: 17px; + padding: 5px 0; + float: right; + cursor: pointer; + } + + .active { + background-color: #fff1; + } } .separator { diff --git a/frontend/src/stores/smallStore.ts b/frontend/src/stores/smallStore.ts index 973e83d..a3a51a3 100644 --- a/frontend/src/stores/smallStore.ts +++ b/frontend/src/stores/smallStore.ts @@ -57,3 +57,85 @@ export const cards = (() => { } }; })(); + +export const views = (() => { + const { subscribe, set, update } = writable([] as View[]); + + const init = async (projectId: number): Promise => { + const response = await api.get(`/v1/projects/${projectId}/views`); + + if (response.status !== status.OK) { + processError(response, 'Failed to get views'); + return false; + } + + set(response.data); + + return true; + }; + const add = async (projectId: number, title: string, primaryTagId: number): Promise => { + const response = await api.post(`/v1/views`, { + title, + project_id: projectId, + primary_tag_id: primaryTagId + }); + + if (response.status !== status.Created) { + processError(response, 'Failed to add view'); + return Promise.reject(); + } + + const view: View = { + id: response.data.id, + title: title, + project_id: projectId, + primary_tag_id: primaryTagId, + secondary_tag_id: 0 + }; + + update((views) => [...views, view]); + + return view; + }; + const remove = async (view: View): Promise => { + const response = await api.delete(`/v1/views/${view.id}`); + + if (response.status !== status.NoContent) { + processError(response, 'Failed to delete view'); + return false; + } + + update((views) => views.filter((v) => v.id !== view.id)); + + return true; + }; + + const edit = async (view: View): Promise => { + if (view.title === '') { + if (confirm('Are you sure you want to delete this view?')) { + return remove(view); + } else { + return false; + } + } + + const response = await api.put(`/v1/views/${view.id}`, view); + + if (response.status !== status.NoContent) { + processError(response, 'Failed to update view'); + return false; + } + + update((views) => views.map((v) => (v.id === view.id ? view : v))); + + return true; + }; + + return { + subscribe, + init, + add, + remove, + update: edit + }; +})();