Merge pull request #35 from Gimanh/fix/ui-fixes

fix: ui improvements
This commit is contained in:
Gimanh
2026-03-08 22:15:40 +01:00
committed by GitHub
9 changed files with 44 additions and 25 deletions
@@ -12,9 +12,6 @@
</div>
<template v-else>
<h2 class="text-lg font-semibold mb-4">
{{ projectName }}
</h2>
<UTabs
v-model="activeTab"
:items="tabs"
@@ -60,16 +57,9 @@ import { useCollaborationPermissionsStore } from '@/stores/collaboration-permiss
import MembersList from './parts/members/MembersList.vue'
import RolesList from './parts/roles/RolesList.vue'
import { useAppRouteInfo } from '@/composables/useAppRouteInfo'
import { useGoalsStore } from '@/stores/goals.store'
const { t } = useI18n()
const { projectId } = useAppRouteInfo()
const goalsStore = useGoalsStore()
const projectName = computed(() => {
const goal = goalsStore.goalMap.get(projectId.value)
return goal?.name ?? ''
})
const collaborationStore = useCollaborationStore()
const rolesStore = useCollaborationRolesStore()
const permissionsStore = useCollaborationPermissionsStore()
@@ -15,10 +15,10 @@
<UButton
v-if="isValid"
icon="i-lucide-corner-down-left"
:label="t('collaboration.members.add')"
color="primary"
variant="ghost"
size="xs"
:aria-label="t('collaboration.members.add')"
@click="addMember"
/>
<UIcon
@@ -13,7 +13,7 @@
:key="status.id"
class="h-full max-w-[340px] min-w-[272px] shadow-lg gap-2 flex flex-col w-[91.666667%] rounded-lg"
>
<div class="bg-elevated rounded-lg p-2 px-3 flex items-center text-base h-10">
<div class="bg-elevated rounded-lg p-2 px-3 flex items-center text-base h-10 rounded-b-none">
<span class="grow truncate">
{{ t(status.name) }}
</span>
@@ -27,6 +27,7 @@
v-if="canAddTask"
:goal-id="kanbanStore.goalId"
:status-id="status.id"
class="mx-1"
/>
<draggable
@@ -17,7 +17,7 @@
</TvGoalLikeItem>
<template #content>
<UPageList class="w-full gap-2 flex flex-col">
<UPageList class="w-full gap-2 flex flex-col p-1">
<ProjectAddInput
v-if="!isArchive"
@add="handleAdd"
@@ -56,7 +56,7 @@
<TaskDetailPanel />
</template>
<template #footer>
<div class="flex justify-end gap-2 w-full">
<div class="flex justify-between gap-2 w-full">
<UButton
:label="t('common.delete')"
color="error"
@@ -15,6 +15,8 @@
class="flex-1"
:variant="isDark ? 'subtle' : 'soft'"
@update:model-value="handleAmountInput"
@focus="editing = true"
@blur="handleBlur"
>
<template #leading>
<UIcon
@@ -73,17 +75,23 @@ const tasksStore = useTasksStore()
const { isDark } = useColor()
const { canDeleteTask } = useGoalPermissions()
const localAmount = ref<string>(props.amount?.toString() ?? '')
const localAmount = ref<string>(formatDisplay(props.amount))
const tabResetKey = ref(0)
// don't overwrite what user is typing with server response like "2.00"
let editing:boolean = false
// Watch for external changes, skip if numeric value is the same
function formatDisplay(value: number | string | null): string {
if (value === null || value === '') return ''
const num = parseFloat(value.toString())
if (isNaN(num)) return ''
// drop trailing zeros: 2.00 -> 2, 2.50 -> 2.5
return String(num)
}
// only sync from server when user is not editing
watch(() => props.amount, (newAmount) => {
const incoming = newAmount === null ? null : parseFloat(newAmount.toString())
const current = localAmount.value === '' ? null : parseFloat(localAmount.value)
if (incoming !== current) {
localAmount.value = newAmount?.toString() ?? ''
}
if (editing) return
localAmount.value = formatDisplay(newAmount)
})
const tabItems = computed(() => [
@@ -135,6 +143,12 @@ function handleAmountInput(value: string | number) {
debouncedUpdateAmount(amount)
}
function handleBlur() {
editing = false
// clean up display: "2." -> "2", "02" -> "2"
localAmount.value = formatDisplay(localAmount.value || null)
}
async function clearAmount() {
localAmount.value = ''
await tasksStore.updateTaskAmount({
+7 -1
View File
@@ -1,7 +1,7 @@
<template>
<UDashboardPanel id="collaboration">
<template #header>
<UDashboardNavbar :title="t('collaboration.title')">
<UDashboardNavbar :title="`${projectName} - ${t('collaboration.title')}`">
<template #leading>
<UDashboardSidebarCollapse />
</template>
@@ -15,8 +15,14 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import CollaborationPanel from '@/components/features/collaboration/CollaborationPanel.vue'
import { useAppRouteInfo } from '@/composables/useAppRouteInfo'
import { useGoalsStore } from '@/stores/goals.store'
const { t } = useI18n()
const { projectId } = useAppRouteInfo()
const goalsStore = useGoalsStore()
const projectName = computed(() => goalsStore.goalMap.get(projectId.value)?.name ?? '')
</script>
+5 -1
View File
@@ -3,7 +3,7 @@
id="graph"
>
<template #header>
<UDashboardNavbar :title="t('graph.title')">
<UDashboardNavbar :title="`${projectName} - ${t('graph.title')}`">
<template #leading>
<TvCollapseSidebarDesktop />
</template>
@@ -18,15 +18,19 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import ProjectGraph from '@/components/features/graph/ProjectGraph.vue'
import TvCollapseSidebarDesktop from '@/components/features/base/TvCollapseSidebarDesktop.vue'
import TvTaskDetailOverlay from '@/components/features/tasks/TvTaskDetailOverlay.vue'
import { useAppRouteInfo } from '@/composables/useAppRouteInfo'
import { useGoalsStore } from '@/stores/goals.store'
import { useProjectDataLoader } from '@/composables/useProjectDataLoader'
const { t } = useI18n()
const { projectId } = useAppRouteInfo()
const goalsStore = useGoalsStore()
const projectName = computed(() => goalsStore.goalMap.get(projectId.value)?.name ?? '')
useProjectDataLoader(projectId)
</script>
+5 -1
View File
@@ -4,7 +4,7 @@
:ui="{ body: 'sm:p-4 sm:pb-0' }"
>
<template #header>
<UDashboardNavbar :title="t('kanban.title')">
<UDashboardNavbar :title="`${projectName} - ${t('kanban.title')}`">
<template #leading>
<TvCollapseSidebarDesktop />
</template>
@@ -19,15 +19,19 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import KanbanBoard from '@/components/features/kanban/KanbanBoard.vue'
import TvCollapseSidebarDesktop from '@/components/features/base/TvCollapseSidebarDesktop.vue'
import TvTaskDetailOverlay from '@/components/features/tasks/TvTaskDetailOverlay.vue'
import { useAppRouteInfo } from '@/composables/useAppRouteInfo'
import { useProjectDataLoader } from '@/composables/useProjectDataLoader'
import { useGoalsStore } from '@/stores/goals.store'
const { t } = useI18n()
const { projectId } = useAppRouteInfo()
const goalsStore = useGoalsStore()
const projectName = computed(() => goalsStore.goalMap.get(projectId.value)?.name ?? '')
useProjectDataLoader(projectId)
</script>