This commit is contained in:
2025-04-30 13:11:35 +03:00
parent c8f3c9801f
commit cda54b1e95
60 changed files with 1054 additions and 651 deletions

View File

@@ -0,0 +1,264 @@
<template>
<div class="q-pa-none flex column col-grow no-scroll">
<pn-scroll-list>
<template #card-body-header>
<div class="flex row q-ma-md justify-between">
<q-input
v-model="search"
clearable
clear-icon="close"
:placeholder="$t('project_chats__search')"
dense
class="col-grow"
>
<template #prepend>
<q-icon name="mdi-magnify" />
</template>
</q-input>
</div>
</template>
<q-list bordered separator>
<q-slide-item
v-for="item in displayChats"
:key="item.id"
@right="handleSlide($event, item.id)"
right-color="red"
>
<template #right>
<q-icon size="lg" name="mdi-link-off"/>
</template>
<q-item
:key="item.id"
:clickable="false"
>
<q-item-section avatar>
<q-avatar rounded>
<q-img v-if="item.logo" :src="item.logo"/>
<pn-auto-avatar v-else :name="item.name"/>
</q-avatar>
</q-item-section>
<q-item-section>
<q-item-label lines="1" class="text-bold">
{{ item.name }}
</q-item-label>
<q-item-label caption lines="2">
{{ item.description }}
</q-item-label>
<q-item-label caption lines="1">
<div class = "flex justify-start items-center">
<div class="q-mr-sm flex items-center">
<q-icon name="mdi-account-outline" class="q-mx-sm"/>
<span>{{ item.persons }}</span>
</div>
<div class="q-mx-sm flex items-center">
<q-icon name="mdi-key" class="q-mr-sm"/>
<span>{{ item.owner_id }} </span>
</div>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-slide-item>
</q-list>
</pn-scroll-list>
</div>
<q-page-sticky
:style="{ zIndex: !showOverlay ? 'inherit' : '5100 !important' }"
position="bottom-right"
:offset="[18, 18]"
>
<transition
appear
enter-active-class="animated slideInUp"
>
<q-fab
v-if="showFab"
icon="add"
color="brand"
direction="up"
vertical-actions-align="right"
@click="showOverlay = !showOverlay"
>
<q-fab-action
v-for="item in fabMenu"
:key="item.id"
square
clickable
v-ripple
class="bg-white change-fab-action"
>
<template #icon>
<q-item class="q-pa-xs w100">
<q-item-section avatar class="items-center">
<q-avatar color="brand" rounded text-color="white" :icon="item.icon" />
</q-item-section>
<q-item-section class="items-start">
<q-item-label class="fab-action-item">
{{ $t(item.name) }}
</q-item-label>
<q-item-label caption class="fab-action-item">
{{ $t(item.description) }}
</q-item-label>
</q-item-section>
</q-item>
</template>
</q-fab-action>
</q-fab>
</transition>
</q-page-sticky>
<pn-overlay v-if="showOverlay"/>
<q-dialog v-model="showDialogDeleteChat" @before-hide="onDialogBeforeHide()">
<q-card class="q-pa-none q-ma-none">
<q-card-section align="center">
<div class="text-h6 text-negative ">{{ $t('project_chat__delete_warning') }}</div>
</q-card-section>
<q-card-section class="q-pt-none" align="center">
{{ $t('project_chat__delete_warning_message') }}
</q-card-section>
<q-card-actions align="center">
<q-btn
flat
:label="$t('back')"
color="primary"
v-close-popup
@click="onCancel()"
/>
<q-btn
flat
:label="$t('delete')"
color="primary"
v-close-popup
@click="onConfirm()"
/>
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { ref, computed, onActivated, onDeactivated, onBeforeUnmount } from 'vue'
import { useChatsStore } from 'stores/chats'
const search = ref('')
const showOverlay = ref<boolean>(false)
const chatsStore = useChatsStore()
const showDialogDeleteChat = ref<boolean>(false)
const deleteChatId = ref<number | undefined>(undefined)
const currentSlideEvent = ref<SlideEvent | null>(null)
const closedByUserAction = ref(false)
interface SlideEvent {
reset: () => void
}
const chats = chatsStore.chats
const fabMenu = [
{id: 1, icon: 'mdi-chat-plus-outline', name: 'project_chats__attach_chat', description: 'project_chats__attach_chat_description', func: 'attachChat'},
{id: 2, icon: 'mdi-share-outline', name: 'project_chats__send_chat', description: 'project_chats__send_chat_description', func: 'sendChat'},
]
const displayChats = computed(() => {
if (!search.value || !(search.value && search.value.trim())) return chats
const searchValue = search.value.trim().toLowerCase()
const arrOut = chats
.filter(el =>
el.name.toLowerCase().includes(searchValue) ||
el.description && el.description.toLowerCase().includes(searchValue)
)
return arrOut
})
function handleSlide (event: SlideEvent, id: number) {
currentSlideEvent.value = event
showDialogDeleteChat.value = true
deleteChatId.value = id
}
function onDialogBeforeHide () {
if (!closedByUserAction.value) {
onCancel()
}
closedByUserAction.value = false
}
function onCancel() {
closedByUserAction.value = true
if (currentSlideEvent.value) {
currentSlideEvent.value.reset()
currentSlideEvent.value = null
}
}
function onConfirm() {
closedByUserAction.value = true
if (deleteChatId.value) {
chatsStore.deleteChat(deleteChatId.value)
}
currentSlideEvent.value = null
}
// fix fab jumping
const showFab = ref(false)
const timerId = ref<ReturnType<typeof setTimeout> | null>(null)
onActivated(() => {
timerId.value = setTimeout(() => {
showFab.value = true
}, 300)
})
onDeactivated(() => {
showFab.value = false
if (timerId.value) {
clearTimeout(timerId.value)
timerId.value = null
}
})
onBeforeUnmount(() => {
if (timerId.value) clearTimeout(timerId.value)
})
</script>
<style scoped>
.change-fab-action .q-fab__label--internal {
max-height: none;
}
.change-fab-action {
width: calc(min(100vw, var(--body-width)) - 48px) !important;
}
.fab-action-item {
text-wrap: auto !important;
text-align: left;
}
/* fix mini border after slide */
:deep(.q-slide-item__right)
{
align-self: center;
height: 98%;
}
.fix-fab {
top: calc(100vh - 92px);
left: calc(100vw - 92px);
padding: 18px;
}
</style>