home page
This commit is contained in:
1 parent
adf182a914
commit
fcc438e5fe
5 files changed
+35
-20
No files matched your search
@@ -53,7 +53,7 @@ export async function itemsList(params: {
|
|||||||
page: number
|
page: number
|
||||||
limit: number
|
limit: number
|
||||||
text?: string | null
|
text?: string | null
|
||||||
status?: string | null
|
status?: string | string[] | null
|
||||||
assigneeId?: string | null
|
assigneeId?: string | null
|
||||||
projectId?: string | null
|
projectId?: string | null
|
||||||
}) {
|
}) {
|
||||||
@@ -63,7 +63,7 @@ export async function itemsList(params: {
|
|||||||
projectId: params.projectId,
|
projectId: params.projectId,
|
||||||
text: params.text,
|
text: params.text,
|
||||||
assigneeId: params.assigneeId,
|
assigneeId: params.assigneeId,
|
||||||
status: params.status,
|
status: Array.isArray(params.status) ? params.status.join(',') : params.status,
|
||||||
})
|
})
|
||||||
return apiClient.get<{ list: Issue[]; meta: Meta }>(`/api/issues/list${query}`)
|
return apiClient.get<{ list: Issue[]; meta: Meta }>(`/api/issues/list${query}`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ import { reactive, ref } from 'vue'
|
|||||||
import { useIssueStore } from '@/stores/issue.ts'
|
import { useIssueStore } from '@/stores/issue.ts'
|
||||||
import { useProjectStore } from '@/stores/project.ts'
|
import { useProjectStore } from '@/stores/project.ts'
|
||||||
import type { Issue } from '@/api/issue.api.ts'
|
import type { Issue } from '@/api/issue.api.ts'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
const issueStore = useIssueStore()
|
const issueStore = useIssueStore()
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
|
const { current } = storeToRefs(projectStore)
|
||||||
|
|
||||||
const emit = defineEmits<{ created: [issue: Issue] }>()
|
const emit = defineEmits<{ created: [issue: Issue] }>()
|
||||||
|
|
||||||
const showForm = ref(false)
|
const showForm = ref(false)
|
||||||
@@ -15,10 +18,9 @@ const newIssue = reactive({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const onCreate = async () => {
|
const onCreate = async () => {
|
||||||
const project = projectStore.current
|
if (!current.value) return
|
||||||
if (!project) return
|
|
||||||
|
|
||||||
const created = await issueStore.create({ ...newIssue, projectId: project.id, key: project.key })
|
const created = await issueStore.create({ ...newIssue, projectId: current.value.id, key: current.value.key })
|
||||||
if (!created) return
|
if (!created) return
|
||||||
|
|
||||||
emit('created', created)
|
emit('created', created)
|
||||||
@@ -29,7 +31,7 @@ const onCreate = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-list-item v-if="showForm && projectStore.current">
|
<v-list-item v-if="showForm && current">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<v-menu v-if="false">
|
<v-menu v-if="false">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
@@ -58,7 +60,7 @@ const onCreate = async () => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item v-else-if="showForm && !projectStore.current">
|
<v-list-item v-else-if="showForm && !current">
|
||||||
<div>
|
<div>
|
||||||
<v-alert closable density="compact" @click:close="showForm = false">
|
<v-alert closable density="compact" @click:close="showForm = false">
|
||||||
Select or create project
|
Select or create project
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { computed } from 'vue'
|
|||||||
import AssigneeFilter from '@/components/AssigneeFilter.vue'
|
import AssigneeFilter from '@/components/AssigneeFilter.vue'
|
||||||
import TextFilter from '@/components/TextFilter.vue'
|
import TextFilter from '@/components/TextFilter.vue'
|
||||||
import StatusFilter from '@/components/StatusFilter.vue'
|
import StatusFilter from '@/components/StatusFilter.vue'
|
||||||
|
import ProjectMenu from '@/components/ProjectMenu.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
text?: string | null
|
text?: string | null
|
||||||
@@ -30,7 +31,8 @@ function onStatusChanged(alias: string) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="filter-grid ga-2 align-center">
|
<div class="filter-grid ga-4 align-center">
|
||||||
|
<project-menu />
|
||||||
<text-filter :model-value="text" @update:model-value="emit('update:text', $event)" />
|
<text-filter :model-value="text" @update:model-value="emit('update:text', $event)" />
|
||||||
<status-filter :status="status" @changed="onStatusChanged" />
|
<status-filter :status="status" @changed="onStatusChanged" />
|
||||||
<assignee-filter :selected="selectedAssignees" @changed="onAssigneeChanged" />
|
<assignee-filter :selected="selectedAssignees" @changed="onAssigneeChanged" />
|
||||||
@@ -41,6 +43,6 @@ function onStatusChanged(alias: string) {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.filter-grid {
|
.filter-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto auto auto;
|
grid-template-columns: auto 1fr auto auto auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
+1
-1
@@ -28,7 +28,7 @@ export const useIssueStore = defineStore('item', () => {
|
|||||||
page: number
|
page: number
|
||||||
limit: number
|
limit: number
|
||||||
text?: string | null
|
text?: string | null
|
||||||
status?: string | null
|
status?: string | string[] | null
|
||||||
assigneeId?: string | null
|
assigneeId?: string | null
|
||||||
projectId?: string | null
|
projectId?: string | null
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
+21
-10
@@ -30,32 +30,43 @@ const projectStore = useProjectStore()
|
|||||||
const statusStore = useStatusStore()
|
const statusStore = useStatusStore()
|
||||||
|
|
||||||
const { stats } = storeToRefs(issueStore)
|
const { stats } = storeToRefs(issueStore)
|
||||||
const { projects, currentProjectId } = storeToRefs(projectStore)
|
const { projects, current, currentProjectId } = storeToRefs(projectStore)
|
||||||
|
|
||||||
const currentUserId = computed(() => session.value.data?.user.id ?? null)
|
const currentUserId = computed(() => session.value.data?.user.id ?? null)
|
||||||
|
|
||||||
const myIssues = ref<Issue[]>([])
|
const myIssues = ref<Issue[]>([])
|
||||||
const loadingMyIssues = ref(false)
|
const loadingMyIssues = ref(false)
|
||||||
|
let myIssuesRequestId = 0
|
||||||
|
|
||||||
async function loadMyIssues(userId: string) {
|
async function loadMyIssues(userId: string, projectId: string | null) {
|
||||||
|
// Ignore responses that arrive after the user or project has changed again
|
||||||
|
const requestId = ++myIssuesRequestId
|
||||||
loadingMyIssues.value = true
|
loadingMyIssues.value = true
|
||||||
try {
|
try {
|
||||||
const res = await issueStore.list({ page: 1, limit: 50, assigneeId: userId })
|
const res = await issueStore.list({ page: 1, limit: 50, assigneeId: userId, projectId, status: ACTIVE_STATUSES })
|
||||||
myIssues.value = res.list.filter((issue) => ACTIVE_STATUSES.includes(issue.status))
|
if (requestId !== myIssuesRequestId) return
|
||||||
|
myIssues.value = res.list
|
||||||
} finally {
|
} finally {
|
||||||
loadingMyIssues.value = false
|
if (requestId === myIssuesRequestId) loadingMyIssues.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
currentUserId,
|
[currentUserId, currentProjectId],
|
||||||
(id) => {
|
([userId, projectId]) => {
|
||||||
if (id) void loadMyIssues(id)
|
if (userId) void loadMyIssues(userId, projectId)
|
||||||
else myIssues.value = []
|
else myIssues.value = []
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const myIssuesTitle = computed(() => (current.value ? `My issues · ${current.value.key}` : 'My issues'))
|
||||||
|
|
||||||
|
const myIssuesLink = computed(() => ({
|
||||||
|
path: '/issues',
|
||||||
|
query: currentUserId.value ? { assignee: currentUserId.value } : {},
|
||||||
|
}))
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
currentProjectId,
|
currentProjectId,
|
||||||
(id) => {
|
(id) => {
|
||||||
@@ -134,8 +145,8 @@ function onClickCard(status?: string) {
|
|||||||
<v-col cols="12" md="8">
|
<v-col cols="12" md="8">
|
||||||
<v-card border>
|
<v-card border>
|
||||||
<v-card-title class="d-flex align-center justify-space-between">
|
<v-card-title class="d-flex align-center justify-space-between">
|
||||||
<span>My issues</span>
|
<span>{{ myIssuesTitle }}</span>
|
||||||
<v-btn size="small" variant="text" to="/issues">All issues</v-btn>
|
<v-btn size="small" variant="text" :to="myIssuesLink">View all mine</v-btn>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
<v-list v-if="sortedMyIssues.length" density="compact">
|
<v-list v-if="sortedMyIssues.length" density="compact">
|
||||||
|
|||||||
Reference in new issue
Block a user