From 1faa0777e4c4d1af8443f44a395b2f3ceaf5a32b Mon Sep 17 00:00:00 2001 From: Cloud User Date: Mon, 7 Oct 2024 17:08:04 +0000 Subject: [PATCH] Reapply "T#2886/skills display iframe in memory history" This reverts commit adaad13cce93270bd19a3c6ac7fe789b333175a4. --- .gitignore | 1 + dashboard/pom.xml | 2 +- dashboard/src/App.vue | 69 ++++--- .../metrics/common/NumUsersPerDay.vue | 22 +++ .../metrics/common/UserTagChartConfig.js | 4 + .../metrics/common/UserTagsByLevelChart.vue | 37 +++- .../TrainingProfileComparisonChart.vue | 26 ++- .../metrics/skill/UsersByTagChart.vue | 24 +++ .../utils/services/UsePageVisitService.js | 5 +- dashboard/src/main.js | 5 +- dashboard/src/router/SkillsClientPath.js | 32 ++++ .../src/router/SkillsDisplayChildRoutes.js | 32 ++-- .../router/SkillsDisplaySkillsClientRoutes.js | 3 +- dashboard/src/router/UseGlobalNavGuards.js | 21 +-- dashboard/src/router/index.js | 27 ++- .../src/skills-display/SkillsClientUtil.js | 24 +++ .../skills-display/SkillsDisplayInIframe.vue | 44 +++++ .../skills-display/UseSkillsDisplayInfo.js | 21 +-- .../components/test/TestSkillsClient.vue | 8 +- .../components/test/TestSkillsDisplay.vue | 5 + .../userTranscript/UseLoadTranscriptData.js | 40 +++- .../skills-display/iframe/UseIframeInit.js | 49 ++++- .../services/UseSkillsDisplayService.js | 13 +- .../stores/UseSkillsDisplayAttributesState.js | 4 +- .../stores/UseSkillsDisplayBreadcrumbState.js | 2 +- .../UseSkillsDisplayParentFrameState.js | 13 +- dashboard/src/stores/UseAuthState.js | 1 + .../client-display-features_spec.js | 2 +- .../client-display-last-seen_spec.js | 2 +- .../skills-client-features_spec.js | 122 ++++++++++++ ...isplay-navigation-in-skills-client_spec.js | 168 +++++++++++++++++ ...transcript-export-in-skills-client_spec.js | 175 ++++++++++++++++++ .../skills-display-transcript-export_spec.js | 81 ++------ .../e2e/metrics/projectMetrics_skills_spec.js | 2 +- e2e-tests/cypress/e2e/skills_table_spec.js | 4 +- e2e-tests/cypress/support/commands.js | 2 +- pom.xml | 2 +- service/pom.xml | 2 +- .../skills/auth/form/oauth2/OAuthUtils.groovy | 4 +- .../controller/UserInfoController.groovy | 2 +- .../model/SubjectSkillsExportResult.groovy | 2 +- .../skills/services/UserInfoValidator.groovy | 17 +- .../services/settings/SettingsService.groovy | 14 +- 43 files changed, 929 insertions(+), 206 deletions(-) create mode 100644 dashboard/src/router/SkillsClientPath.js create mode 100644 dashboard/src/skills-display/SkillsClientUtil.js create mode 100644 e2e-tests/cypress/e2e/client-display/skills-client-features_spec.js create mode 100644 e2e-tests/cypress/e2e/client-display/skills-display-navigation-in-skills-client_spec.js create mode 100644 e2e-tests/cypress/e2e/client-display/skills-display-transcript-export-in-skills-client_spec.js diff --git a/.gitignore b/.gitignore index aa045ff046..13077705f3 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ service/src/main/resources/public/** .history /e2e-tests/cypress/screenshots/ /e2e-tests/cypress/snapshots/**/__diff_output__ +/e2e-tests/cypress/downloads/* /dashboard/build/ diff --git a/dashboard/pom.xml b/dashboard/pom.xml index d47233f23d..22692c0dd8 100644 --- a/dashboard/pom.xml +++ b/dashboard/pom.xml @@ -5,7 +5,7 @@ skills-service-parent skill-tree - 3.2.0-SNAPSHOT + 3.1.1-SNAPSHOT 4.0.0 diff --git a/dashboard/src/App.vue b/dashboard/src/App.vue index 7a78baea49..2e4c7ffb82 100644 --- a/dashboard/src/App.vue +++ b/dashboard/src/App.vue @@ -44,8 +44,6 @@ import ScrollToTop from '@/common-components/utilities/ScrollToTop.vue' import IconManagerService from '@/components/utils/iconPicker/IconManagerService.js' import log from 'loglevel'; -log.setLevel('WARN') - const authState = useAuthState() const appInfoState = useAppInfoState() const appConfig = useAppConfig() @@ -76,18 +74,25 @@ const addCustomIconCSSForClientDisplay = () => { } const inceptionConfigurer = useInceptionConfigurer() const pageVisitService = usePageVisitService() +const loadUserAndDisplayInfo = () => { + inceptionConfigurer.configure() + pageVisitService.reportPageVisit(route.path, route.fullPath) + const loadRoot = accessState.loadIsRoot() + const loadSupervisor = accessState.loadIsSupervisor() + const loadEmailEnabled = appInfoState.loadEmailEnabled() + const loadCustomIconCSS = addCustomIconCSSForClientDisplay() + const promises = [loadRoot, loadSupervisor, loadEmailEnabled, loadCustomIconCSS] + if (!skillsDisplayInfo.isSkillsClientPath()) { + const loadTheme = themeHelper.loadTheme() + promises.push(loadTheme) + } + return Promise.all(promises).then(() => { + isAppLoaded.value = true + }) +} watch(() => authState.userInfo, async (newUserInfo) => { if (newUserInfo) { - inceptionConfigurer.configure() - pageVisitService.reportPageVisit(route.path, route.fullPath) - const loadRoot = accessState.loadIsRoot() - const loadSupervisor = accessState.loadIsSupervisor() - const loadEmailEnabled = appInfoState.loadEmailEnabled() - const loadTheme = themeHelper.loadTheme() - const loadCustomIconCSS = addCustomIconCSSForClientDisplay() - Promise.all([loadRoot, loadSupervisor, loadEmailEnabled, loadTheme, loadCustomIconCSS]).then(() => { - isAppLoaded.value = true - }) + await loadUserAndDisplayInfo() } else { isAppLoaded.value = true } @@ -100,6 +105,7 @@ watch(() => themeHelper.currentTheme, (newTheme, oldTheme) => { const iframeInit = useIframeInit() onBeforeMount(() => { if (skillsDisplayInfo.isSkillsClientPath()) { + log.trace('App.vue: skillsDisplayInfo.isSkillsClientPath()=true, initiating iframe handshake') iframeInit.handleHandshake() } errorHandling.registerErrorHandling() @@ -110,6 +116,7 @@ onBeforeMount(() => { onMounted(() => { invoke(async () => { if (skillsDisplayInfo.isSkillsClientPath()) { + log.trace('App.vue: skillsDisplayInfo.isSkillsClientPath()=true, waiting for iframe to load') await until(iframeInit.loadedIframe).toBe(true) log.debug('App.vue: skillsDisplayInfo.isSkillsClientPath()=true, loaded iframe!') } @@ -117,18 +124,32 @@ onMounted(() => { }) }) +const restoreSessionIfAvailable = () => { + if (skillsDisplayInfo.isSkillsClientPath()) { + authState.setRestoringSession(false) + return Promise.resolve() + } + return authState.restoreSessionIfAvailable() +} + const loadConfigs = () => { appConfig.loadConfigState().finally(() => { - authState.restoreSessionIfAvailable().finally(() => { + restoreSessionIfAvailable().finally(() => { skillsDisplayAttributes.loadConfigStateIfNeeded().then(() => { inceptionConfigurer.configure() - globalNavGuards.addNavGuards() + if (!skillsDisplayInfo.isSkillsClientPath()) { + globalNavGuards.addNavGuards() + } if (!authState.isAuthenticated) { isAppLoaded.value = true } - // do not need to set isAppLoaded to true here because it will be handled - // by the watch of authState.userInfo + if (skillsDisplayInfo.isSkillsClientPath()) { + loadUserAndDisplayInfo() + } else { + // do not need to set isAppLoaded to true here because it will be handled + // by the watch of authState.userInfo + } }) }) }) @@ -150,9 +171,11 @@ const isDashboardFooter = computed(() => notSkillsClient.value && !isLoadingApp.
-
- -

Loading...

+
+
+ +

Loading...

+
@@ -166,12 +189,12 @@ const isDashboardFooter = computed(() => notSkillsClient.value && !isLoadingApp.
+ + + +
- - - - diff --git a/dashboard/src/components/metrics/common/NumUsersPerDay.vue b/dashboard/src/components/metrics/common/NumUsersPerDay.vue index 12aa1d3b3c..a22849c215 100644 --- a/dashboard/src/components/metrics/common/NumUsersPerDay.vue +++ b/dashboard/src/components/metrics/common/NumUsersPerDay.vue @@ -22,6 +22,9 @@ import MetricsOverlay from "@/components/metrics/utils/MetricsOverlay.vue"; import MetricsService from "@/components/metrics/MetricsService.js"; import TimeLengthSelector from "@/components/metrics/common/TimeLengthSelector.vue"; import NumberFormatter from '@/components/utils/NumberFormatter.js' +import { useSkillsDisplayThemeState } from '@/skills-display/stores/UseSkillsDisplayThemeState.js'; +import { useThemesHelper } from '@/components/header/UseThemesHelper.js'; + const appConfig = useAppConfig(); const route = useRoute(); @@ -33,6 +36,16 @@ const props = defineProps({ }, }); +const themeState = useSkillsDisplayThemeState() +const themeHelper = useThemesHelper() + +const chartAxisColor = () => { + if (themeState.theme.charts.axisLabelColor) { + return themeState.theme.charts.axisLabelColor + } + return themeHelper.isDarkTheme ? 'white' : undefined +} + onMounted(() => { if (route.params.skillId) { localProps.value.skillId = route.params.skillId; @@ -96,6 +109,9 @@ const chartOptions = ref({ }, yaxis: { labels: { + style: { + colors: chartAxisColor() + }, formatter(val) { return NumberFormatter.format(val); }, @@ -106,8 +122,14 @@ const chartOptions = ref({ }, xaxis: { type: 'datetime', + labels: { + style: { + colors: chartAxisColor() + } + } }, tooltip: { + theme: themeHelper.isDarkTheme ? 'dark' : 'light', shared: false, y: { formatter(val) { diff --git a/dashboard/src/components/metrics/common/UserTagChartConfig.js b/dashboard/src/components/metrics/common/UserTagChartConfig.js index e2132a98fa..de2e66a696 100644 --- a/dashboard/src/components/metrics/common/UserTagChartConfig.js +++ b/dashboard/src/components/metrics/common/UserTagChartConfig.js @@ -14,9 +14,12 @@ * limitations under the License. */ import NumberFormatter from '@/components/utils/NumberFormatter.js'; +import { useThemesHelper } from '@/components/header/UseThemesHelper.js'; export function useUserTagChartConfig() { + const themeHelper = useThemesHelper() + const pieChartOptions = { chart: { // height: 250, @@ -117,6 +120,7 @@ export function useUserTagChartConfig() { opacity: 1, }, tooltip: { + theme: themeHelper.isDarkTheme ? 'dark' : 'light', y: { formatter(val) { return NumberFormatter.format(val); diff --git a/dashboard/src/components/metrics/common/UserTagsByLevelChart.vue b/dashboard/src/components/metrics/common/UserTagsByLevelChart.vue index 5bbcde0d3c..d8d58f56a8 100644 --- a/dashboard/src/components/metrics/common/UserTagsByLevelChart.vue +++ b/dashboard/src/components/metrics/common/UserTagsByLevelChart.vue @@ -19,43 +19,58 @@ import MetricsService from "@/components/metrics/MetricsService.js"; import { useRoute } from 'vue-router'; import MetricsOverlay from "@/components/metrics/utils/MetricsOverlay.vue"; import NumberFormatter from '@/components/utils/NumberFormatter.js'; +import { useSkillsDisplayThemeState } from '@/skills-display/stores/UseSkillsDisplayThemeState.js'; +import { useThemesHelper } from '@/components/header/UseThemesHelper.js'; const props = defineProps(['tag']); const route = useRoute(); +const themeState = useSkillsDisplayThemeState() +const themeHelper = useThemesHelper() + +const chartAxisColor = () => { + if (themeState.theme.charts.axisLabelColor) { + return themeState.theme.charts.axisLabelColor + } + return themeHelper.isDarkTheme ? 'white' : undefined +} const series = ref([]); const loading = ref(true); const chartOptions = ref({ chart: { width: 250, - type: 'bar', - toolbar: { + type: 'bar', + toolbar: { show: true, - offsetX: 0, - offsetY: 0, + offsetX: 0, + offsetY: 0, }, }, plotOptions: { bar: { horizontal: true, - dataLabels: { + dataLabels: { position: 'bottom', }, }, }, stroke: { show: true, - width: 2, - colors: ['transparent'], + width: 2, + colors: ['transparent'], }, xaxis: { title: { + style: { + color: chartAxisColor() + }, text: '# of Users', }, labels: { style: { fontSize: '13px', - fontWeight: 600, + fontWeight: 600, + colors: chartAxisColor(), }, }, }, @@ -63,8 +78,14 @@ const chartOptions = ref({ title: { text: props.tag.label, }, + labels: { + style: { + colors: chartAxisColor() + } + } }, tooltip: { + theme: themeHelper.isDarkTheme ? 'dark' : 'light', y: { formatter(val) { return NumberFormatter.format(val); diff --git a/dashboard/src/components/metrics/multipleProjects/TrainingProfileComparisonChart.vue b/dashboard/src/components/metrics/multipleProjects/TrainingProfileComparisonChart.vue index a7445e92ba..55d46b5c6a 100644 --- a/dashboard/src/components/metrics/multipleProjects/TrainingProfileComparisonChart.vue +++ b/dashboard/src/components/metrics/multipleProjects/TrainingProfileComparisonChart.vue @@ -17,6 +17,8 @@ limitations under the License. import { ref, onMounted, watch } from 'vue'; import NumberFormatter from "@/components/utils/NumberFormatter.js"; import MetricsOverlay from "@/components/metrics/utils/MetricsOverlay.vue"; +import { useSkillsDisplayThemeState } from '@/skills-display/stores/UseSkillsDisplayThemeState.js'; +import { useThemesHelper } from '@/components/header/UseThemesHelper.js'; const props = defineProps({ title: { @@ -41,6 +43,15 @@ const props = defineProps({ }, }); +const themeState = useSkillsDisplayThemeState() +const themeHelper = useThemesHelper() + +const chartAxisColor = () => { + if (themeState.theme.charts.axisLabelColor) { + return themeState.theme.charts.axisLabelColor + } + return themeHelper.isDarkTheme ? 'white' : undefined +} const chartId = ref(props.title.replace(/\s+/g, '')); const chartRef = ref(); @@ -50,8 +61,8 @@ const seriesInternal = ref([]); const options = { chart: { type: 'bar', - height: 350, - toolbar: { + height: 350, + toolbar: { show: false, }, }, @@ -71,15 +82,26 @@ const options = { }, xaxis: { categories: props.labels, + labels: { + style: { + colors: chartAxisColor() + } + } }, yaxis: { min: 0, labels: { + style: { + colors: chartAxisColor() + }, formatter(val) { return typeof val === 'number' ? NumberFormatter.format(val) : val; }, }, }, + tooltip: { + theme: themeHelper.isDarkTheme ? 'dark' : 'light', + } }; onMounted(() => { diff --git a/dashboard/src/components/metrics/skill/UsersByTagChart.vue b/dashboard/src/components/metrics/skill/UsersByTagChart.vue index 88c75116a2..4f9938cb99 100644 --- a/dashboard/src/components/metrics/skill/UsersByTagChart.vue +++ b/dashboard/src/components/metrics/skill/UsersByTagChart.vue @@ -19,10 +19,21 @@ import { useRoute } from 'vue-router'; import MetricsService from "@/components/metrics/MetricsService.js"; import MetricsOverlay from "@/components/metrics/utils/MetricsOverlay.vue"; import NumberFormatter from '@/components/utils/NumberFormatter.js' +import { useSkillsDisplayThemeState } from '@/skills-display/stores/UseSkillsDisplayThemeState.js'; +import { useThemesHelper } from '@/components/header/UseThemesHelper.js'; const route = useRoute(); const props = defineProps(['tag']); +const themeState = useSkillsDisplayThemeState() +const themeHelper = useThemesHelper() + +const chartAxisColor = () => { + if (themeState.theme.charts.axisLabelColor) { + return themeState.theme.charts.axisLabelColor + } + return themeHelper.isDarkTheme ? 'white' : undefined +} const inProgressSeries = ref([]); const achievedSeries = ref([]); @@ -38,6 +49,7 @@ const chartOptions = { }, }, tooltip: { + theme: themeHelper.isDarkTheme ? 'dark' : 'light', y: { formatter(val) { return NumberFormatter.format(val); @@ -62,20 +74,32 @@ const chartOptions = { xaxis: { categories: [], title: { + style: { + color: chartAxisColor() + }, text: '# of Users', }, labels: { style: { fontSize: '13px', fontWeight: 600, + colors: chartAxisColor(), }, }, }, yaxis: { categories: [], title: { + style: { + color: chartAxisColor() + }, text: props.tag.label, }, + labels: { + style: { + colors: chartAxisColor() + } + } }, dataLabels: { enabled: false, diff --git a/dashboard/src/components/utils/services/UsePageVisitService.js b/dashboard/src/components/utils/services/UsePageVisitService.js index 78828795e0..a70fadb479 100644 --- a/dashboard/src/components/utils/services/UsePageVisitService.js +++ b/dashboard/src/components/utils/services/UsePageVisitService.js @@ -20,7 +20,7 @@ import { useAppConfig } from '@/common-components/stores/UseAppConfig.js' export const usePageVisitService = () => { const appConfig = useAppConfig() - const reportPageVisit = (path, fullPath) => { + const reportPageVisit = (path, fullPath, skillDisplay = false, projectId = null) => { if (appConfig.enablePageVisitReporting) { const domain = new URL(window.location) axios.put( @@ -31,7 +31,8 @@ export const usePageVisitService = () => { hostname: domain.hostname, port: domain.port, protocol: domain.protocol, - skillDisplay: false + skillDisplay, + projectId, }, { handleError: false } ) diff --git a/dashboard/src/main.js b/dashboard/src/main.js index 0366fb1edd..e9994e5f28 100644 --- a/dashboard/src/main.js +++ b/dashboard/src/main.js @@ -20,7 +20,7 @@ import { createPinia } from 'pinia' import PrimeVue from 'primevue/config' import ToastService from 'primevue/toastservice' import App from './App.vue' -import router from './router' +import constructRouter from './router' import VueAnnouncer from '@vue-a11y/announcer' import VueApexCharts from 'vue3-apexcharts' import log from 'loglevel' @@ -89,11 +89,12 @@ import '@toast-ui/editor/dist/toastui-editor.css' import 'video.js/dist/video-js.css' -log.setLevel('warn') +log.setLevel('info') const pinia = createPinia() const app = createApp(App) +const router = constructRouter() app.use(router) app.use(pinia) diff --git a/dashboard/src/router/SkillsClientPath.js b/dashboard/src/router/SkillsClientPath.js new file mode 100644 index 0000000000..6639105409 --- /dev/null +++ b/dashboard/src/router/SkillsClientPath.js @@ -0,0 +1,32 @@ +/* + * Copyright 2024 SkillTree + * + * 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 + * + * https://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. + */ + +const rootUrl = '/static/clientPortal/' +export default { + RootUrl: rootUrl, + HomePath: `${rootUrl}index.html`, + TestSkillsClientPath: '/test-skills-client/', + + isSkillsClientIframePath(){ + const path = window?.location?.pathname + return path?.startsWith(this.RootUrl) + }, + + isSkillsClientPath(){ + const path = window?.location?.pathname + return this.isSkillsClientIframePath() //|| path?.startsWith(this.TestSkillsClientPath) + } +} \ No newline at end of file diff --git a/dashboard/src/router/SkillsDisplayChildRoutes.js b/dashboard/src/router/SkillsDisplayChildRoutes.js index f28e57b6fa..f7ce49346e 100644 --- a/dashboard/src/router/SkillsDisplayChildRoutes.js +++ b/dashboard/src/router/SkillsDisplayChildRoutes.js @@ -22,7 +22,7 @@ import BadgesDetailsPage from '@/skills-display/components/badges/BadgesDetailsP import BadgeDetailsPage from '@/skills-display/components/badges/BadgeDetailsPage.vue' import QuizPage from '@/skills-display/components/quiz/QuizPage.vue' -const createSkillsDisplayChildRoutes = (appendToName) => { +const createSkillsDisplayChildRoutes = (appendToName, startWithSlash = false) => { const projectPlaceholder = '##PROJECT##' const projectPlaceholderRegex = new RegExp(projectPlaceholder, 'g') @@ -33,10 +33,10 @@ const createSkillsDisplayChildRoutes = (appendToName) => { const skillPlaceholder = '##SKILL##' const skillPlaceholderRegex = new RegExp(skillPlaceholder, 'g') - + const prependToPath = startWithSlash ? '/' : '' return [{ name: `SkillsDisplay${appendToName}`, - path: '', + path: `${prependToPath}`, component: SkillsDisplay, meta: { requiresAuth: true, @@ -46,7 +46,7 @@ const createSkillsDisplayChildRoutes = (appendToName) => { } } }, { - path: 'rank', + path: `${prependToPath}rank`, component: MyRankDetailsPage, name: `myRankDetails${appendToName}`, meta: { @@ -57,7 +57,7 @@ const createSkillsDisplayChildRoutes = (appendToName) => { } } }, { - path: 'subjects/:subjectId/rank', + path: `${prependToPath}subjects/:subjectId/rank`, component: MyRankDetailsPage, name: `subjectRankDetails${appendToName}`, props: true, @@ -66,7 +66,7 @@ const createSkillsDisplayChildRoutes = (appendToName) => { } }, { name: `BadgesDetailsPage${appendToName}`, - path: 'badges', + path: `${prependToPath}badges`, component: BadgesDetailsPage, meta: { requiresAuth: true, @@ -76,7 +76,7 @@ const createSkillsDisplayChildRoutes = (appendToName) => { } } }, { - path: 'badges/:badgeId', + path: `${prependToPath}badges/:badgeId`, component: BadgeDetailsPage, name: `badgeDetails${appendToName}`, props: true, @@ -84,7 +84,7 @@ const createSkillsDisplayChildRoutes = (appendToName) => { title: 'Badge Details' } }, { - path: 'badges/global/:badgeId', + path: `${prependToPath}badges/global/:badgeId`, component: BadgeDetailsPage, name: `globalBadgeDetails${appendToName}`, props: true, @@ -92,21 +92,21 @@ const createSkillsDisplayChildRoutes = (appendToName) => { title: 'Global Badge Details' } }, { - path: 'badges/global/:badgeId/skills/:skillId', + path: `${prependToPath}badges/global/:badgeId/skills/:skillId`, component: SkillPage, name: `globalBadgeSkillDetails${appendToName}`, meta: { title: `Global Badge ${skillPlaceholder} Details` } }, { - path: 'badges/:badgeId/skills/:skillId', + path: `${prependToPath}badges/:badgeId/skills/:skillId`, component: SkillPage, name: `badgeSkillDetails${appendToName}`, meta: { title: `Badge ${skillPlaceholder} Details` } }, { - path: 'badges/:badgeId/crossProject/:crossProjectId/:dependentSkillId', + path: `${prependToPath}badges/:badgeId/crossProject/:crossProjectId/:dependentSkillId`, component: SkillPage, name: `crossProjectSkillDetailsUnderBadge${appendToName}`, meta: { @@ -114,7 +114,7 @@ const createSkillsDisplayChildRoutes = (appendToName) => { } }, { name: `SubjectDetailsPage${appendToName}`, - path: 'subjects/:subjectId', + path: `${prependToPath}subjects/:subjectId`, component: SubjectDetailsPage, meta: { requiresAuth: true, @@ -124,14 +124,14 @@ const createSkillsDisplayChildRoutes = (appendToName) => { } } }, { - path: 'subjects/:subjectId/skills/:skillId', + path: `${prependToPath}subjects/:subjectId/skills/:skillId`, component: SkillPage, name: `skillDetails${appendToName}`, meta: { title: `${skillPlaceholder} Details` } }, { - path: 'subjects/:subjectId/skills/:skillId/crossProject/:crossProjectId/:dependentSkillId', + path: `${prependToPath}subjects/:subjectId/skills/:skillId/crossProject/:crossProjectId/:dependentSkillId`, component: SkillPage, name: `crossProjectSkillDetails${appendToName}`, meta: { @@ -139,7 +139,7 @@ const createSkillsDisplayChildRoutes = (appendToName) => { } }, { name: `quizPage${appendToName}`, - path: 'subjects/:subjectId/skills/:skillId/quizzes/:quizId', + path: `${prependToPath}subjects/:subjectId/skills/:skillId/quizzes/:quizId`, component: QuizPage, meta: { requiresAuth: true, @@ -150,7 +150,7 @@ const createSkillsDisplayChildRoutes = (appendToName) => { } }, { name: `SkillsDisplayErrorPage${appendToName}`, - path: 'error', + path: `${prependToPath}error`, component: SkillsDisplayErrorPage, meta: { requiresAuth: false, diff --git a/dashboard/src/router/SkillsDisplaySkillsClientRoutes.js b/dashboard/src/router/SkillsDisplaySkillsClientRoutes.js index 9442e4d57a..014ca6f98e 100644 --- a/dashboard/src/router/SkillsDisplaySkillsClientRoutes.js +++ b/dashboard/src/router/SkillsDisplaySkillsClientRoutes.js @@ -14,10 +14,11 @@ * limitations under the License. */ import SkillsDisplayInIframe from '@/skills-display/SkillsDisplayInIframe.vue' +import SkillsClientPath from '@/router/SkillsClientPath.js' const createSkillsClientRoutes = (skillsDisplayChildRoutes) => { return { - path: '/static/clientPortal/index.html', + path: SkillsClientPath.HomePath, component: SkillsDisplayInIframe, name: 'SkillsDisplayInIframe', meta: { diff --git a/dashboard/src/router/UseGlobalNavGuards.js b/dashboard/src/router/UseGlobalNavGuards.js index 240adc02c0..9b0c2dac35 100644 --- a/dashboard/src/router/UseGlobalNavGuards.js +++ b/dashboard/src/router/UseGlobalNavGuards.js @@ -26,10 +26,9 @@ import { useProjConfig } from '@/stores/UseProjConfig.js' import IconManagerService from '@/components/utils/iconPicker/IconManagerService.js' import { useAccessState } from '@/stores/UseAccessState.js' import { useInviteOnlyProjectState } from '@/stores/UseInviteOnlyProjectState.js' -import { useSkillsDisplayParentFrameState } from '@/skills-display/stores/UseSkillsDisplayParentFrameState.js' import { useLog } from '@/components/utils/misc/useLog.js' -import { useSkillsDisplayInfo } from '@/skills-display/UseSkillsDisplayInfo.js' import { useSkillsDisplayAttributesState } from '@/skills-display/stores/UseSkillsDisplayAttributesState.js' +import PathAppendValues from '@/router/SkillsDisplayPathAppendValues.js' export const useGlobalNavGuards = () => { @@ -44,8 +43,6 @@ export const useGlobalNavGuards = () => { const projConfig = useProjConfig() const router = useRouter() const route = useRoute() - const skillDisplayParentFrameState = useSkillsDisplayParentFrameState() - const skillsDisplayInfo = useSkillsDisplayInfo() const skillsDisplayAttributes = useSkillsDisplayAttributesState() const log = useLog() @@ -128,7 +125,7 @@ export const useGlobalNavGuards = () => { } next(newRoute) } else { - if(skillsClientDisplayPath) { + if((to.name?.endsWith(PathAppendValues.Local) || to.name?.endsWith(PathAppendValues.Inception)) && skillsClientDisplayPath) { const newRoute = to.path + skillsClientDisplayPath; const nextRoute = '/redirect?nextPage=' + newRoute next(nextRoute) @@ -189,20 +186,6 @@ export const useGlobalNavGuards = () => { }) }, 150) } - if (skillDisplayParentFrameState.parentFrame) { - const params = { - path: skillsDisplayInfo.cleanPath(to.path), - fullPath: skillsDisplayInfo.cleanPath(to.fullPath), - name: to.name, - query: to.query, - currentLocation: window.location.toString(), - historySize: window.history.length, - }; - if (log.isDebugEnabled()) { - log.debug(`emit route-change event to parent frame with ${JSON.stringify(params)}`) - } - skillDisplayParentFrameState.parentFrame.emit('route-changed', params); - } }) // this is handled in beforeEachNavGuard diff --git a/dashboard/src/router/index.js b/dashboard/src/router/index.js index 7ce7dcf948..5f6a5ee21e 100644 --- a/dashboard/src/router/index.js +++ b/dashboard/src/router/index.js @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createRouter, createWebHistory } from 'vue-router' +import { createMemoryHistory, createRouter, createWebHistory } from 'vue-router' import Login from '@/components/access/Login.vue' import MyProgress from '@/components/myProgress/MyProgress.vue' import createAdminRoutes from './AdminRoutes.js' @@ -86,6 +86,8 @@ import EmailVerifiedConfirmation from "@/components/access/EmailVerifiedConfirma import RequestEmailVerification from "@/components/access/RequestEmailVerification.vue"; import RedirectPage from "@/components/utils/RedirectPage.vue"; import UpgradeInProgressPage from '@/components/utils/errors/UpgradeInProgressPage.vue' +import SkillsClientPath from '@/router/SkillsClientPath.js' +import log from 'loglevel' const routes = [ { @@ -893,9 +895,22 @@ routes.push({ children: createSkillsDisplayChildRoutes(PathAppendValues.LocalTest) }) -const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), - routes -}) +const isSkillsClient = SkillsClientPath.isSkillsClientIframePath() +const history = isSkillsClient ? createMemoryHistory(import.meta.env.BASE_URL) : createWebHistory(import.meta.env.BASE_URL) +const actualRoutes = isSkillsClient ? [createSkillsClientRoutes(createSkillsDisplayChildRoutes(PathAppendValues.SkillsClient, true))] : routes +const constructRouter = () => { + const router = createRouter({ + history, + routes: actualRoutes + }) + + if (isSkillsClient) { + router.push('/') + } + + log.trace(`Constructed router for path [${window?.location?.pathname}] isSkillsClient: [${isSkillsClient}]`) + + return router +} -export default router +export default constructRouter diff --git a/dashboard/src/skills-display/SkillsClientUtil.js b/dashboard/src/skills-display/SkillsClientUtil.js new file mode 100644 index 0000000000..9f5ac9b000 --- /dev/null +++ b/dashboard/src/skills-display/SkillsClientUtil.js @@ -0,0 +1,24 @@ +/* + * Copyright 2024 SkillTree + * + * 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 + * + * https://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. + */ +import SkillsClientPath from '@/router/SkillsClientPath.js' + +export default { + + isSkillsClientPath(){ + const path = window?.location?.pathname + return path?.startsWith(SkillsClientPath.RootUrl) || path?.startsWith('/test-skills-client/') + } +} \ No newline at end of file diff --git a/dashboard/src/skills-display/SkillsDisplayInIframe.vue b/dashboard/src/skills-display/SkillsDisplayInIframe.vue index 55bcd55cdb..1a7e2792c6 100644 --- a/dashboard/src/skills-display/SkillsDisplayInIframe.vue +++ b/dashboard/src/skills-display/SkillsDisplayInIframe.vue @@ -16,8 +16,52 @@ limitations under the License.