Create & edit views
This commit is contained in:
parent
5bc43aaba2
commit
bc566280a6
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 576 512">
|
||||
<path
|
||||
d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"
|
||||
fill="#fff"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 632 B |
|
@ -53,8 +53,8 @@
|
|||
<div>
|
||||
<button class:disabled={true}>Sub-group</button>
|
||||
</div>
|
||||
<button>Filter</button>
|
||||
<button>Sort</button>
|
||||
<button class:disabled={true}>Filter</button>
|
||||
<button class:disabled={true}>Sort</button>
|
||||
<button id="newButton" on:click={async () => cards.add(project.id, getEmptyTags())}>New</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
|
@ -32,15 +56,46 @@
|
|||
{#if views}
|
||||
<h2>{project.title}</h2>
|
||||
<ul>
|
||||
{#each views as view}
|
||||
<li>
|
||||
<ViewIcon />
|
||||
<!-- on:click={() => {
|
||||
{#each $views as view}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->
|
||||
<li
|
||||
on:click={() => currentView.set(view)}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
currentView.set(view);
|
||||
}} -->
|
||||
<span>
|
||||
{view.title}
|
||||
</span>
|
||||
}
|
||||
}}
|
||||
class:active={$currentView === view}
|
||||
>
|
||||
<ViewIcon />
|
||||
<input
|
||||
type="text"
|
||||
readonly={viewEditId !== view.id}
|
||||
bind:value={view.title}
|
||||
class:inEdit={viewEditId === view.id}
|
||||
on:blur={() => saveView(view)}
|
||||
id="viewTitle-{view.id}"
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
saveView(view);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
on:click={() => {
|
||||
if (viewEditId === view.id) {
|
||||
saveView(view);
|
||||
} else {
|
||||
viewEditId = view.id;
|
||||
viewEditValue = view.title;
|
||||
document.getElementById(`viewTitle-${view.id}`)?.focus();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
@ -49,7 +104,19 @@
|
|||
</div>
|
||||
<div>
|
||||
<div class="separator"></div>
|
||||
<div id="newView" on:click={() => {}}>+ New view</div>
|
||||
<div
|
||||
id="newView"
|
||||
on:click={newView}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
newView();
|
||||
}
|
||||
}}
|
||||
>
|
||||
+ New view
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
@ -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 {
|
||||
|
|
|
@ -57,3 +57,85 @@ export const cards = (() => {
|
|||
}
|
||||
};
|
||||
})();
|
||||
|
||||
export const views = (() => {
|
||||
const { subscribe, set, update } = writable([] as View[]);
|
||||
|
||||
const init = async (projectId: number): Promise<boolean> => {
|
||||
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<View> => {
|
||||
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<boolean> => {
|
||||
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<boolean> => {
|
||||
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
|
||||
};
|
||||
})();
|
||||
|
|
Loading…
Reference in New Issue