Sort columns

This commit is contained in:
Brieuc Dubois 2024-01-04 02:43:51 +01:00
parent 98e54f94a6
commit 84a102ace8
11 changed files with 130 additions and 21 deletions

View File

@ -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)

View File

@ -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"`
} }

View File

@ -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"

View File

@ -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>

View File

@ -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';

View File

@ -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';

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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}