This commit is contained in:
vadim committed 2026-09-29 13:20:30 +03:00
1 parent 3157946186
commit e62747d62d
8 files changed
+161 -38

No files matched your search

+7 -7
View File
@@ -2,17 +2,17 @@
import { ref } from 'vue' import { ref } from 'vue'
interface Props { interface Props {
value?: string text?: string
} }
const props = defineProps<Props>() const props = defineProps<Props>()
const emit = defineEmits<{ changed: [value: string] }>() const emit = defineEmits<{ changed: [text: string] }>()
const editing = ref(false) const editing = ref(false)
const draft = ref(props.value ?? '') const draft = ref(props.text ?? '')
const startEdit = () => { const startEdit = () => {
draft.value = props.value ?? '' draft.value = props.text ?? ''
editing.value = true editing.value = true
} }
@@ -22,7 +22,7 @@ const save = () => {
} }
const cancel = () => { const cancel = () => {
draft.value = props.value ?? '' draft.value = props.text ?? ''
editing.value = false editing.value = false
} }
</script> </script>
@@ -30,8 +30,8 @@ const cancel = () => {
<template> <template>
<div class="issue-content"> <div class="issue-content">
<template v-if="!editing"> <template v-if="!editing">
<v-sheet class="pa-2 cursor-pointer content-sheet" @click="startEdit"> <v-sheet class="pa-4 cursor-pointer content-sheet" @click="startEdit">
{{ value ?? 'click to add content' }} {{ text ?? 'click to add content' }}
</v-sheet> </v-sheet>
</template> </template>
+4 -1
View File
@@ -93,11 +93,14 @@ defineExpose({
<div class="text-medium-emphasis">Created At:</div> <div class="text-medium-emphasis">Created At:</div>
<div>{{ createdAtFormatted }}</div> <div>{{ createdAtFormatted }}</div>
<div class="text-medium-emphasis">Due Date:</div>
<div>{{ issue.dueDate }}</div>
</v-card-text> </v-card-text>
</v-card> </v-card>
<expansion-panel title="Details"> <expansion-panel title="Details">
<issue-content :value="issue.content" @changed="onContentChanged" /> <issue-content :text="issue.content" @changed="onContentChanged" />
</expansion-panel> </expansion-panel>
<expansion-panel title="Activity"> <expansion-panel title="Activity">
+59
View File
@@ -0,0 +1,59 @@
<script setup lang="ts">
import { ref } from 'vue'
interface Props {
date?: string | null
}
const props = defineProps<Props>()
const emit = defineEmits<{ changed: [date: string] }>()
const editing = ref(false)
const draft = ref<Date | null>(props.date ? new Date(props.date) : null)
const formatted = () => {
if (!props.date) return 'click to add due date'
return new Date(props.date).toLocaleDateString('ru-RU')
}
const startEdit = () => {
draft.value = props.date ? new Date(props.date) : null
editing.value = true
}
const save = () => {
if (!draft.value) return
emit('changed', draft.value.toISOString())
editing.value = false
}
const cancel = () => {
draft.value = props.date ? new Date(props.date) : null
editing.value = false
}
</script>
<template>
<div class="issue-due-date">
<template v-if="!editing">
<v-sheet class="pa-4 cursor-pointer due-date-sheet" @click="startEdit">
{{ formatted() }}
</v-sheet>
</template>
<template v-else>
<v-date-input autofocus class="mb-1" hide-details v-model="draft" variant="outlined" />
<div class="d-flex justify-end ga-1">
<v-btn density="compact" variant="text" text="Cancel" @click="cancel" />
<v-btn density="compact" color="primary" variant="flat" text="Save" @click="save" />
</div>
</template>
</div>
</template>
<style scoped lang="scss">
.due-date-sheet:hover {
background-color: rgba(var(--v-theme-on-surface), 0.06);
}
</style>
+53
View File
@@ -0,0 +1,53 @@
<script setup lang="ts">
import { ref } from 'vue'
interface Props {
text?: string
}
const props = defineProps<Props>()
const emit = defineEmits<{ changed: [text: string] }>()
const editing = ref(false)
const draft = ref(props.text ?? '')
const startEdit = () => {
draft.value = props.text ?? ''
editing.value = true
}
const save = () => {
emit('changed', draft.value)
editing.value = false
}
const cancel = () => {
draft.value = props.text ?? ''
editing.value = false
}
</script>
<template>
<div class="issue-title">
<template v-if="!editing">
<v-sheet class="pa-4 cursor-pointer title-sheet" @click="startEdit">
{{ text ?? 'click to add title' }}
</v-sheet>
</template>
<template v-else>
<v-text-field autofocus class="mb-1" hide-details v-model="draft" variant="outlined" />
<div class="d-flex justify-end ga-1">
<v-btn density="compact" variant="text" text="Cancel" @click="cancel" />
<v-btn density="compact" color="primary" variant="flat" text="Save" @click="save" />
</div>
</template>
</div>
</template>
<style scoped lang="scss">
.title-sheet:hover {
background-color: rgba(var(--v-theme-on-surface), 0.06);
}
</style>
+21
View File
@@ -0,0 +1,21 @@
<script setup lang="ts">
interface Props {
text?: string
label?: string
color?: string
}
const props = withDefaults(defineProps<Props>(), {
text: 'text',
color: 'primary',
})
</script>
<template>
<div class="d-flex align-center ga-1">
<v-chip v-if="props.label" size="small" :color="props.color" variant="flat" density="compact" :text="props.label" />
<div>{{ props.text }}</div>
</div>
</template>
<style scoped lang="scss"></style>
+4 -9
View File
@@ -2,6 +2,7 @@
import { computed, onMounted } from 'vue' import { computed, onMounted } from 'vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useProjectStore } from '@/stores/project.ts' import { useProjectStore } from '@/stores/project.ts'
import LabelText from '@/components/LabelText.vue'
const projectStore = useProjectStore() const projectStore = useProjectStore()
const { projects, current } = storeToRefs(projectStore) const { projects, current } = storeToRefs(projectStore)
@@ -35,12 +36,7 @@ onMounted(async () => {
<v-menu> <v-menu>
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
<v-btn density="comfortable" v-bind="props"> <v-btn density="comfortable" v-bind="props">
<div class="d-flex ga-1 align-center"> <label-text :label="current?.key" :text="currentProjectName" />
<v-chip v-if="current" size="small" variant="flat" color="primary" density="compact">
{{ current.key }}
</v-chip>
<div>{{ currentProjectName }}</div>
</div>
</v-btn> </v-btn>
</template> </template>
@@ -55,9 +51,8 @@ onMounted(async () => {
@update:selected="select" @update:selected="select"
> >
<v-list-item slim v-for="project in projects" :key="project.id" :value="project.id"> <v-list-item slim v-for="project in projects" :key="project.id" :value="project.id">
<v-list-item-title class="d-flex ga-1 align-center"> <v-list-item-title>
<v-chip size="small" color="primary" variant="flat" density="compact">{{ project.key }}</v-chip> <label-text :label="project.key" :text="project.name" />
<div>{{ project.name }}</div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
</v-list> </v-list>
+3 -12
View File
@@ -9,6 +9,7 @@ import { authClient } from '@/lib/auth-client.ts'
import type { Issue } from '@/api/issue.api.ts' import type { Issue } from '@/api/issue.api.ts'
import type { Project } from '@/api/project.api.ts' import type { Project } from '@/api/project.api.ts'
import DashboardCountCard from '@/components/DashboardCountCard.vue' import DashboardCountCard from '@/components/DashboardCountCard.vue'
import LabelText from '@/components/LabelText.vue'
const ACTIVE_STATUSES: Issue['status'][] = ['OPEN', 'IN_PROGRESS'] const ACTIVE_STATUSES: Issue['status'][] = ['OPEN', 'IN_PROGRESS']
@@ -177,18 +178,8 @@ function onClickCard(status?: string) {
</v-card-title> </v-card-title>
<v-divider /> <v-divider />
<v-list v-if="projects.length" density="compact"> <v-list v-if="projects.length" density="compact">
<v-list-item <v-list-item v-for="project in projects.slice(0, 6)" :key="project.id" @click="openProject(project)">
v-for="project in projects.slice(0, 6)" <label-text :label="project.key" :text="project.name" />
:key="project.id"
class="cursor-pointer"
@click="openProject(project)"
>
<template #prepend>
<v-chip size="small" color="primary" variant="flat" density="compact">{{ project.key }}</v-chip>
</template>
<template #title>
<span class="ml-2">{{ project.name }}</span>
</template>
</v-list-item> </v-list-item>
</v-list> </v-list>
<v-card-text v-else class="text-medium-emphasis">Нет проектов</v-card-text> <v-card-text v-else class="text-medium-emphasis">Нет проектов</v-card-text>
+10 -9
View File
@@ -21,13 +21,6 @@ const totalPages = ref(0)
const serverItems = ref<Project[]>([]) const serverItems = ref<Project[]>([])
const loading = ref(false) const loading = ref(false)
const headers = [
{ key: 'key', title: 'Key', sortable: false },
{ key: 'name', title: 'Name', sortable: false },
{ key: 'createdAt', title: 'Created', sortable: false },
{ key: 'actions', title: '', sortable: false, align: 'end' as const },
]
async function fetch() { async function fetch() {
loading.value = true loading.value = true
try { try {
@@ -124,7 +117,6 @@ async function onDelete(project: Project) {
deletingId.value = null deletingId.value = null
} }
} }
</script> </script>
<template> <template>
@@ -138,7 +130,12 @@ async function onDelete(project: Project) {
hover hover
hide-default-footer hide-default-footer
:loading="loading" :loading="loading"
:headers="headers" :headers="[
{ key: 'key', title: 'Key', sortable: false, width: '8rem' },
{ key: 'name', title: 'Name', sortable: false },
{ key: 'createdAt', title: 'Created', sortable: false },
{ key: 'actions', title: '', sortable: false, align: 'end' as const },
]"
:items-per-page="itemsPerPage" :items-per-page="itemsPerPage"
:page="page" :page="page"
:items="serverItems" :items="serverItems"
@@ -147,6 +144,10 @@ async function onDelete(project: Project) {
@update:options="loadItems" @update:options="loadItems"
@click:row="(_e: unknown, { item }: { item: Project }) => openIssues(item)" @click:row="(_e: unknown, { item }: { item: Project }) => openIssues(item)"
> >
<template #[`item.key`]="{ item }">
<v-chip size="small" color="primary" variant="flat" density="compact" :text="item.key" />
</template>
<template #[`item.createdAt`]="{ item }"> <template #[`item.createdAt`]="{ item }">
{{ new Date(item.createdAt).toLocaleDateString('ru-RU') }} {{ new Date(item.createdAt).toLocaleDateString('ru-RU') }}
</template> </template>