1import { useScreenSize } from "@/composables/screen";
2import { canViewPersona, personaInterrupt } from "@/persona";
3import { useAuthStore } from "@/stores/auth";
4import { useUserStore } from "@/stores/user";
5import { CUSTOMER_PORTAL_ROOT } from "@/utils";
6import { ROUTES } from "@opendesk/shared/portalRoutes";
7import { createRouter, createWebHistory } from "vue-router";
8const { isMobileView } = useScreenSize();
9
10export const LOGIN_PAGE = "/login";
11
12// type the meta fields
13declare module "vue-router" {
14 interface RouteMeta {
15 auth?: boolean;
16 agent?: boolean;
17 admin?: boolean;
18 public?: boolean;
19 onSuccessRoute?: string;
20 parent?: string;
21 }
22}
23
24// Pages that render inside the desk chrome.
25const portalRoutes = [
26 // Agent Portal Routes
27 {
28 path: "",
29 redirect: "/home",
30 },
31 {
32 path: "/home",
33 name: "Home",
34 component: () => import("@/pages/home/Home.vue"),
35 },
36
37 {
38 path: "/tickets",
39 name: "TicketsAgent",
40 component: () => import("@/pages/ticket/Tickets.vue"),
41 },
42 {
43 path: "/tickets/:ticketId",
44 name: "TicketAgent",
45 component: () =>
46 import(`@/pages/ticket/${handleMobileView("TicketAgent")}.vue`),
47 props: true,
48 },
49 {
50 path: "/tickets/new/:templateId?",
51 name: "TicketAgentNew",
52 component: () => import("@/pages/ticket/TicketNew.vue"),
53 props: true,
54 meta: {
55 onSuccessRoute: "TicketAgent",
56 parent: "TicketsAgent",
57 },
58 },
59 {
60 path: "/notifications",
61 name: "Notifications",
62 component: () => import("@/pages/MobileNotifications.vue"),
63 },
64 {
65 path: "/kb",
66 name: "AgentKnowledgeBase",
67 component: () => import("@/pages/knowledge-base/KnowledgeBaseAgent.vue"),
68 },
69 {
70 path: "/search",
71 name: "SearchAgent",
72 component: () => import("@/pages/SearchAgent.vue"),
73 meta: { auth: true },
74 },
75 {
76 path: "/kb/articles/:articleId",
77 name: "Article",
78 component: () => import("@/pages/knowledge-base/Article.vue"),
79 props: true,
80 },
81 {
82 path: "/articles/new/:id",
83 name: "NewArticle",
84 component: () => import("@/pages/knowledge-base/NewArticle.vue"),
85 props: true,
86 },
87 {
88 path: "/customers",
89 name: "CustomerList",
90 component: () => import("@/pages/customer/Customers.vue"),
91 },
92 {
93 path: "/customers/:id",
94 name: "Customer",
95 component: () => import("@/pages/customer/Customer.vue"),
96 props: true,
97 },
98 {
99 path: "/contacts",
100 name: "ContactList",
101 component: () => import("@/pages/contact/Contacts.vue"),
102 },
103 {
104 path: "/contacts/:id",
105 name: "Contact",
106 component: () => import("@/pages/contact/Contact.vue"),
107 props: true,
108 },
109 {
110 path: "/agents",
111 name: "AgentList",
112 redirect: "/tickets",
113 },
114 {
115 path: "/teams",
116 name: "Teams",
117 redirect: "/tickets",
118 },
119 {
120 path: "/teams/:teamId",
121 name: "Team",
122 redirect: "/tickets",
123 },
124 {
125 path: "/dashboard",
126 name: "Dashboard",
127 component: () => import("@/pages/dashboard/Dashboard.vue"),
128 },
129 {
130 path: "/call-logs",
131 name: "CallLogs",
132 component: () => import("@/pages/call-logs/CallLogs.vue"),
133 },
134
135 // Additonal routes
136 {
137 path: "/:pathMatch(.*)*",
138 name: "Invalid Page",
139 component: () => import("@/pages/InvalidPage.vue"),
140 },
141];
142
143const routes = [
144 // Renders bare — no portal chrome.
145 {
146 path: "/onboarding",
147 name: "Persona",
148 component: () => import("@/pages/PersonaForm.vue"),
149 beforeEnter: () => canViewPersona(useAuthStore()) || { name: "Home" },
150 },
151 {
152 path: "/",
153 component: () => import("@/roots/AgentRoot.vue"),
154 children: portalRoutes,
155 },
156];
157
158const handleMobileView = (componentName: string) => {
159 return isMobileView.value ? `Mobile${componentName}` : componentName;
160};
161
162export const router = createRouter({
163 history: createWebHistory("/opendesk/"),
164 routes,
165});
166
167router.beforeEach(async (to, _, next) => {
168 const authStore = useAuthStore();
169 if (authStore.isLoggedIn) {
170 await authStore.init();
171 }
172
173 const interrupt = personaInterrupt(to, authStore);
174 if (interrupt) return next(interrupt);
175
176 if (!authStore.isLoggedIn) {
177 const redirectURL = to.fullPath !== "/" ? to.fullPath : "";
178
179 window.location.href =
180 LOGIN_PAGE +
181 (redirectURL ? `?redirect-to=/opendesk${redirectURL}` : "/opendesk");
182 } else if (to.name === "TicketAgent" && !authStore.isAgent) {
183 window.location.replace(
184 CUSTOMER_PORTAL_ROOT + ROUTES.ticket(to.params.ticketId as string)
185 );
186 } else if (!authStore.hasDeskAccess) {
187 window.location.replace(window.customer_home_page);
188 } else {
189 next();
190 }
191});
192
193router.afterEach(async (to) => {
194 const { users } = useUserStore();
195 if (!users?.fetched) {
196 await users.fetch();
197 }
198});
199