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
+ };
+})();