314 lines
9.3 KiB
JavaScript
314 lines
9.3 KiB
JavaScript
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
|
import { useOrganizationStore } from '@/features/organizations/stores/organizationStore.js';
|
|
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
|
|
import { createRouter, createWebHistory } from 'vue-router';
|
|
|
|
const LoginView = () => import('@/features/auth/views/LoginView.vue');
|
|
const Landing = () => import('@/static/views/Landing.vue');
|
|
const ProductPage = () => import('@/static/views/ProductPage.vue');
|
|
const ProductFeaturePage = () => import('@/static/views/ProductFeaturePage.vue');
|
|
const PricingPage = () => import('@/static/views/PricingPage.vue');
|
|
const BlogsPage = () => import('@/static/views/BlogsPage.vue');
|
|
const GuidesPage = () => import('@/static/views/GuidesPage.vue');
|
|
const RegisterView = () => import('@/features/auth/views/RegisterView.vue');
|
|
const ForgotPasswordView = () => import('@/features/auth/views/ForgotPasswordView.vue');
|
|
const ResetPasswordView = () => import('@/features/auth/views/ResetPasswordView.vue');
|
|
const VerifyEmailView = () => import('@/features/auth/views/VerifyEmailView.vue');
|
|
const OverviewView = () => import('@/features/workspaces/views/OverviewView.vue');
|
|
const ChannelsView = () => import('@/features/channels/views/ChannelsView.vue');
|
|
const CampaignsView = () => import('@/features/campaigns/views/CampaignsView.vue');
|
|
const CampaignDetailView = () => import('@/features/campaigns/views/CampaignDetailView.vue');
|
|
const MediaLibraryView = () => import('@/features/content/views/MediaLibraryView.vue');
|
|
const WorkspaceCreateView = () => import('@/features/workspaces/views/WorkspaceCreateView.vue');
|
|
const OrganizationOnboardingView = () => import('@/features/organizations/views/OrganizationOnboardingView.vue');
|
|
const OrganizationSettingsView = () => import('@/features/organizations/views/OrganizationSettingsView.vue');
|
|
const SettingsLayoutView = () => import('@/features/settings/views/SettingsLayoutView.vue');
|
|
const UserSettingsView = () => import('@/features/user-profile/views/UserSettingsView.vue');
|
|
const IntegrationsSettingsView = () => import('@/features/settings/views/IntegrationsSettingsView.vue');
|
|
const WorkspaceSettingsView = () => import('@/features/workspaces/views/WorkspaceSettingsView.vue');
|
|
const ReviewQueueView = () => import('@/features/reviews/views/ReviewQueueView.vue');
|
|
const ContentItemsView = () => import('@/features/content/views/ContentItemsView.vue');
|
|
const ContentItemDetailView = () => import('@/features/content/views/ContentItemDetailView.vue');
|
|
const MyFeedbackListView = () => import('@/features/feedback/views/MyFeedbackListView.vue');
|
|
const MyFeedbackDetailView = () => import('@/features/feedback/views/MyFeedbackDetailView.vue');
|
|
const DeveloperFeedbackListView = () => import('@/features/feedback/views/DeveloperFeedbackListView.vue');
|
|
const DeveloperFeedbackDetailView = () => import('@/features/feedback/views/DeveloperFeedbackDetailView.vue');
|
|
|
|
const routes = [
|
|
{
|
|
path: '/',
|
|
name: 'landing',
|
|
component: Landing,
|
|
},
|
|
{
|
|
path: '/product',
|
|
name: 'product',
|
|
component: ProductPage,
|
|
},
|
|
{
|
|
path: '/product/:featureSlug',
|
|
name: 'product-feature',
|
|
component: ProductFeaturePage,
|
|
},
|
|
{
|
|
path: '/pricing',
|
|
name: 'pricing',
|
|
component: PricingPage,
|
|
},
|
|
{
|
|
path: '/blogs',
|
|
name: 'blogs',
|
|
component: BlogsPage,
|
|
},
|
|
{
|
|
path: '/guides',
|
|
name: 'guides',
|
|
component: GuidesPage,
|
|
},
|
|
{
|
|
path: '/app',
|
|
redirect: { name: 'dashboard' },
|
|
},
|
|
{
|
|
path: '/app/dashboard',
|
|
name: 'dashboard',
|
|
component: OverviewView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/workspace',
|
|
name: 'workspace-dashboard',
|
|
redirect: { name: 'content-items' },
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/channels',
|
|
name: 'channels',
|
|
component: ChannelsView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/media-library',
|
|
name: 'media-library',
|
|
component: MediaLibraryView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/campaigns',
|
|
name: 'campaigns',
|
|
component: CampaignsView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/campaigns/:campaignId',
|
|
name: 'campaign-detail',
|
|
component: CampaignDetailView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/reviews',
|
|
name: 'review-queue',
|
|
component: ReviewQueueView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/my-feedback',
|
|
name: 'my-feedback',
|
|
component: MyFeedbackListView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/my-feedback/:id',
|
|
name: 'my-feedback-detail',
|
|
component: MyFeedbackDetailView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/feedback',
|
|
name: 'developer-feedback',
|
|
component: DeveloperFeedbackListView,
|
|
meta: { requiresAuth: true, roles: ['developer'] },
|
|
},
|
|
{
|
|
path: '/app/feedback/:id',
|
|
name: 'developer-feedback-detail',
|
|
component: DeveloperFeedbackDetailView,
|
|
meta: { requiresAuth: true, roles: ['developer'] },
|
|
},
|
|
{
|
|
path: '/app/onboarding',
|
|
name: 'organization-onboarding',
|
|
component: OrganizationOnboardingView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/organizations/:organizationId/settings',
|
|
name: 'organization-settings',
|
|
component: OrganizationSettingsView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/workspace-settings',
|
|
name: 'workspace-settings',
|
|
component: WorkspaceSettingsView,
|
|
meta: { requiresAuth: true, roles: ['administrator', 'manager'] },
|
|
},
|
|
{
|
|
path: '/app/workspaces/new',
|
|
name: 'workspace-create',
|
|
component: WorkspaceCreateView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/settings',
|
|
component: SettingsLayoutView,
|
|
meta: { requiresAuth: true },
|
|
children: [
|
|
{
|
|
path: '',
|
|
redirect: { name: 'settings-user-information' },
|
|
},
|
|
{
|
|
path: 'user-information',
|
|
name: 'settings-user-information',
|
|
component: UserSettingsView,
|
|
},
|
|
{
|
|
path: 'workspaces',
|
|
name: 'settings-workspaces',
|
|
component: WorkspaceSettingsView,
|
|
meta: { requiresAuth: true, roles: ['administrator', 'manager'] },
|
|
},
|
|
{
|
|
path: 'integrations',
|
|
name: 'settings-integrations',
|
|
component: IntegrationsSettingsView,
|
|
meta: { requiresAuth: true, roles: ['administrator', 'manager'] },
|
|
},
|
|
],
|
|
},
|
|
{
|
|
path: '/app/content',
|
|
name: 'content-items',
|
|
component: ContentItemsView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/content/new',
|
|
name: 'content-item-create',
|
|
component: ContentItemDetailView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/app/content/:id',
|
|
name: 'content-item-detail',
|
|
component: ContentItemDetailView,
|
|
meta: { requiresAuth: true },
|
|
},
|
|
{
|
|
path: '/login',
|
|
name: 'login',
|
|
component: LoginView,
|
|
meta: { notAuthenticated: true },
|
|
props: route => ({ returnUrl: route.query.returnUrl || '/app/dashboard' }),
|
|
},
|
|
{
|
|
path: '/profile',
|
|
redirect: { name: 'dashboard' },
|
|
},
|
|
{
|
|
path: '/register',
|
|
name: 'register',
|
|
component: RegisterView,
|
|
meta: { requiresAuth: false },
|
|
},
|
|
{
|
|
path: '/forgot-password',
|
|
name: 'forgot-password',
|
|
component: ForgotPasswordView,
|
|
meta: { notAuthenticated: true },
|
|
},
|
|
{
|
|
path: '/reset-password',
|
|
name: 'reset-password',
|
|
component: ResetPasswordView,
|
|
meta: { notAuthenticated: true },
|
|
props: route => ({ email: route.query.email, token: route.query.token }),
|
|
},
|
|
{
|
|
path: '/verify-email',
|
|
name: 'verify-email',
|
|
component: VerifyEmailView,
|
|
meta: { notAuthenticated: true },
|
|
},
|
|
{
|
|
path: '/:pathMatch(.*)*',
|
|
redirect: { name: 'landing' },
|
|
},
|
|
];
|
|
|
|
const router = createRouter({
|
|
history: createWebHistory(import.meta.env.BASE_URL),
|
|
routes,
|
|
});
|
|
|
|
async function loadAccessScope() {
|
|
const organizationStore = useOrganizationStore();
|
|
const workspaceStore = useWorkspaceStore();
|
|
|
|
await Promise.all([
|
|
organizationStore.fetchOrganizations(),
|
|
workspaceStore.fetchWorkspaces(),
|
|
]);
|
|
}
|
|
|
|
function hasNoOrganizationOrWorkspaceAccess() {
|
|
const organizationStore = useOrganizationStore();
|
|
const workspaceStore = useWorkspaceStore();
|
|
|
|
return organizationStore.organizations.length === 0 && workspaceStore.workspaces.length === 0;
|
|
}
|
|
|
|
async function getAuthenticatedHomeRoute() {
|
|
await loadAccessScope();
|
|
|
|
return hasNoOrganizationOrWorkspaceAccess()
|
|
? { name: 'organization-onboarding' }
|
|
: { name: 'dashboard' };
|
|
}
|
|
|
|
// Navigation guards
|
|
router.beforeEach(async (to) => {
|
|
const authStore = useAuthStore();
|
|
|
|
if (to.matched.some(record => record.meta.requiresAuth)) {
|
|
if (!authStore.isAuthenticated) {
|
|
return {
|
|
name: 'login',
|
|
query: { returnUrl: to.fullPath },
|
|
};
|
|
}
|
|
|
|
await loadAccessScope();
|
|
|
|
if (hasNoOrganizationOrWorkspaceAccess() && to.name !== 'organization-onboarding') {
|
|
return { name: 'organization-onboarding' };
|
|
}
|
|
|
|
const requiredRoles = to.matched.flatMap(record => record.meta.roles ?? []);
|
|
if (requiredRoles.length > 0 && !authStore.hasAnyRole(requiredRoles)) {
|
|
return { name: 'dashboard' };
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (to.matched.some(record => record.meta.notAuthenticated)) {
|
|
return authStore.isAuthenticated ? await getAuthenticatedHomeRoute() : true;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
export default router;
|