refactor: improvemets

Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
2026-03-24 19:11:29 -04:00
parent ab508a5361
commit 18d07d84cb
11 changed files with 499 additions and 270 deletions

View File

@@ -1,34 +1,38 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useCollectionsStore } from '@MailManager/stores/collectionsStore'
import type { CollectionObject } from '@MailManager/models/collection'
import type { ServiceInterface } from '@MailManager/types/service'
import type { ServiceObject } from '@MailManager/models'
// Props
interface Props {
selectedFolder?: CollectionObject | null
serviceGroups: Array<{
service: ServiceInterface
folders: CollectionObject[]
service: ServiceObject
loading: boolean
loaded: boolean
error: string | null
}>
}
const props = defineProps<Props>()
const collectionsStore = useCollectionsStore()
// Emits
const emit = defineEmits<{
select: [folder: CollectionObject]
createFolder: [service: ServiceInterface, parentFolder: CollectionObject | null]
editFolder: [service: ServiceInterface, folder: CollectionObject]
createFolder: [service: ServiceObject, parentFolder: CollectionObject | null]
editFolder: [service: ServiceObject, folder: CollectionObject]
}>()
// Page-based navigation state per service account
const pageLevels = ref<Record<string, (string | number | null)[]>>({})
const getServiceKey = (service: ServiceInterface): string => {
const getServiceKey = (service: ServiceObject): string => {
return `${service.provider}-${service.identifier}`
}
const getCurrentPageLevel = (service: ServiceInterface): (string | number | null)[] => {
const getCurrentPageLevel = (service: ServiceObject): (string | number | null)[] => {
const key = getServiceKey(service)
if (!pageLevels.value[key]) {
pageLevels.value[key] = [null]
@@ -37,20 +41,19 @@ const getCurrentPageLevel = (service: ServiceInterface): (string | number | null
}
// Get folders for current page level
const getCurrentPageFolders = (service: ServiceInterface, folders: CollectionObject[]): CollectionObject[] => {
const getCurrentPageFolders = (service: ServiceObject): CollectionObject[] => {
const level = getCurrentPageLevel(service)
const currentParent = level[level.length - 1]
return folders.filter(f => {
if (currentParent === null) {
return f.collection === null || f.collection === undefined
}
return String(f.collection) === String(currentParent)
})
return collectionsStore.collectionsInCollection(service.provider, service.identifier, currentParent)
}
// Check if folder has children
const hasChildren = (folder: CollectionObject, allFolders: CollectionObject[]): boolean => {
return allFolders.some(f => String(f.collection) === String(folder.identifier))
const hasChildren = (folder: CollectionObject): boolean => {
return collectionsStore.hasChildrenInCollection(folder.provider, folder.service, folder.identifier)
}
const getServiceFolders = (service: ServiceObject): CollectionObject[] => {
return collectionsStore.collectionsForService(service.provider, service.identifier)
}
// Get icon for folder based on role
@@ -109,13 +112,13 @@ const handleFolderClick = (folder: CollectionObject) => {
}
// Navigate into a folder to show its children
const handleNavigateInto = (service: ServiceInterface, folderId: string | number) => {
const handleNavigateInto = (service: ServiceObject, folderId: string | number) => {
const level = getCurrentPageLevel(service)
level.push(folderId)
}
// Navigate back in page-based view
const navigateBack = (service: ServiceInterface) => {
const navigateBack = (service: ServiceObject) => {
const level = getCurrentPageLevel(service)
if (level.length > 1) {
level.pop()
@@ -123,14 +126,14 @@ const navigateBack = (service: ServiceInterface) => {
}
// Get breadcrumb label for current page
const getCurrentBreadcrumb = (service: ServiceInterface, folders: CollectionObject[]): string => {
const getCurrentBreadcrumb = (service: ServiceObject): string => {
const level = getCurrentPageLevel(service)
const currentParent = level[level.length - 1]
if (currentParent === null) return 'All Folders'
const labels = level
.filter((id): id is string | number => id !== null)
.map(id => folders.find(f => String(f.identifier) === String(id))?.properties.label)
.map(id => collectionsStore.collection(service.provider, service.identifier, id)?.properties.label)
.filter((label): label is string => !!label)
if (labels.length === 0) return 'Folders'
@@ -139,14 +142,12 @@ const getCurrentBreadcrumb = (service: ServiceInterface, folders: CollectionObje
}
// Get current parent folder for dialog context
const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionObject[]): CollectionObject | null => {
const getCurrentParentFolder = (service: ServiceObject): CollectionObject | null => {
const level = getCurrentPageLevel(service)
const currentParent = level[level.length - 1]
if (currentParent === null) return null
// Search through all folders in the array
const found = folders.find(f => String(f.identifier) === String(currentParent))
return found || null
return collectionsStore.collection(service.provider, service.identifier, currentParent)
}
</script>
@@ -176,7 +177,7 @@ const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionOb
variant="text"
size="small"
density="compact"
@click.stop="emit('createFolder', group.service, getCurrentParentFolder(group.service, group.folders))"
@click.stop="emit('createFolder', group.service, getCurrentParentFolder(group.service))"
>
<v-icon>mdi-folder-plus</v-icon>
<v-tooltip activator="parent" location="bottom">New Folder</v-tooltip>
@@ -187,12 +188,12 @@ const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionOb
<!-- Breadcrumb with New Folder button -->
<v-list-subheader v-if="getCurrentPageLevel(group.service).length > 1" class="d-flex align-center">
<span class="flex-grow-1">{{ getCurrentBreadcrumb(group.service, group.folders) }}</span>
<span class="flex-grow-1">{{ getCurrentBreadcrumb(group.service) }}</span>
<v-btn
icon="mdi-folder-plus"
variant="text"
size="x-small"
@click="emit('createFolder', group.service, getCurrentParentFolder(group.service, group.folders))"
@click="emit('createFolder', group.service, getCurrentParentFolder(group.service))"
>
<v-icon size="small">mdi-folder-plus</v-icon>
<v-tooltip activator="parent" location="bottom">New Subfolder</v-tooltip>
@@ -210,7 +211,7 @@ const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionOb
<!-- Current level folders -->
<v-list-item
v-for="folder in getCurrentPageFolders(group.service, group.folders)"
v-for="folder in getCurrentPageFolders(group.service)"
:key="`${folder.provider}-${folder.service}-${folder.identifier}`"
class="folder-page-item folder-row-item"
:title="folder.properties.label"
@@ -236,7 +237,7 @@ const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionOb
<!-- Chevron for folders with children -->
<v-btn
v-if="hasChildren(folder, group.folders)"
v-if="hasChildren(folder)"
icon="mdi-chevron-right"
variant="text"
size="small"
@@ -274,20 +275,73 @@ const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionOb
</v-menu>
</template>
</v-list-item>
<v-list-item v-if="group.loading && getServiceFolders(group.service).length === 0" disabled class="folder-status-item">
<template v-slot:prepend>
<v-progress-circular indeterminate size="18" width="2" color="primary" />
</template>
<v-list-item-title>Loading folders</v-list-item-title>
</v-list-item>
<v-list-item
v-else-if="group.error && getServiceFolders(group.service).length === 0"
disabled
class="folder-status-item"
>
<template v-slot:prepend>
<v-icon icon="mdi-alert-circle-outline" color="error" />
</template>
<v-list-item-title>Folders unavailable</v-list-item-title>
<v-list-item-subtitle>{{ group.error }}</v-list-item-subtitle>
</v-list-item>
<v-list-item
v-else-if="group.loaded && getServiceFolders(group.service).length === 0"
disabled
class="folder-status-item"
>
<template v-slot:prepend>
<v-icon icon="mdi-folder-off-outline" />
</template>
<v-list-item-title>No folders found</v-list-item-title>
</v-list-item>
</v-list-group>
<!-- Single service - show folders directly -->
<template v-else>
<v-list-item
class="account-header-item"
:title="group.service.label || 'Mail Account'"
:subtitle="group.service.primaryAddress || undefined"
>
<template v-slot:prepend>
<v-icon icon="mdi-email-outline" />
</template>
<template v-slot:append>
<v-btn
icon="mdi-folder-plus"
variant="text"
size="small"
density="compact"
@click.stop="emit('createFolder', group.service, getCurrentParentFolder(group.service))"
>
<v-icon>mdi-folder-plus</v-icon>
<v-tooltip activator="parent" location="bottom">New Folder</v-tooltip>
</v-btn>
</template>
</v-list-item>
<!-- Header with New Folder button -->
<v-list-subheader class="d-flex align-center">
<span class="flex-grow-1">
{{ getCurrentPageLevel(group.service).length > 1 ? getCurrentBreadcrumb(group.service, group.folders) : 'FOLDERS' }}
{{ getCurrentPageLevel(group.service).length > 1 ? getCurrentBreadcrumb(group.service) : 'FOLDERS' }}
</span>
<v-btn
icon="mdi-folder-plus"
variant="text"
size="x-small"
@click="emit('createFolder', group.service, getCurrentParentFolder(group.service, group.folders))"
@click="emit('createFolder', group.service, getCurrentParentFolder(group.service))"
>
<v-icon size="small">mdi-folder-plus</v-icon>
<v-tooltip activator="parent" location="bottom">
@@ -307,7 +361,7 @@ const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionOb
<!-- Current level folders -->
<v-list-item
v-for="folder in getCurrentPageFolders(group.service, group.folders)"
v-for="folder in getCurrentPageFolders(group.service)"
:key="`${folder.provider}-${folder.service}-${folder.identifier}`"
class="folder-row-item"
:title="folder.properties.label"
@@ -333,7 +387,7 @@ const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionOb
<!-- Chevron for folders with children or Menu for actions -->
<v-btn
v-if="hasChildren(folder, group.folders)"
v-if="hasChildren(folder)"
icon="mdi-chevron-right"
variant="text"
size="small"
@@ -370,11 +424,59 @@ const getCurrentParentFolder = (service: ServiceInterface, folders: CollectionOb
</v-menu>
</template>
</v-list-item>
<v-list-item v-if="group.loading && getServiceFolders(group.service).length === 0" disabled class="folder-status-item">
<template v-slot:prepend>
<v-progress-circular indeterminate size="18" width="2" color="primary" />
</template>
<v-list-item-title>Loading folders</v-list-item-title>
</v-list-item>
<v-list-item
v-else-if="group.error && getServiceFolders(group.service).length === 0"
disabled
class="folder-status-item"
>
<template v-slot:prepend>
<v-icon icon="mdi-alert-circle-outline" color="error" />
</template>
<v-list-item-title>Folders unavailable</v-list-item-title>
<v-list-item-subtitle>{{ group.error }}</v-list-item-subtitle>
</v-list-item>
<v-list-item
v-else-if="group.loaded && getServiceFolders(group.service).length === 0"
disabled
class="folder-status-item"
>
<template v-slot:prepend>
<v-icon icon="mdi-folder-off-outline" />
</template>
<v-list-item-title>No folders found</v-list-item-title>
</v-list-item>
</template>
</template>
</div>
</template>
<style scoped>
.account-header-item {
--v-list-item-prepend-size: 22px;
background-color: rgba(var(--v-theme-primary), 0.1);
border-radius: 6px;
margin-bottom: 4px;
}
.account-header-item :deep(.v-list-item__prepend) {
padding-inline-start: 4px;
margin-inline-end: 2px;
}
.folder-status-item {
padding-inline-start: 16px;
}
</style>
<style scoped>
.v-list-item--active {
background-color: rgba(var(--v-theme-primary), 0.12);