Sort columns
This commit is contained in:
parent
98e54f94a6
commit
84a102ace8
|
@ -68,7 +68,10 @@ func UpdateView(c *fiber.Ctx) error {
|
|||
|
||||
view := types.View{ID: id}
|
||||
if err := c.BodyParser(&view); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Cannot parse request",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
|
||||
count, err := db.UpdateView(view)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package types
|
||||
|
||||
type View struct {
|
||||
ID int `json:"id"`
|
||||
ProjectID int `json:"project_id"`
|
||||
Title string `json:"title"`
|
||||
PrimaryTagID *int `json:"primary_tag_id"`
|
||||
SecondaryTagID *int `json:"secondary_tag_id"`
|
||||
SortTagID *int `json:"sort_tag_id"`
|
||||
SortDirection *string `json:"sort_direction"`
|
||||
ID int `json:"id"`
|
||||
ProjectID int `json:"project_id"`
|
||||
Title string `json:"title"`
|
||||
PrimaryTagID *int `json:"primary_tag_id"`
|
||||
SecondaryTagID *int `json:"secondary_tag_id"`
|
||||
SortTagID *int `json:"sort_tag_id"`
|
||||
SortDirection *int `json:"sort_direction"`
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import Menu from '../tuils/menu.svelte';
|
||||
import Menu from '../../../../utils/menu.svelte';
|
||||
|
||||
export let isOpen = false;
|
||||
export let choices: { id: number; value: string }[] = [];
|
||||
|
@ -7,7 +7,7 @@
|
|||
export let currentChoice: number | null;
|
||||
</script>
|
||||
|
||||
<Menu {isOpen}>
|
||||
<Menu bind:isOpen>
|
||||
{#each choices as choice}
|
||||
<div
|
||||
class="menu-item"
|
|
@ -0,0 +1,54 @@
|
|||
<script lang="ts">
|
||||
import Menu from '../../../../utils/menu.svelte';
|
||||
|
||||
export let isOpen = false;
|
||||
export let choices: { id: number; value: string }[] = [];
|
||||
export let onChoice = (id: number) => {};
|
||||
export let currentChoice: number | null;
|
||||
export let currentDirection: number | null;
|
||||
</script>
|
||||
|
||||
<Menu bind:isOpen>
|
||||
{#each choices as choice}
|
||||
<div
|
||||
class="menu-item"
|
||||
on:click={() => onChoice(choice.id)}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:keypress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
onChoice(choice.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span>{choice.value}</span>
|
||||
{#if currentChoice === choice.id}
|
||||
<span class="mark">
|
||||
{#if currentDirection === 1}
|
||||
↑
|
||||
{:else}
|
||||
↓
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</Menu>
|
||||
|
||||
<style lang="less">
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 5px 20px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.mark {
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { tick } from 'svelte';
|
||||
import AddIcon from '../../../icons/addIcon.svelte';
|
||||
import Menu from '../../../tuils/menu.svelte';
|
||||
import Menu from '../../../utils/menu.svelte';
|
||||
import ModalTagTypes from './modal_tag_types.svelte';
|
||||
import projectTags from '../../../../stores/projectTags';
|
||||
import { toastAlert } from '../../../../utils/toasts';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { tick } from 'svelte';
|
||||
import type { MeTag } from '../../../../stores/interfaces';
|
||||
import Menu from '../../../tuils/menu.svelte';
|
||||
import Menu from '../../../utils/menu.svelte';
|
||||
import projectTags from '../../../../stores/projectTags';
|
||||
import { toastAlert } from '../../../../utils/toasts';
|
||||
import ModalTagTypes from './modal_tag_types.svelte';
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="ts">
|
||||
import projectTags from '../../../../stores/projectTags';
|
||||
import { getTagTypeFromId, tagTypes } from '../../../../utils/tagTypes';
|
||||
import Menu from '../../../tuils/menu.svelte';
|
||||
import Menu from '../../../utils/menu.svelte';
|
||||
|
||||
export let type: number;
|
||||
export let isTagMenuOpen: boolean = false;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import api, { processError } from '../../../../utils/api';
|
||||
import status from '../../../../utils/status';
|
||||
import TrashIcon from '../../../icons/trashIcon.svelte';
|
||||
import Menu from '../../../tuils/menu.svelte';
|
||||
import Menu from '../../../utils/menu.svelte';
|
||||
|
||||
export const multiple: boolean = false;
|
||||
export let card: Card;
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
import type { Project, TagValue, View } from '../../stores/interfaces';
|
||||
import { cards, currentView, views } from '../../stores/smallStore';
|
||||
import projectTags from '../../stores/projectTags';
|
||||
import GroupMenu from './groupMenu.svelte';
|
||||
import GroupMenu from './card/header/menus/group_menu.svelte';
|
||||
import SortMenu from './card/header/menus/sort_menu.svelte';
|
||||
|
||||
export let project: Project;
|
||||
export let view: View;
|
||||
let groupMenuOpen = false;
|
||||
let sortMenuOpen = false;
|
||||
|
||||
async function setGroup(id: number): Promise<boolean> {
|
||||
if ($currentView == null) return false;
|
||||
|
@ -22,6 +24,26 @@
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
async function setSort(id: number): Promise<boolean> {
|
||||
if ($currentView == null) return false;
|
||||
|
||||
const view = {
|
||||
...$currentView,
|
||||
sort_tag_id: id,
|
||||
sort_direction: $currentView.sort_direction
|
||||
? $currentView.sort_tag_id === id
|
||||
? -$currentView.sort_direction
|
||||
: 1
|
||||
: 1
|
||||
};
|
||||
|
||||
const res = await views.edit(view);
|
||||
|
||||
if (res) currentView.set(view);
|
||||
|
||||
return res;
|
||||
}
|
||||
</script>
|
||||
|
||||
<header>
|
||||
|
@ -33,7 +55,7 @@
|
|||
class:defined={$currentView?.primary_tag_id}>Group</button
|
||||
>
|
||||
<GroupMenu
|
||||
isOpen={groupMenuOpen}
|
||||
bind:isOpen={groupMenuOpen}
|
||||
choices={Object.values($projectTags).map((tag) => ({ id: tag.id, value: tag.title }))}
|
||||
onChoice={async (id) => {
|
||||
if (!(await setGroup(id))) return;
|
||||
|
@ -44,7 +66,24 @@
|
|||
</div>
|
||||
<button class:disabled={true}>Sub-group</button>
|
||||
<button class:disabled={true}>Filter</button>
|
||||
<button class:disabled={true}>Sort</button>
|
||||
<div>
|
||||
<button
|
||||
on:click={() => (sortMenuOpen = !sortMenuOpen)}
|
||||
class:defined={$currentView?.sort_tag_id}>Sort</button
|
||||
>
|
||||
<SortMenu
|
||||
bind:isOpen={sortMenuOpen}
|
||||
choices={Object.values($projectTags)
|
||||
.filter((tag) => tag.id !== view?.primary_tag_id)
|
||||
.map((tag) => ({ id: tag.id, value: tag.title }))}
|
||||
onChoice={async (id) => {
|
||||
if (!(await setSort(id))) return;
|
||||
sortMenuOpen = false;
|
||||
}}
|
||||
currentChoice={view?.sort_tag_id}
|
||||
currentDirection={view?.sort_direction}
|
||||
/>
|
||||
</div>
|
||||
<button id="newButton" on:click={async () => cards.add(project.id, [])}>New</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
@ -35,9 +35,23 @@
|
|||
optionId={option.id}
|
||||
primary_tag_id={view.primary_tag_id}
|
||||
title={option.value}
|
||||
columnCards={$cards.filter((c) =>
|
||||
c.tags.map((t) => t.option_id).includes(option.id)
|
||||
)}
|
||||
columnCards={$cards
|
||||
.filter((c) => c.tags.map((t) => t.option_id).includes(option.id))
|
||||
.sort((a, b) => {
|
||||
if (!view?.sort_tag_id) return 0;
|
||||
const aTag = a.tags.find((t) => t.tag_id === view?.sort_tag_id);
|
||||
const bTag = b.tags.find((t) => t.tag_id === view?.sort_tag_id);
|
||||
|
||||
if (!aTag) return -(view?.sort_direction || 1);
|
||||
if (!bTag) return view?.sort_direction || 1;
|
||||
|
||||
const aValue = aTag.value || aTag.option_id || 0;
|
||||
const bValue = bTag.value || bTag.option_id || 0;
|
||||
|
||||
return aValue < bValue
|
||||
? view?.sort_direction || 1
|
||||
: -(view?.sort_direction || 1);
|
||||
})}
|
||||
projectId={project.id}
|
||||
/>
|
||||
{/each}
|
||||
|
|
Loading…
Reference in New Issue