chore: add missing multi-level editor for approval workflow, rename projects to campaings.

This commit is contained in:
2026-05-01 14:23:37 -04:00
parent 5077f557f4
commit 884ca4b96d
148 changed files with 11567 additions and 1383 deletions

View File

@@ -9,13 +9,14 @@
import { useNotificationsStore } from '@/features/notifications/stores/notificationsStore.js';
import { getNotificationRoute } from '@/features/notifications/notificationRoutes.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
import { useCampaignsStore } from '@/features/campaigns/stores/campaignsStore.js';
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
import {
mdiBellOutline,
mdiCalendarMonthOutline,
mdiChevronDown,
mdiCogOutline,
mdiFileDocumentOutline,
mdiFolderOutline,
mdiHomeOutline,
mdiImageMultipleOutline,
@@ -40,7 +41,7 @@
const contentItemsStore = useContentItemsStore();
const languageStore = useLanguageStore();
const notificationsStore = useNotificationsStore();
const projectsStore = useProjectsStore();
const campaignsStore = useCampaignsStore();
const userProfileStore = useUserProfileStore();
const isUserMenuOpen = ref(false);
const isNotificationsOpen = ref(false);
@@ -56,7 +57,7 @@
{ to: '/app/workspace', labelKey: 'nav.workspacePlan', icon: mdiCalendarMonthOutline },
{ to: '/app/media-library', labelKey: 'nav.mediaLibrary', icon: mdiImageMultipleOutline },
{ to: '/app/my-feedback', labelKey: 'nav.myFeedback', icon: mdiBugOutline },
{ to: '/app/feedback', labelKey: 'nav.feedbackReview', icon: mdiBugOutline, roles: ['Developer'] },
{ to: '/app/feedback', labelKey: 'nav.feedbackReview', icon: mdiBugOutline, roles: ['developer'] },
{ to: '/app/workspace-settings', labelKey: 'nav.settings', icon: mdiCogOutline },
];
const visiblePrimaryLinks = computed(() =>
@@ -65,23 +66,23 @@
const openSections = ref({
channels: false,
projects: false,
campaigns: false,
});
const normalizedSearchQuery = computed(() => searchQuery.value.trim().toLowerCase());
const projectResults = computed(() => {
const campaignResults = computed(() => {
if (!normalizedSearchQuery.value) {
return [];
}
return projectsStore.projects
.filter(project => project.name.toLowerCase().includes(normalizedSearchQuery.value))
return campaignsStore.campaigns
.filter(campaign => campaign.name.toLowerCase().includes(normalizedSearchQuery.value))
.slice(0, 5)
.map(project => ({
id: project.id,
label: project.name,
.map(campaign => ({
id: campaign.id,
label: campaign.name,
description: 'Campaign',
route: { name: 'campaign-detail', params: { projectId: project.id } },
route: { name: 'campaign-detail', params: { campaignId: campaign.id } },
}));
});
const contentResults = computed(() => {
@@ -104,7 +105,7 @@
}));
});
const hasSearchResults = computed(() =>
projectResults.value.length > 0 || contentResults.value.length > 0
campaignResults.value.length > 0 || contentResults.value.length > 0
);
const isSearchOpen = computed(() => isSearchFocused.value && normalizedSearchQuery.value.length > 0);
@@ -208,7 +209,7 @@
}
if (path.startsWith('/app/campaigns')) {
openSections.value.projects = true;
openSections.value.campaigns = true;
}
},
{ immediate: true }
@@ -269,13 +270,13 @@
class="sidebar-floating-panel"
>
<div
v-if="projectResults.length"
v-if="campaignResults.length"
class="sidebar-search-group"
>
<strong>Campaigns</strong>
<button
v-for="result in projectResults"
:key="`project-${result.id}`"
v-for="result in campaignResults"
:key="`campaign-${result.id}`"
class="sidebar-search-result"
@click="openSearchResult(result)"
>
@@ -401,13 +402,43 @@
</router-link>
</div>
<div class="sidebar-section">
<div class="sidebar-section-header">
<router-link
to="/app/content"
class="sidebar-link sidebar-link-section"
active-class="sidebar-link-active"
:title="!isExpanded ? t('nav.content') : null"
>
<span class="sidebar-link-main">
<v-icon :icon="mdiFileDocumentOutline" />
<span
v-if="isExpanded"
class="sidebar-link-label"
>
{{ t('nav.content') }}
</span>
</span>
</router-link>
<router-link
v-if="isExpanded"
:to="{ name: 'content-item-create' }"
class="sidebar-section-action"
:title="t('contentItems.newItem')"
>
<v-icon :icon="mdiPlus" />
</router-link>
</div>
</div>
<div class="sidebar-section">
<div class="sidebar-section-header">
<router-link
to="/app/campaigns"
class="sidebar-link sidebar-link-section"
active-class="sidebar-link-active"
:title="!isExpanded ? t('nav.projects') : null"
:title="!isExpanded ? t('nav.campaigns') : null"
>
<span class="sidebar-link-main">
<v-icon :icon="mdiFolderOutline" />
@@ -415,7 +446,7 @@
v-if="isExpanded"
class="sidebar-link-label"
>
{{ t('nav.projects') }}
{{ t('nav.campaigns') }}
</span>
</span>
</router-link>
@@ -424,7 +455,7 @@
v-if="isExpanded"
to="/app/campaigns?create=true"
class="sidebar-section-action"
:title="t('projects.createTitle')"
:title="t('campaigns.createTitle')"
>
<v-icon :icon="mdiPlus" />
</router-link>
@@ -433,18 +464,18 @@
v-if="isExpanded"
class="sidebar-section-toggle"
type="button"
@click="toggleSection('projects')"
@click="toggleSection('campaigns')"
>
<v-icon
:icon="mdiChevronDown"
class="sidebar-chevron"
:class="{ 'sidebar-chevron-open': openSections.projects }"
:class="{ 'sidebar-chevron-open': openSections.campaigns }"
/>
</button>
</div>
<div
v-if="isExpanded && openSections.projects"
v-if="isExpanded && openSections.campaigns"
class="sidebar-sublist"
>
<router-link
@@ -452,24 +483,24 @@
class="sidebar-sublink sidebar-sublink-overview"
active-class="sidebar-sublink-active"
>
<span>{{ t('sidebar.allProjects') }}</span>
<span>{{ t('sidebar.allCampaigns') }}</span>
</router-link>
<router-link
v-for="project in projectsStore.projects"
:key="project.id"
:to="{ name: 'campaign-detail', params: { projectId: project.id } }"
v-for="campaign in campaignsStore.campaigns"
:key="campaign.id"
:to="{ name: 'campaign-detail', params: { campaignId: campaign.id } }"
class="sidebar-sublink"
active-class="sidebar-sublink-active"
>
<span>{{ project.name }}</span>
<span>{{ campaign.name }}</span>
</router-link>
<div
v-if="!projectsStore.projects.length"
v-if="!campaignsStore.campaigns.length"
class="sidebar-empty"
>
{{ t('sidebar.noProjects') }}
{{ t('sidebar.noCampaigns') }}
</div>
</div>
</div>