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}
|
view := types.View{ID: id}
|
||||||
if err := c.BodyParser(&view); err != nil {
|
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)
|
count, err := db.UpdateView(view)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
type View struct {
|
type View struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
ProjectID int `json:"project_id"`
|
ProjectID int `json:"project_id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
PrimaryTagID *int `json:"primary_tag_id"`
|
PrimaryTagID *int `json:"primary_tag_id"`
|
||||||
SecondaryTagID *int `json:"secondary_tag_id"`
|
SecondaryTagID *int `json:"secondary_tag_id"`
|
||||||
SortTagID *int `json:"sort_tag_id"`
|
SortTagID *int `json:"sort_tag_id"`
|
||||||
SortDirection *string `json:"sort_direction"`
|
SortDirection *int `json:"sort_direction"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Menu from '../tuils/menu.svelte';
|
import Menu from '../../../../utils/menu.svelte';
|
||||||
|
|
||||||
export let isOpen = false;
|
export let isOpen = false;
|
||||||
export let choices: { id: number; value: string }[] = [];
|
export let choices: { id: number; value: string }[] = [];
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
export let currentChoice: number | null;
|
export let currentChoice: number | null;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Menu {isOpen}>
|
<Menu bind:isOpen>
|
||||||
{#each choices as choice}
|
{#each choices as choice}
|
||||||
<div
|
<div
|
||||||
class="menu-item"
|
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">
|
<script lang="ts">
|
||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
import AddIcon from '../../../icons/addIcon.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 ModalTagTypes from './modal_tag_types.svelte';
|
||||||
import projectTags from '../../../../stores/projectTags';
|
import projectTags from '../../../../stores/projectTags';
|
||||||
import { toastAlert } from '../../../../utils/toasts';
|
import { toastAlert } from '../../../../utils/toasts';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
import type { MeTag } from '../../../../stores/interfaces';
|
import type { MeTag } from '../../../../stores/interfaces';
|
||||||
import Menu from '../../../tuils/menu.svelte';
|
import Menu from '../../../utils/menu.svelte';
|
||||||
import projectTags from '../../../../stores/projectTags';
|
import projectTags from '../../../../stores/projectTags';
|
||||||
import { toastAlert } from '../../../../utils/toasts';
|
import { toastAlert } from '../../../../utils/toasts';
|
||||||
import ModalTagTypes from './modal_tag_types.svelte';
|
import ModalTagTypes from './modal_tag_types.svelte';
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import projectTags from '../../../../stores/projectTags';
|
|
||||||
import { getTagTypeFromId, tagTypes } from '../../../../utils/tagTypes';
|
import { getTagTypeFromId, tagTypes } from '../../../../utils/tagTypes';
|
||||||
import Menu from '../../../tuils/menu.svelte';
|
import Menu from '../../../utils/menu.svelte';
|
||||||
|
|
||||||
export let type: number;
|
export let type: number;
|
||||||
export let isTagMenuOpen: boolean = false;
|
export let isTagMenuOpen: boolean = false;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import api, { processError } from '../../../../utils/api';
|
import api, { processError } from '../../../../utils/api';
|
||||||
import status from '../../../../utils/status';
|
import status from '../../../../utils/status';
|
||||||
import TrashIcon from '../../../icons/trashIcon.svelte';
|
import TrashIcon from '../../../icons/trashIcon.svelte';
|
||||||
import Menu from '../../../tuils/menu.svelte';
|
import Menu from '../../../utils/menu.svelte';
|
||||||
|
|
||||||
export const multiple: boolean = false;
|
export const multiple: boolean = false;
|
||||||
export let card: Card;
|
export let card: Card;
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
import type { Project, TagValue, View } from '../../stores/interfaces';
|
import type { Project, TagValue, View } from '../../stores/interfaces';
|
||||||
import { cards, currentView, views } from '../../stores/smallStore';
|
import { cards, currentView, views } from '../../stores/smallStore';
|
||||||
import projectTags from '../../stores/projectTags';
|
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 project: Project;
|
||||||
export let view: View;
|
export let view: View;
|
||||||
let groupMenuOpen = false;
|
let groupMenuOpen = false;
|
||||||
|
let sortMenuOpen = false;
|
||||||
|
|
||||||
async function setGroup(id: number): Promise<boolean> {
|
async function setGroup(id: number): Promise<boolean> {
|
||||||
if ($currentView == null) return false;
|
if ($currentView == null) return false;
|
||||||
|
@ -22,6 +24,26 @@
|
||||||
|
|
||||||
return res;
|
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>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
|
@ -33,7 +55,7 @@
|
||||||
class:defined={$currentView?.primary_tag_id}>Group</button
|
class:defined={$currentView?.primary_tag_id}>Group</button
|
||||||
>
|
>
|
||||||
<GroupMenu
|
<GroupMenu
|
||||||
isOpen={groupMenuOpen}
|
bind:isOpen={groupMenuOpen}
|
||||||
choices={Object.values($projectTags).map((tag) => ({ id: tag.id, value: tag.title }))}
|
choices={Object.values($projectTags).map((tag) => ({ id: tag.id, value: tag.title }))}
|
||||||
onChoice={async (id) => {
|
onChoice={async (id) => {
|
||||||
if (!(await setGroup(id))) return;
|
if (!(await setGroup(id))) return;
|
||||||
|
@ -44,7 +66,24 @@
|
||||||
</div>
|
</div>
|
||||||
<button class:disabled={true}>Sub-group</button>
|
<button class:disabled={true}>Sub-group</button>
|
||||||
<button class:disabled={true}>Filter</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>
|
<button id="newButton" on:click={async () => cards.add(project.id, [])}>New</button>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -35,9 +35,23 @@
|
||||||
optionId={option.id}
|
optionId={option.id}
|
||||||
primary_tag_id={view.primary_tag_id}
|
primary_tag_id={view.primary_tag_id}
|
||||||
title={option.value}
|
title={option.value}
|
||||||
columnCards={$cards.filter((c) =>
|
columnCards={$cards
|
||||||
c.tags.map((t) => t.option_id).includes(option.id)
|
.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}
|
projectId={project.id}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
Loading…
Reference in New Issue