UNCLASSIFIED - NO CUI

Skip to content
Snippets Groups Projects
Err.vue 3.19 KiB
Newer Older
luke.glasscock's avatar
luke.glasscock committed
<template>
  <div class="error-page">
    <div class="error-page-content">
      <div class="row justify-content-center pt-16">
        <v-img :src="YodaFireLogo" max-width="200"></v-img>
      </div>
      <div class="row justify-content-center">
        <h2 class="white--text error-title">Are you lost, traveler?</h2>
luke.glasscock's avatar
luke.glasscock committed
      </div>
      <div class="row justify-content-center pb-16">
daniel.cushman's avatar
daniel.cushman committed
        <p class="col-md-6 white--text pt-2 mx-2">
luke.glasscock's avatar
luke.glasscock committed
          Please contact your team admin or
          <router-link to="/contact-us">contact us</router-link> for help.
        </p>
      </div>

      <div class="suggestions-container" v-if="pageSuggestions.length > 0">
        <h3>Some of these related links might get you back on track:</h3>
        <div class="d-flex flex-column pa-4">
          <a
            v-for="(result, idx) in pageSuggestions.slice(0, 5)"
            :key="idx"
            class="py-2"
            :href="result.ref"
          >
            {{ result.title }}
          </a>
        </div>
      </div>
luke.glasscock's avatar
luke.glasscock committed
    </div>
  </div>
</template>

<script>
import YodaFireLogo from "@/assets/images/Yoda_Fire.webp";
import Search from "@/api/search.js";
import { routesByPath } from "@/router/routes.js";
const MAX_SUGGESTIONS = 3;

luke.glasscock's avatar
luke.glasscock committed
export default {
graham.smith's avatar
graham.smith committed
  name: "ErrorComponent",
luke.glasscock's avatar
luke.glasscock committed
  components: {},
  data() {
    return {
      YodaFireLogo,
      pageSuggestions: [],
luke.glasscock's avatar
luke.glasscock committed
    };
  mounted() {
    const searchApi = new Search();
    // parse out the address bar path into tokens
    const pathSearchTokens = decodeURI(this.$route.fullPath)
      .replaceAll("/", " ")
      .trim()
      .split(" ");

    // try increasing edit distances to find potential matches
    for (let editDistance = 0; editDistance < 4; editDistance++) {
      const tokens = pathSearchTokens.map((t) => `${t}~${editDistance}`);
      // run search
      const results = searchApi.search(tokens.join(" "));
      // assemble suggestions
      this.addSuggestions(results);
      if (this.pageSuggestions.length >= MAX_SUGGESTIONS) {
        break;
      }
    }
  },
  methods: {
    /**
     * Adds the search results from `searchResults` to pageSuggestions,
     * without adding duplicates
     */
    addSuggestions(searchResults) {
      // early exit
      if (searchResults.length === 0) {
        return;
      }
      // get current keys (ref)
      const refs = this.pageSuggestions.map((suggestion) => suggestion.ref);
      // add each non-duplicate ref
      for (const searchResult of searchResults) {
        if (!refs[searchResult.ref]) {
          const details = routesByPath[searchResult.ref];

          this.pageSuggestions.push(
            Object.assign(searchResult, {
              title: details.meta?.title,
            })
          );
        }
      }
    },
  },
luke.glasscock's avatar
luke.glasscock committed
};
</script>
<style lang="scss" scoped>
luke.glasscock's avatar
luke.glasscock committed
.error-page {
  height: 100%;
  padding-top: 16px;
  padding-bottom: 16px;

  background-image: url(@/assets/images/tech-bg.webp);
  background-attachment: fixed;
  background-size: cover;
  background-repeat: no-repeat;
  background-color: $bottom-bg;
  .error-title {
    font-size: 48px;
    line-height: 48px;
    text-transform: none;
luke.glasscock's avatar
luke.glasscock committed
  }

  .suggestions-container {
    h3 {
      color: white;
luke.glasscock's avatar
luke.glasscock committed
      text-transform: none;
    }
  }
}
</style>