This commit is contained in:
2025-07-16 21:52:57 +03:00
parent b51a472738
commit 3e43efc70d
31 changed files with 801 additions and 391 deletions

View File

@@ -68,7 +68,6 @@
{ id: 5, name: 'account__company_data', icon: 'mdi-account-group-outline', description: 'account__company_data_description', pathName: 'your_company' },
{ id: 6, name: 'account__settings', icon: 'mdi-cog-outline', description: 'account__settings_description', iconColor: 'info', pathName: 'settings' },
{ id: 7, name: 'account__support', icon: 'mdi-lifebuoy', description: 'account__support_description', iconColor: 'info', pathName: 'support' },
{ id: 8, name: 'account__3rd_party_software', icon: 'mdi-scale-balance', description: 'account__3rd_party_software_description', iconColor: 'grey', pathName: '3software' },
{ id: 9, name: 'account__terms_of_use', icon: 'mdi-book-open-variant-outline', description: '', iconColor: 'grey', pathName: 'terms' },
{ id: 10, name: 'account__privacy', icon: 'mdi-lock-outline', description: '', iconColor: 'grey', pathName: 'privacy' }
]))

View File

@@ -1,8 +1,8 @@
<template>
<company-block
v-model="newCompany"
title="company_create__title_card"
btnText="company_create__btn"
title="company_add__title_card"
btnText="company_add__btn"
@update = "addCompany"
/>
</template>

View File

@@ -25,7 +25,7 @@
v-model="searchProject"
clearable
clear-icon="close"
:placeholder="$t('project_chats__search')"
:placeholder="$t('projects__search')"
dense
class="col-grow q-px-md q-py-md"
>
@@ -91,22 +91,22 @@
class="flex column items-center w100"
:class="showArchive ? 'bg-grey-12' : ''"
>
<q-btn-dropdown
class="w100 fix-rotate-arrow"
<q-btn
class="w100 rotate-icon-btn"
color="grey"
flat no-caps
flat
no-caps
@click="showArchive=!showArchive"
dropdown-icon="arrow_drop_up"
icon-right="arrow_drop_down"
:class="{ 'rotate-icon': showArchive }"
>
<template #label>
<span class="text-caption">
{{ !showArchive
? $t('projects__show_archive') + ' (' + archiveProjects.length +')'
: $t('projects__hide_archive')
}}
</span>
</template>
</q-btn-dropdown>
<span class="text-caption">
{{ !showArchive
? $t('projects__show_archive') + ' (' + archiveProjects.length +')'
: $t('projects__hide_archive')
}}
</span>
</q-btn>
<div class="w100" style="overflow: hidden">
<transition
@@ -186,6 +186,7 @@
color="negative"
title="projects__dialog_archive_title"
message1="projects__dialog_archive_message"
message2="projects__dialog_archive_message2"
mainBtnLabel="projects__dialog_archive_ok"
@clickMainBtn="onConfirmArchiveProject()"
@close="onCancel()"
@@ -198,7 +199,8 @@
color="green"
title="projects__dialog_restore_title"
message1="projects__dialog_restore_message"
mainBtnLabel="projects__dialog_cancel_ok"
message2="projects__dialog_restore_message2"
mainBtnLabel="projects__dialog_restore_ok"
@clickMainBtn="restoreFromArchive()"
/>
</template>
@@ -319,7 +321,17 @@
flex-wrap: nowrap;
}
.fix-rotate-arrow :deep(.q-btn-dropdown--simple) {
margin-left: 0;
.rotate-icon-btn {
transition: transform 0.3s;
}
.rotate-icon-btn.rotate-icon :deep(.q-icon) {
transform: rotate(180deg);
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
}
.rotate-icon-btn :deep(.q-icon) {
transform: rotate(0deg);
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
}
</style>

View File

@@ -1,234 +0,0 @@
<template>
<pn-page-card>
<template #title>
{{ $t('software__title') }}
</template>
<pn-scroll-list>
<template #card-body-header>
<div class="q-pa-md">
{{ $t('software__description') }}
</div>
</template>
<q-list separator>
<q-expansion-item
v-for="item in software"
group="somegroup"
>
<template #header>
<q-item-section avatar>
<q-avatar square size="sm" v-if="item.logo">
<img :src="'3software/logo/' + item.logo">
</q-avatar>
</q-item-section>
<q-item-section>
<div class="flex items-baseline">
<span class="text-h6">
{{ item.name }}
</span>
<span v-if = "item.ver" class="text-caption q-pl-xs">
{{ 'v.' + item.ver }}
</span>
</div>
</q-item-section>
</template>
<div class="w100 flex column q-px-md q-gutter-y-md q-py-sm">
<div class="flex row no-wrap items-center">
<q-icon name="mdi-scale-balance" size="sm" class="q-pr-lg" color="grey"/>
<div
@click="downloadFile('3software/license/' + item.license_file)"
class="flex w100 column q-pl-sm cursor-pointer"
>
<span> {{ item.license }} </span>
<span
class="text-caption"
style="white-space: pre-line;"
>
{{ item.license_copyright }}
</span>
</div>
</div>
<div class="flex row no-wrap items-center" v-if="item.web">
<q-icon name="mdi-web" size="sm" class="q-pr-lg" color="grey"/>
<span
class="q-pl-sm cursor-pointer"
@click="tg.openLink(item.web_url)"
>
{{ item.web }}
</span>
</div>
<div class="flex row no-wrap items-center" v-if="item.git">
<q-icon name="mdi-github" size="sm" class="q-pr-lg" color="grey"/>
<span
class="q-pl-sm cursor-pointer"
@click="tg.openLink(item.git_url)"
>
{{ item.git }}
</span>
</div>
</div>
</q-expansion-item>
</q-list>
</pn-scroll-list>
</pn-page-card>
</template>
<script setup lang="ts">
import { inject } from 'vue'
import type { WebApp } from '@twa-dev/types'
const tg = inject('tg') as WebApp
const software = [
{
id: 1,
name: 'Vue',
ver: '3.4',
logo: 'vue.webp',
web: 'vuejs.org',
web_url: 'https://vuejs.org/',
git: 'vuejs/core',
git_url: 'https://github.com/vuejs/core',
license: 'MIT License',
license_copyright: 'Copyright (c) 2018-present, Yuxi (Evan) You and Vue contributors',
license_file: 'vue/LICENSE.txt'
},
{
id: 2,
name: 'Quasar',
ver: '2.x',
logo: 'quasar.png',
web: 'quasar.dev',
web_url: 'https://quasar.dev/',
git: 'quasarframework/quasar',
git_url: 'https://github.com/quasarframework/quasar',
license: 'MIT License',
license_copyright: 'Copyright (c) 2015-present Razvan Stoenescu',
license_file: 'quasar/LICENSE.txt'
},
{
id: 3,
name: 'Pinia',
ver: '2.x',
logo: 'pinia.svg',
web: 'pinia.vuejs.org',
web_url: 'https://pinia.vuejs.org/',
git: 'vuejs/pinia',
git_url: 'https://github.com/vuejs/pinia',
license: 'MIT License',
license_copyright: 'Copyright (c) 2019-present Eduardo San Martin Morote',
license_file: 'pinia/LICENSE.txt'
},
{
id: 4,
name: 'Vue Router',
ver: '4.x',
logo: 'vue.webp',
web: 'router.vuejs.org',
web_url: 'https://router.vuejs.org/',
git: 'vuejs/router',
git_url: 'https://github.com/vuejs/router',
license: 'MIT License',
license_copyright: 'Copyright (c) 2019-present Eduardo San Martin Morote',
license_file: 'vue-router/LICENSE.txt'
},
{
id: 5,
name: 'Vue i18n',
ver: '9.x',
logo: 'vue-i18n.svg',
web: 'vue-i18n.intlify.dev',
web_url: 'https://vue-i18n.intlify.dev/',
git: 'intlify/vue-i18n',
git_url: 'https://github.com/intlify/vue-i18n',
license: 'MIT License',
license_copyright: 'Copyright (c) 2016-present kazuya kawaguchi and contributors',
license_file: 'vue-i18n/LICENSE.txt'
},
{
id: 6,
name: 'Axios',
ver: '1.x',
logo: 'axios.svg',
web: 'axios-http.com',
web_url: 'https://axios-http.com/',
git: 'axios/axios',
git_url: 'https://github.com/axios/axios',
license: 'MIT License',
license_copyright: 'Copyright (c) 2014-present Matt Zabriskie & Collaborators',
license_file: 'axios/LICENSE.txt'
},
{
id: 7,
name: 'better-sqlite3',
ver: '11.x',
logo: '',
web: '',
web_url: '',
git: 'WiseLibs/better-sqlite3',
git_url: 'https://github.com/WiseLibs/better-sqlite3',
license: 'MIT License',
license_copyright: 'Copyright (c) 2017 Joshua Wise',
license_file: 'better-sqlite3/LICENSE.txt'
},
{
id: 8,
name: 'Express',
ver: '4.x',
logo: 'express.svg',
web: 'expressjs.com',
web_url: 'https://expressjs.com/',
git: 'expressjs/express',
git_url: 'https://github.com/expressjs/express',
license: 'MIT License',
license_copyright: `Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>`,
license_file: 'express/LICENSE.txt'
}
]
const downloadFile = async (url: string) => {
try {
const fullUrl = '/admin/' + url;
const fileUrl = new URL(fullUrl, window.location.origin).href;
const response = await fetch(fileUrl)
if (!response.ok) throw new Error(`HTTP error: ${response.status}`)
const blob = await response.blob()
const downloadUrl = URL.createObjectURL(blob)
const link = document.createElement('a')
const extractFileName = (url: string): string => {
return url.substring(url.lastIndexOf('/') + 1)
}
link.href = downloadUrl
link.download = extractFileName(url)
link.style.display = 'none'
document.body.appendChild(link)
link.click()
setTimeout(() => {
document.body.removeChild(link)
URL.revokeObjectURL(downloadUrl)
}, 100)
} catch (error) {
console.error('Download error:', error)
}
}
</script>
<style scope>
</style>

View File

@@ -6,75 +6,120 @@
<pn-scroll-list>
<q-list separator>
<q-item>
<q-item-section avatar>
<q-avatar color="primary" rounded text-color="white" icon="mdi-translate" size="md" />
</q-item-section>
<q-item-section>
<span>{{ $t('settings__language') }}</span>
</q-item-section>
<q-item-section>
<q-select
class="fix-input-right text-body1"
v-model="locale"
:options="localeOptions"
dense
borderless
emit-value
map-options
hide-bottom-space
/>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-avatar color="primary" rounded text-color="white" icon="mdi-format-size" size="md" />
</q-item-section>
<q-item-section>
<span>{{ $t('settings__font_size') }}</span>
</q-item-section>
<q-item-section>
<div class="flex justify-end">
<q-btn
@click="settingsStore.decreaseFontSize()"
color="negative" flat
icon="mdi-format-font-size-decrease"
class="q-pa-sm q-mx-xs"
:disable="!settingsStore.canDecrease"
/>
<q-btn
@click="settingsStore.increaseFontSize()"
color="positive" flat
icon="mdi-format-font-size-increase"
class="q-pa-sm q-mx-xs"
:disable="!settingsStore.canIncrease"
/>
</div>
</q-item-section>
</q-item>
<q-item-label header>{{ $t('settings__software_title') }}</q-item-label>
<pn-item-btm-dialog
title="settings__language"
caption="settings__software_title"
icon="mdi-translate"
iconColor="primary"
>
<template #value>
{{ localeOptions.find(el => el.value === locale)?.label }}
</template>
<pn-list-selector
v-model="locale"
:options="localeOptions"
/>
</pn-item-btm-dialog>
<pn-item-btm-dialog
title="settings__font_size"
caption="settings__software_title"
icon="mdi-format-size"
iconColor="primary"
>
<template #value>
{{ $t(fontSizeLabel) }}
</template>
<pn-list-selector
v-model="fontSize"
:options="fontSizeOptions"
/>
</pn-item-btm-dialog>
<q-item-label class="q-mt-md" header>{{ $t('settings__bot_title') }}</q-item-label>
<pn-item-btm-dialog
title="settings__language"
caption="settings__bot_title"
icon="mdi-translate"
iconColor="primary"
>
<template #value>
{{ localeOptions.find(el => el.value === localeBot)?.label }}
</template>
<pn-list-selector
v-model="localeBot"
:options="localeOptions"
/>
</pn-item-btm-dialog>
<pn-item-btm-dialog
title="settings__timezone"
caption="settings__bot_title"
icon="mdi-map-clock-outline"
iconColor="primary"
>
<template #value>
{{ timeZoneBot.tz }}
{{ timeZoneBot.offsetString }}
</template>
<pn-time-zone-selector
v-model="timeZoneBot"
:locale
/>
</pn-item-btm-dialog>
</q-list>
</pn-scroll-list>
</pn-page-card>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { ref, watch, computed, onMounted } from 'vue'
import { useSettingsStore } from 'stores/settings'
import pnItemBtmDialog from 'components/pnItemBtmDialog.vue'
import pnListSelector from 'components/pnListSelector.vue'
import pnTimeZoneSelector from 'components/pnTimeZoneSelector.vue'
const settingsStore = useSettingsStore()
const locale = ref('')
const localeBot = ref('')
const localeOptions = settingsStore.supportLocale
const locale = computed({
get: () => settingsStore.settings.locale,
// eslint-disable-next-line @typescript-eslint/no-misused-promises
set: (value: string) => settingsStore.updateLocale(value)
watch(locale, async (newValue) => {
await settingsStore.updateSettings({ locale: newValue })
})
watch(localeBot, async (newValue) => {
await settingsStore.updateSettings({ localeBot: newValue })
})
const fontSize = ref(14)
const fontSizeOptions = settingsStore.supportFontSizes
const fontSizeLabel = computed(() =>
fontSizeOptions.find(el => el.value === fontSize.value)?.label ?? ''
)
watch(fontSize, async (newValue) => {
await settingsStore.updateSettings({ fontSize: newValue })
})
const timeZoneBot = ref<{ tz: string, offset: number }>({ tz: '', offset: 1 })
watch(timeZoneBot, async (newValue) => {
if (newValue) await settingsStore.updateSettings({ timeZoneBot: newValue })
})
onMounted(() => {
locale.value = settingsStore.settings.locale
localeBot.value = settingsStore.settings.localeBot
fontSize.value = settingsStore.settings.fontSize
timeZoneBot.value = settingsStore.settings.timeZoneBot
})
</script>
<style scoped>
.fix-input-right :deep(.q-field__native) {
justify-content: end;
}
</style>

View File

@@ -8,7 +8,7 @@
v-model="search"
clearable
clear-icon="close"
:placeholder="$t('project_chats__search')"
:placeholder="$t('chats__search')"
dense
class="col-grow"
v-if="chats.length !== 0"
@@ -71,8 +71,8 @@
<pn-onboard-btn
v-if="chats.length === 0 && chatsInit"
icon="mdi-chat-plus-outline"
:message1="$t('project_chat__onboard_msg1')"
:message2="$t('project_chat__onboard_msg2')"
:message1="$t('chats__onboard_msg1')"
:message2="$t('chats__onboard_msg2')"
@click="showOverlay=true; fabState=true"
/>
<div
@@ -110,7 +110,7 @@
anchor="center left" self="center end"
style="width: calc(min(100vw, var(--body-width)) - 102px) !important;"
>
{{ $t('project_chats_disabled_FAB')}}
{{ $t('chats_disabled_FAB')}}
</q-tooltip>
</template>
<q-fab-action
@@ -152,9 +152,10 @@
v-model="showDialogDeleteChat"
icon="mdi-link-off"
color="negative"
title="project_chat__delete_warning"
message1="project_chat__delete_warning_message"
mainBtnLabel="project_chat__dialog_cancel_ok"
title="chats__dialog_unlink_title"
message1="chats__dialog_unlink_message"
message2="chats__dialog_unlink_message2"
mainBtnLabel="chats__dialog_unlink_ok"
@clickMainBtn="onConfirm()"
@close="onCancel()"
@before-hide="onDialogBeforeHide()"
@@ -190,8 +191,8 @@
const chatsInit = computed(() => chatsStore.isInit)
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},
{id: 1, icon: 'mdi-chat-plus-outline', name: 'chats__attach_chat', description: 'chats__attach_chat_description', func: attachChat},
{id: 2, icon: 'mdi-share-outline', name: 'chats__send_chat', description: 'chats__send_chat_description', func: sendChat},
]
const displayChats = computed(() => {
@@ -253,7 +254,7 @@
const key = await chatsStore.getKey()
const message = urlAdmin + key + urlAdminPermission
const tgShareUrl = 'https://t.me/share/url?url=' +
encodeURIComponent( t('project_chats__send_chat_title')) +
encodeURIComponent( t('chats__send_chat_title')) +
'&text=' + `${encodeURIComponent(message)}`
tg.openTelegramLink(tgShareUrl)
}

View File

@@ -7,7 +7,7 @@
:color="companies.length <= 2 ? 'grey-6' : 'primary'"
flat
no-caps
@click="maskCompany()"
@click="maskCompany"
:disable="companies.length <= 2"
class="q-pr-md"
rounded
@@ -16,9 +16,10 @@
left
size="sm"
name="mdi-domino-mask"
class="q-mr-xs"
/>
<div>
{{ $t('company__mask')}}
{{ $t('companies__mask')}}
</div>
</q-btn>
</div>
@@ -53,7 +54,7 @@
<q-item-label lines="1" class="text-caption text-amber-10" v-if="item.id === myCompany?.id">
<div class="flex items-center">
<q-icon name="star" class="q-pr-xs"/>
{{ $t('company__my_company') }}
{{ $t('companies__my_company') }}
</div>
</q-item-label>
<q-item-label lines="1" class="text-bold">{{ item.name }}</q-item-label>
@@ -93,9 +94,9 @@
<pn-onboard-btn
v-if="companies.length <= 1 && companiesInit"
icon="mdi-account-multiple-plus-outline"
:message1="$t('company__onboard_msg1')"
:message2="$t('company__onboard_msg2')"
@btn-click="createCompany()"
:message1="$t('companies__onboard_msg1')"
:message2="$t('companies__onboard_msg2')"
@btn-click="createCompany"
/>
<div
class="flex column justify-center items-center w100"
@@ -130,9 +131,10 @@
v-model="showDialogDeleteCompany"
icon="mdi-account-multiple-minus-outline"
color="negative"
title="company__dialog_delete_title"
message1="company__dialog_delete_message"
mainBtnLabel="company__dialog_delete_ok"
title="companies__dialog_delete_title"
message1="companies__dialog_delete_message"
message2="companies__dialog_delete_message2"
mainBtnLabel="companies__dialog_delete_ok"
@clickMainBtn="onConfirm()"
@close="onCancel()"
@before-hide="onDialogBeforeHide()"

View File

@@ -7,7 +7,7 @@
v-model="search"
clearable
clear-icon="close"
:placeholder="$t('project_users__search')"
:placeholder="$t('users__search')"
dense
class="col-grow"
>
@@ -60,27 +60,92 @@
</q-slide-item>
</q-list>
<!-- LEAVE USERS SECTION -->
<div
v-if="leaveUsers.length!==0"
class="flex column items-center w100"
:class="showLeaveUsers ? 'bg-grey-12' : ''"
>
<q-btn
class="w100 rotate-icon-btn"
color="grey"
flat
no-caps
@click="showLeaveUsers=!showLeaveUsers"
icon-right="arrow_drop_down"
:class="{ 'rotate-icon': showLeaveUsers }"
>
<span class="text-caption">
{{ !showLeaveUsers
? $t('users__show_left_users') + ' (' + leaveUsers.length +')'
: $t('users__hide_left_users')
}}
</span>
</q-btn>
<div class="w100" style="overflow: hidden">
<transition
appear
enter-active-class="animated slideInDown"
leave-active-class="animated slideOutUp"
>
<q-list separator v-if="showLeaveUsers" class="w100">
<q-item
v-for = "item in leaveUsers"
:key="item.id"
class="w100 text-grey"
>
<q-item-section avatar>
<pn-auto-avatar
:img="item.photo"
:name="item.section1"
/>
</q-item-section>
<q-item-section>
<q-item-label lines="1" class="text-bold" v-if="item.section1">
{{item.section1}}
</q-item-label>
<q-item-label lines="1" caption v-if="item.section3">
{{item.section3}}
</q-item-label>
<q-item-label caption lines="2">
<div class="flex items-center">
<q-icon name="telegram" v-if="item.section2_1 || item.section2_2" class="q-pr-xs" style="color: #27a7e7"/>
<div v-if="item.section2_1" class="q-mr-sm text-bold">{{item.section2_1}}</div>
<div class="text-blue" v-if="item.section2_2">{{'@' + item.section2_2}}</div>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</transition>
</div>
</div>
<!-- END LEAVE USERS SECTION -->
<!-- BLOCKED USERS SECTION -->
<div
v-if="blockedUsers.length!==0"
class="flex column items-center w100"
:class="showBlockedUsers ? 'bg-grey-12' : ''"
>
<q-btn-dropdown
class="w100 fix-rotate-arrow"
<q-btn
class="w100 rotate-icon-btn"
color="grey"
flat no-caps
flat
no-caps
@click="showBlockedUsers=!showBlockedUsers"
dropdown-icon="arrow_drop_up"
icon-right="arrow_drop_down"
:class="{ 'rotate-icon': showBlockedUsers }"
>
<template #label>
<span class="text-caption">
{{ !showBlockedUsers
? $t('users__show_archive') + ' (' + blockedUsers.length +')'
: $t('user__hide_archive')
}}
</span>
</template>
</q-btn-dropdown>
<span class="text-caption">
{{ !showBlockedUsers
? $t('users__show_blocked_users') + ' (' + blockedUsers.length +')'
: $t('users__hide_blocked_users')
}}
</span>
</q-btn>
<div class="w100" style="overflow: hidden">
<transition
@@ -123,12 +188,13 @@
</transition>
</div>
</div>
<!-- END BLOCKED USERS SECTION -->
<pn-onboard-btn
v-if="users.length === 0 && usersInit"
icon="mdi-account-outline"
:message1="$t('project_users__onboard_msg1')"
:message2="$t('project_users__onboard_msg2')"
:message1="$t('users__onboard_msg1')"
:message2="$t('users__onboard_msg2')"
noBtn
/>
<div
@@ -142,13 +208,14 @@
</div>
<pn-small-dialog
v-model="showDialogDeleteUser"
v-model="showDialogBlockUser"
icon="mdi-account-remove-outline"
color="negative"
title="user__dialog_delete_title"
message1="user__dialog_delete_message"
mainBtnLabel="user__dialog_delete_ok"
@clickMainBtn="onConfirmDeleteUser()"
title="users__dialog_block_title"
message1="users__dialog_block_message"
message2="users__dialog_block_message2"
mainBtnLabel="users__dialog_block_ok"
@clickMainBtn="onConfirmBlockUser()"
@close="onCancel()"
@before-hide="onDialogBeforeHide()"
/>
@@ -157,9 +224,10 @@
v-model="showDialogRestoreUser"
icon="mdi-account-reactivate-outline"
color="green"
title="user__dialog_restore_title"
message1="user__dialog_restore_message"
mainBtnLabel="user__dialog_restore_ok"
title="users__dialog_restore_title"
message1="users__dialog_restore_message"
message2="users__dialog_restore_message2"
mainBtnLabel="users__dialog_restore_ok"
@clickMainBtn="onConfirmRestoreUser()"
/>
@@ -183,12 +251,18 @@
const users = usersStore.getUsers
const usersInit = computed(() => usersStore.isInit)
const deleteUserId = ref<number | undefined>(undefined)
const showDialogDeleteUser = ref<boolean>(false)
const blockUserId = ref<number | undefined>(undefined)
const showDialogBlockUser = ref<boolean>(false)
const currentSlideEvent = ref<SlideEvent | null>(null)
const closedByUserAction = ref(false)
const mapUsers = computed(() => users.map(el => ({...el, ...userSection(el)})))
const mapUsers = computed(() => users.map(el => ({
...el,
...userSection(el),
companyName: el.company_id && companiesStore.companyById(el.company_id)
? companiesStore.companyById(el.company_id)?.name
: null
})))
interface SlideEvent {
reset: () => void
@@ -202,12 +276,13 @@
el.section1.toLowerCase().includes(searchValue) ||
el.section2_1.toLowerCase().includes(searchValue) ||
el.section2_2.toLowerCase().includes(searchValue) ||
el.section3.toLowerCase().includes(searchValue)
el.section3.toLowerCase().includes(searchValue) ||
el.companyName && el.companyName.toLowerCase().includes(searchValue)
)
return arrOut
})
const displayUsers = computed(() => displayUsersAll.value.filter(el => !el.is_block))
const displayUsers = computed(() => displayUsersAll.value.filter(el => !el.is_blocked))
function userSection (user: User) {
const tname = () => {
@@ -244,8 +319,8 @@
function handleSlide (event: SlideEvent, id: number) {
currentSlideEvent.value = event
showDialogDeleteUser.value = true
deleteUserId.value = id
showDialogBlockUser.value = true
blockUserId.value = id
}
function onDialogBeforeHide () {
@@ -263,26 +338,29 @@
}
}
async function onConfirmDeleteUser() {
async function onConfirmBlockUser() {
closedByUserAction.value = true
if (deleteUserId.value) {
await usersStore.blockUser(deleteUserId.value)
if (blockUserId.value) {
await usersStore.blockUser(blockUserId.value)
}
currentSlideEvent.value = null
}
const showBlockedUsers = ref(false)
const blockedUsers = computed(() => displayUsersAll.value.filter(el => el.is_block))
const blockedUsers = computed(() => displayUsersAll.value.filter(el => el.is_blocked))
const unblockUserId = ref<number | undefined> (undefined)
const showDialogRestoreUser = ref(false)
const showLeaveUsers = ref(false)
const leaveUsers = computed(() => displayUsersAll.value.filter(el => el.is_leave))
function handleUnblockUser (id: number) {
showDialogRestoreUser.value = true
unblockUserId.value = id
}
async function onConfirmRestoreUser () {
if (unblockUserId.value) await usersStore.restore(unblockUserId.value)
if (unblockUserId.value) await usersStore.unblockUser(unblockUserId.value)
}
watch(showDialogRestoreUser, (newD :boolean) => {
@@ -293,5 +371,18 @@
</script>
<style>
<style scoped>
.rotate-icon-btn {
transition: transform 0.3s;
}
.rotate-icon-btn.rotate-icon :deep(.q-icon) {
transform: rotate(180deg);
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
}
.rotate-icon-btn :deep(.q-icon) {
transform: rotate(0deg);
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
}
</style>