diff --git a/src/main/java/fr/recia/collabsoft/service/db/AssociatedAppService.java b/src/main/java/fr/recia/collabsoft/service/db/AssociatedAppService.java new file mode 100644 index 00000000..7b4a50cd --- /dev/null +++ b/src/main/java/fr/recia/collabsoft/service/db/AssociatedAppService.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 GIP-RECIA, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fr.recia.collabsoft.service.db; + +import fr.recia.collabsoft.db.entity.AssociatedApp; +import fr.recia.collabsoft.db.repository.AssociatedAppRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.IteratorUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +public class AssociatedAppService { + + @Autowired + private AssociatedAppRepository associatedAppRepository; + + public List getApps() { + final List apps = IteratorUtils.toList( + associatedAppRepository.findAll().iterator() + ); + + if (apps.isEmpty()) log.debug("No apps"); + + return apps; + } + +} diff --git a/src/main/java/fr/recia/collabsoft/web/rest/ConfigurationController.java b/src/main/java/fr/recia/collabsoft/web/rest/ConfigurationController.java index 93a31176..c7474114 100644 --- a/src/main/java/fr/recia/collabsoft/web/rest/ConfigurationController.java +++ b/src/main/java/fr/recia/collabsoft/web/rest/ConfigurationController.java @@ -15,7 +15,10 @@ */ package fr.recia.collabsoft.web.rest; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import fr.recia.collabsoft.configuration.CollabsoftProperties; +import fr.recia.collabsoft.service.db.AssociatedAppService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -34,11 +37,17 @@ public class ConfigurationController { @Autowired private CollabsoftProperties collabsoftProperties; + @Autowired + private AssociatedAppService associatedAppService; @GetMapping public ResponseEntity getConfiguration() { Map data = new HashMap<>(); - data.put("front", collabsoftProperties.getFront()); + ObjectMapper objectMapper = new ObjectMapper(); + Map front = objectMapper.convertValue(collabsoftProperties.getFront(), new TypeReference<>() { + }); + front.put("apps", associatedAppService.getApps()); + data.put("front", front); return new ResponseEntity<>(data, HttpStatus.OK); } diff --git a/src/main/webapp/src/router/index.ts b/src/main/webapp/src/router/index.ts index a1340640..bd3ae40e 100644 --- a/src/main/webapp/src/router/index.ts +++ b/src/main/webapp/src/router/index.ts @@ -1,15 +1,16 @@ -import { AppSlug } from '@/types/enums/AppSlug.ts'; import { Navigation } from '@/types/enums/Navigation.ts'; -import { createRouter, createWebHistory } from 'vue-router'; +import { type RouteRecordRedirectOption, createRouter, createWebHistory } from 'vue-router'; + +const redirect: RouteRecordRedirectOption = () => { + return { name: Navigation.projects }; +}; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', - redirect: () => { - return { name: Navigation.projects }; - }, + redirect, component: () => import('@/views/HomeView.vue'), children: [ { @@ -36,38 +37,14 @@ const router = createRouter({ }, { path: '/app', - redirect: () => { - return { name: Navigation.projects }; - }, + name: 'app', + redirect, component: () => import('@/views/AppView.vue'), - children: [ - { - path: `${AppSlug.tldraw}/:fileId(\\d+)`, - name: AppSlug.tldraw, - component: () => import('@/views/app/tldraw/TldrawView.vue'), - }, - { - path: `${AppSlug.tldraw}/:roomId([A-Z]{6})`, - name: `collaborative-${AppSlug.tldraw}`, - component: () => import('@/views/app/tldraw/CollaborativeTldrawView.vue'), - }, - { - path: `${AppSlug.wisemapping}/:fileId(\\d+)`, - name: AppSlug.wisemapping, - component: () => import('@/views/app/wisemapping/WisemappingView.vue'), - }, - { - path: `${AppSlug.wisemapping}/:roomId([A-Z]{6})`, - name: `collaborative-${AppSlug.wisemapping}`, - component: () => import('@/views/app/wisemapping/CollaborativeWisemappingView.vue'), - }, - ], + children: [], }, { path: '/:pathName(.*)', - redirect: () => { - return { name: Navigation.projects }; - }, + redirect, }, ], }); diff --git a/src/main/webapp/src/stores/configurationStore.ts b/src/main/webapp/src/stores/configurationStore.ts index 82522033..d542d499 100644 --- a/src/main/webapp/src/stores/configurationStore.ts +++ b/src/main/webapp/src/stores/configurationStore.ts @@ -1,8 +1,10 @@ import { getConfiguration } from '@/services/configurationService.ts'; +import type { AssociatedApp } from '@/types/associatedAppType.ts'; import type { Configuration } from '@/types/configurationType.ts'; import type { Soffit } from '@/types/soffitType.ts'; import { errorHandler, initToken } from '@/utils/axiosUtils.ts'; import { useEntTheme } from '@/utils/entUtils.ts'; +import { initAppsRoutes } from '@/utils/routerUtils.ts'; import { defineStore } from 'pinia'; import { computed, ref } from 'vue'; @@ -19,6 +21,7 @@ export const useConfigurationStore = defineStore('configuration', () => { configuration.value = response.data; await initToken(configuration.value!.front.userInfoApiUrl); await useEntTheme(configuration.value!.front.extendedUportalHeader.templateApiPath); + await initAppsRoutes(configuration.value!.front.apps); return true; } catch (e) { @@ -47,6 +50,12 @@ export const useConfigurationStore = defineStore('configuration', () => { */ const isSettings = ref(false); + /* -- Apps -- */ + + const availableApps = computed>(() => + configuration.value!.front.apps.filter((app) => app.enabled), + ); + return { configuration, init, @@ -56,5 +65,6 @@ export const useConfigurationStore = defineStore('configuration', () => { isSoffitOk, lastNavigation, isSettings, + availableApps, }; }); diff --git a/src/main/webapp/src/types/configurationType.ts b/src/main/webapp/src/types/configurationType.ts index d72f4572..f62c144c 100644 --- a/src/main/webapp/src/types/configurationType.ts +++ b/src/main/webapp/src/types/configurationType.ts @@ -1,3 +1,5 @@ +import type { AssociatedApp } from '@/types/associatedAppType.ts'; + export type Configuration = { front: { userInfoApiUrl: string; @@ -33,5 +35,6 @@ export type Configuration = { componentPath: string; templateApiPath: string; }; + apps: Array; }; }; diff --git a/src/main/webapp/src/utils/routerUtils.ts b/src/main/webapp/src/utils/routerUtils.ts new file mode 100644 index 00000000..95d8b3ec --- /dev/null +++ b/src/main/webapp/src/utils/routerUtils.ts @@ -0,0 +1,25 @@ +import type { AssociatedApp } from '@/types/associatedAppType.ts'; +import { capitalize } from 'vue'; +import { type RouteRecordRaw, type Router } from 'vue-router'; + +const initAppsRoutes = async (apps: Array): Promise => { + const router: Router = (await import('@/router/index.ts')).default; + + apps.forEach((app) => { + const solo: RouteRecordRaw = { + path: `${app.slug}/:fileId(\\d+)`, + name: app.slug, + component: () => import(`@/views/app/${app.slug}/${capitalize(app.slug)}View.vue`), + }; + router.addRoute('app', solo); + + const multi: RouteRecordRaw = { + path: `${app.slug}/:roomId([A-Z]{6})`, + name: `collaborative-${app.slug}`, + component: () => import(`@/views/app/${app.slug}/Collaborative${capitalize(app.slug)}View.vue`), + }; + router.addRoute('app', multi); + }); +}; + +export { initAppsRoutes };