81df955845
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
418 lines
11 KiB
Vue
418 lines
11 KiB
Vue
<template>
|
|
<nav id="nav_bar" class="bg-neutral-700">
|
|
<div
|
|
class="mx-auto px-4"
|
|
:class="[showNavbarBreadcrumb ? 'min-h-12' : 'h-12']"
|
|
>
|
|
<div
|
|
class="flex justify-between items-center flex-wrap relative"
|
|
v-show="showNavbarBreadcrumb"
|
|
>
|
|
<div id="nav_bar_logged_in" class="flex flex-1 items-center">
|
|
<!-- Back to Files page -->
|
|
<router-link to="/files" class="mr-4" v-if="showIcon" id="backPage">
|
|
<span
|
|
class="material-symbols-outlined text-neutral-10 !leading-loose"
|
|
>
|
|
arrow_back
|
|
</span>
|
|
</router-link>
|
|
<div>
|
|
<h2
|
|
v-if="navViewName !== 'UPLOAD'"
|
|
class="mr-14 py-3 text-2xl font-black text-neutral-10"
|
|
>
|
|
{{ navViewName }}
|
|
</h2>
|
|
<h2 v-else class="mr-14 py-3 text-2xl font-black text-neutral-10">
|
|
FILES
|
|
</h2>
|
|
</div>
|
|
<ul
|
|
class="flex justify-center items-center space-x-4 text-xl font-semibold text-neutral-300 cursor-pointer"
|
|
>
|
|
<li
|
|
@click="onNavItemBtnClick($event, item)"
|
|
v-for="(item, index) in navViewData[navViewName]"
|
|
:key="index"
|
|
class="nav-item"
|
|
:class="{ active: activePage === item }"
|
|
>
|
|
{{ item }}
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<!-- Files Page: Search and Upload -->
|
|
<div
|
|
class="flex justify-end items-center"
|
|
v-if="navViewName === 'FILES'"
|
|
>
|
|
<div
|
|
id="import_btn"
|
|
class="btn btn-sm btn-neutral cursor-pointer"
|
|
@click="uploadModal = true"
|
|
>
|
|
Import
|
|
<UploadModal
|
|
:visible="uploadModal"
|
|
@closeModal="uploadModal = $event"
|
|
></UploadModal>
|
|
</div>
|
|
</div>
|
|
<!-- Upload, Performance, Compare have no button actions -->
|
|
<div v-else-if="noShowSaveButton"></div>
|
|
<!-- Other Page: Save and Download -->
|
|
<!-- Save: if data exists, prompt rename; if no data, prompt save; if unchanged, do nothing -->
|
|
<div v-else class="space-x-4">
|
|
<button
|
|
class="btn btn-sm"
|
|
:class="[disabledSave ? 'btn-disable' : 'btn-neutral']"
|
|
:disabled="disabledSave"
|
|
@click="saveModal"
|
|
>
|
|
Save
|
|
</button>
|
|
</div>
|
|
<AcctMenu v-if="showNavbarBreadcrumb" />
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</template>
|
|
<script setup>
|
|
// The Lucia project.
|
|
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
|
// Authors:
|
|
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
|
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
|
// imacat.yang@dsp.im (imacat), 2023/9/23
|
|
/**
|
|
* @module components/Navbar Navigation bar with breadcrumb-style
|
|
* page tabs, import button for Files, and save button for
|
|
* Map/Conformance pages.
|
|
*/
|
|
|
|
import { ref, computed, watch, onMounted } from "vue";
|
|
import { useRoute, useRouter } from "vue-router";
|
|
import { storeToRefs } from "pinia";
|
|
import emitter from "@/utils/emitter";
|
|
import { useFilesStore } from "@/stores/files";
|
|
import { useAllMapDataStore } from "@/stores/allMapData";
|
|
import { useConformanceStore } from "@/stores/conformance";
|
|
import { usePageAdminStore } from "@/stores/pageAdmin";
|
|
import { useMapCompareStore } from "@/stores/mapCompareStore";
|
|
import {
|
|
saveFilter,
|
|
savedSuccessfully,
|
|
saveConformance,
|
|
} from "@/module/alertModal.js";
|
|
import UploadModal from "./File/UploadModal.vue";
|
|
import AcctMenu from "./AccountMenu/AcctMenu.vue";
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
|
|
const store = useFilesStore();
|
|
const allMapDataStore = useAllMapDataStore();
|
|
const conformanceStore = useConformanceStore();
|
|
const mapCompareStore = useMapCompareStore();
|
|
const pageAdminStore = usePageAdminStore();
|
|
|
|
const {
|
|
logId,
|
|
tempFilterId,
|
|
createFilterId,
|
|
filterName,
|
|
postRuleData,
|
|
isUpdateFilter,
|
|
} = storeToRefs(allMapDataStore);
|
|
const {
|
|
conformanceRuleData,
|
|
conformanceLogId,
|
|
conformanceFilterId,
|
|
conformanceLogTempCheckId,
|
|
conformanceFilterTempCheckId,
|
|
conformanceLogCreateCheckId,
|
|
conformanceFilterCreateCheckId,
|
|
isUpdateConformance,
|
|
conformanceFileName,
|
|
} = storeToRefs(conformanceStore);
|
|
const {
|
|
activePage,
|
|
pendingActivePage,
|
|
activePageComputedByRoute,
|
|
shouldKeepPreviousPage,
|
|
} = storeToRefs(pageAdminStore);
|
|
const {
|
|
setPendingActivePage,
|
|
setPreviousPage,
|
|
setActivePage,
|
|
setActivePageComputedByRoute,
|
|
setIsPagePendingBoolean,
|
|
} = pageAdminStore;
|
|
|
|
const showNavbarBreadcrumb = ref(false);
|
|
const navViewData = {
|
|
// e.g. FILES: ['ALL', 'DISCOVER', 'COMPARE', 'DESIGN', 'SIMULATION'],
|
|
FILES: ["ALL", "DISCOVER", "COMPARE"],
|
|
// e.g. DISCOVER: ['MAP', 'CONFORMANCE', 'PERFORMANCE', 'DATA']
|
|
DISCOVER: ["MAP", "CONFORMANCE", "PERFORMANCE"],
|
|
// e.g. COMPARE: ['PROCESS MAP', 'DASHBOARD']
|
|
COMPARE: ["MAP", "PERFORMANCE"],
|
|
"ACCOUNT MANAGEMENT": [],
|
|
"MY ACCOUNT": [],
|
|
};
|
|
const navViewName = ref("FILES");
|
|
const uploadModal = ref(false);
|
|
|
|
const disabledSave = computed(() => {
|
|
switch (route.name) {
|
|
case "Map":
|
|
case "CheckMap":
|
|
// Cannot save without a filter ID or a temporary tempFilterId
|
|
return !tempFilterId.value;
|
|
case "Conformance":
|
|
case "CheckConformance":
|
|
return !(
|
|
conformanceFilterTempCheckId.value || conformanceLogTempCheckId.value
|
|
);
|
|
default:
|
|
return true;
|
|
}
|
|
});
|
|
|
|
const showIcon = computed(() => {
|
|
return !["FILES", "UPLOAD"].includes(navViewName.value);
|
|
});
|
|
|
|
const noShowSaveButton = computed(() => {
|
|
return (
|
|
navViewName.value === "UPLOAD" ||
|
|
navViewName.value === "COMPARE" ||
|
|
navViewName.value === "ACCOUNT MANAGEMENT" ||
|
|
activePage.value === "PERFORMANCE"
|
|
);
|
|
});
|
|
|
|
watch(
|
|
() => route,
|
|
() => {
|
|
getNavViewName();
|
|
},
|
|
{ deep: true },
|
|
);
|
|
|
|
/**
|
|
* Handles navigation when a navbar tab is clicked.
|
|
* @param {Event} event - The click event from the nav item.
|
|
*/
|
|
function onNavItemBtnClick(event) {
|
|
let type;
|
|
let fileId;
|
|
let isCheckPage;
|
|
const navItemCandidate = event.target.innerText;
|
|
|
|
setPendingActivePage(navItemCandidate);
|
|
|
|
switch (navViewName.value) {
|
|
case "FILES":
|
|
store.filesTag = navItemCandidate;
|
|
break;
|
|
case "DISCOVER": {
|
|
type = route.params.type;
|
|
fileId = route.params.fileId;
|
|
isCheckPage = route.name.includes("Check");
|
|
const discoverRoutes = {
|
|
MAP: "Map",
|
|
CONFORMANCE: "Conformance",
|
|
PERFORMANCE: "Performance",
|
|
};
|
|
const baseName = discoverRoutes[navItemCandidate];
|
|
if (baseName) {
|
|
const routeName = isCheckPage ? `Check${baseName}` : baseName;
|
|
router.push({
|
|
name: routeName,
|
|
params: { type: type, fileId: fileId },
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
case "COMPARE":
|
|
switch (navItemCandidate) {
|
|
case "MAP":
|
|
router.push({
|
|
name: "MapCompare",
|
|
params: mapCompareStore.routeParam,
|
|
});
|
|
break;
|
|
case "PERFORMANCE":
|
|
router.push({
|
|
name: "CompareDashboard",
|
|
params: mapCompareStore.routeParam,
|
|
});
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines the navbar view name and active page from the current route.
|
|
* @returns {string} The navigation item name to highlight.
|
|
*/
|
|
function getNavViewName() {
|
|
const name = route.name;
|
|
let valueToSet;
|
|
|
|
if (route.name === "NotFound404" || !route.matched[1]) {
|
|
return;
|
|
}
|
|
|
|
// route.matched[1] is the second matched route record for the current route
|
|
navViewName.value = route.matched[1].name.toUpperCase();
|
|
store.filesTag = "ALL";
|
|
switch (navViewName.value) {
|
|
case "FILES":
|
|
valueToSet = activePage.value;
|
|
break;
|
|
case "DISCOVER":
|
|
switch (name) {
|
|
case "Map":
|
|
case "CheckMap":
|
|
valueToSet = "MAP";
|
|
break;
|
|
case "Conformance":
|
|
case "CheckConformance":
|
|
valueToSet = "CONFORMANCE";
|
|
break;
|
|
case "Performance":
|
|
case "CheckPerformance":
|
|
valueToSet = "PERFORMANCE";
|
|
break;
|
|
}
|
|
break;
|
|
case "COMPARE":
|
|
switch (name) {
|
|
case "MapCompare":
|
|
valueToSet = "MAP";
|
|
break;
|
|
case "CompareDashboard":
|
|
valueToSet = "PERFORMANCE";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Frontend is not sure which button will the user press on the modal,
|
|
// so here we need to save to a pending state
|
|
// The frontend cannot determine which modal button the user will press
|
|
// (cancel or confirm/save), so we save it to a pending state.
|
|
if (!shouldKeepPreviousPage.value) {
|
|
// If the user did not press cancel
|
|
setPendingActivePage(valueToSet);
|
|
}
|
|
|
|
return valueToSet;
|
|
}
|
|
|
|
/** Opens the save modal for Map or Conformance pages. */
|
|
async function saveModal() {
|
|
// Help determine MAP/CONFORMANCE save with "submit" or "cancel".
|
|
// Notify Map to close the Sidebar.
|
|
emitter.emit("saveModal", false);
|
|
|
|
switch (route.name) {
|
|
case "Map":
|
|
await handleMapSave();
|
|
break;
|
|
case "CheckMap":
|
|
await handleCheckMapSave();
|
|
break;
|
|
case "Conformance":
|
|
case "CheckConformance":
|
|
await handleConformanceSave();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** Sets nav item button background color when the active page is empty. */
|
|
function handleNavItemBtn() {
|
|
if (activePageComputedByRoute.value === "") {
|
|
setActivePageComputedByRoute(route.matched[route.matched.length - 1].name);
|
|
}
|
|
}
|
|
|
|
/** Saves or creates a filter for the Map page. */
|
|
async function handleMapSave() {
|
|
if (createFilterId.value) {
|
|
await allMapDataStore.updateFilter();
|
|
if (isUpdateFilter.value) {
|
|
await savedSuccessfully(filterName.value);
|
|
}
|
|
} else if (logId.value) {
|
|
const isSaved = await saveFilter(allMapDataStore.addFilterId);
|
|
if (isSaved) {
|
|
setActivePage("MAP");
|
|
await router.push(`/discover/filter/${createFilterId.value}/map`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Saves a filter from the Check Map page. */
|
|
async function handleCheckMapSave() {
|
|
const isSaved = await saveFilter(allMapDataStore.addFilterId);
|
|
if (isSaved) {
|
|
setActivePage("MAP");
|
|
await router.push(`/discover/filter/${createFilterId.value}/map`);
|
|
}
|
|
}
|
|
|
|
/** Saves or updates conformance check data. */
|
|
async function handleConformanceSave() {
|
|
if (
|
|
conformanceFilterCreateCheckId.value ||
|
|
conformanceLogCreateCheckId.value
|
|
) {
|
|
await conformanceStore.updateConformance();
|
|
if (isUpdateConformance.value) {
|
|
await savedSuccessfully(conformanceFileName.value);
|
|
}
|
|
} else {
|
|
const isSaved = await saveConformance(
|
|
conformanceStore.addConformanceCreateCheckId,
|
|
);
|
|
if (isSaved) {
|
|
if (conformanceLogId.value) {
|
|
setActivePage("CONFORMANCE");
|
|
await router.push(
|
|
`/discover/conformance/log/${conformanceLogCreateCheckId.value}/conformance`,
|
|
);
|
|
} else if (conformanceFilterId.value) {
|
|
setActivePage("CONFORMANCE");
|
|
await router.push(
|
|
`/discover/conformance/filter/${conformanceFilterCreateCheckId.value}/conformance`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
handleNavItemBtn();
|
|
if (route.params.type === "filter") {
|
|
createFilterId.value = route.params.fileId;
|
|
}
|
|
showNavbarBreadcrumb.value = route.matched[0].name !== "AuthContainer";
|
|
getNavViewName();
|
|
});
|
|
</script>
|
|
<style scoped>
|
|
#searchFiles::-webkit-search-cancel-button {
|
|
appearance: none;
|
|
}
|
|
</style>
|