HEX
Server: Microsoft-IIS/10.0
System: Windows NT ITPWINWEBSVR22 10.0 build 20348 (Windows Server 2022) AMD64
User: www.conferencesearch.co.uk (0)
PHP: 8.3.30
Disabled: NONE
Upload Files
File: D:/web/matomo.vdk/plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.vue
<!--
  Matomo - free/libre analytics platform

  @link    https://matomo.org
  @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
-->

<template>
  <div class="reportingMenu">
    <ul
      class="navbar hide-on-med-and-down collapsible"
      role="menu"
      :aria-label="translate('CoreHome_MainNavigation')"
    >
      <li
        class="menuTab"
        role="menuitem"
        v-for="category in menu"
        :class="{ 'active': category.id === activeCategory }"
        :key="category.id"
        :data-category-id="category.id"
      >
        <component
          v-if="category.component"
          :is="category.component"
          @action="loadCategory(category)"
        ></component>
        <a
          v-if="!category.component"
          class="item"
          tabindex="5"
          href=""
          @click.prevent="loadCategory(category)"
        >
          <span
            :class="`menu-icon ${category.icon ? category.icon :
              (category.subcategories && category.id === activeCategory ?
                'icon-chevron-down' : 'icon-chevron-right')}`"
          />{{ category.name }}
          <span class="hidden">
            {{ translate('CoreHome_Menu') }}
          </span>
        </a>
        <ul v-if="!category.component" role="menu">
          <li
            role="menuitem"
            :class="{
              'active': (subcategory.id === displayedSubcategory
                || (subcategory.isGroup && activeSubsubcategory === displayedSubcategory)
              ) && category.id === displayedCategory,
            }"
            v-for="subcategory in category.subcategories"
            :key="subcategory.id"
          >
            <MenuItemsDropdown
              v-if="subcategory.isGroup"
              :show-search="true"
              :menu-title="htmlEntities(subcategory.name)"
            >
              <a
                class="item"
                tabindex="5"
                :class="{
                  active: subcat.id === activeSubsubcategory
                    && subcategory.id === displayedSubcategory
                    && category.id === displayedCategory,
                }"
                :href="`#?${makeUrl(category, subcat)}`"
                @click="loadSubcategory(category, subcat, $event)"
                v-for="subcat in subcategory.subcategories"
                :title="subcat.tooltip"
                :key="subcat.id"
              >
                {{ subcat.name }}
              </a>
            </MenuItemsDropdown>
            <a
              v-if="!subcategory.isGroup"
              :href="`#?${makeUrl(category, subcategory)}`"
              class="item"
              @click="loadSubcategory(category, subcategory, $event)"
              tabindex="5"
            >
              {{ subcategory.name }}
            </a>
            <a
              class="item-help-icon"
              tabindex="5"
              href="javascript:"
              v-if="subcategory.help"
              @click="showHelp(category, subcategory, $event)"
              :class="{active: helpShownCategory
                && helpShownCategory.subcategory === subcategory.id
                && helpShownCategory.category === category.id
                && subcategory.help}"
            >
              <span class="icon-help" />
            </a>
          </li>
        </ul>
      </li>
    </ul>
    <ul
      id="mobile-left-menu"
      class="sidenav sidenav--reporting-menu-mobile hide-on-large-only"
    >
      <li
        class="no-padding"
        v-for="category in menu"
        :key="category.id"
        :data-category-id="category.id"
      >
        <component
          v-if="category.component"
          :is="category.component"
          @action="loadCategory(category)"
        ></component>
        <ul
          v-if="!category.component"
          class="collapsible collapsible-accordion"
          v-side-nav="{ activator: sideNavActivator }"
        >
          <li>
            <a class="collapsible-header">
              <i :class="category.icon ? category.icon : 'icon-chevron-down'" />{{ category.name }}
            </a>
            <div class="collapsible-body">
              <ul>
                <li v-for="subcategory in category.subcategories" :key="subcategory.id">
                  <template v-if="subcategory.isGroup">
                    <a
                      @click="loadSubcategory(category, subcat)"
                      :href="`#?${makeUrl(category, subcat)}`"
                      v-for="subcat in subcategory.subcategories"
                      :key="subcat.id"
                    >
                      {{ subcat.name }}
                    </a>
                  </template>
                  <template v-if="!subcategory.isGroup">
                    <a
                      @click="loadSubcategory(category, subcategory)"
                      :href="`#?${makeUrl(category, subcategory)}`"
                    >
                      {{ subcategory.name }}
                    </a>
                  </template>
                </li>
              </ul>
            </div>
          </li>
        </ul>
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { defineComponent, watch } from 'vue';
import MenuItemsDropdown from '../MenuItemsDropdown/MenuItemsDropdown.vue';
import SideNav from '../SideNav/SideNav';
import { NotificationsStore } from '../Notification';
import MatomoUrl from '../MatomoUrl/MatomoUrl';
import ReportingMenuStoreInstance from './ReportingMenu.store';
import Matomo from '../Matomo/Matomo';
import { translate } from '../translate';
import WidgetsStoreInstance from '../Widget/Widgets.store';
import { Category, CategoryContainer } from './Category';
import { Subcategory, SubcategoryContainer } from './Subcategory';
import useExternalPluginComponent from '../useExternalPluginComponent';

const REPORTING_HELP_NOTIFICATION_ID = 'reportingmenu-help';

interface ReportingMenuState {
  showSubcategoryHelpOnLoad: { category: Category, subcategory: Subcategory } | null;
  initialLoad: boolean | null;
  helpShownCategory: { category: string, subcategory: string } | null;
}

export default defineComponent({
  components: {
    MenuItemsDropdown,
  },
  directives: {
    SideNav,
  },
  props: {},
  data(): ReportingMenuState {
    return {
      showSubcategoryHelpOnLoad: null,
      initialLoad: true,
      helpShownCategory: null,
    };
  },
  computed: {
    sideNavActivator() {
      return document.querySelector('nav .activateLeftMenu');
    },
    menu() {
      const categories = ReportingMenuStoreInstance.menu.value;

      categories.forEach((category) => {
        if (category.widget && category.widget.indexOf('.') > 0) {
          const [widgetPlugin, widgetComponent] = category.widget.split('.');
          category.component = useExternalPluginComponent(widgetPlugin, widgetComponent);
        }
      });

      return categories;
    },
    activeCategory() {
      return ReportingMenuStoreInstance.activeCategory.value;
    },
    activeSubcategory() {
      return ReportingMenuStoreInstance.activeSubcategory.value;
    },
    activeSubsubcategory() {
      return ReportingMenuStoreInstance.activeSubsubcategory.value;
    },
    displayedCategory() {
      return MatomoUrl.parsed.value.category;
    },
    displayedSubcategory() {
      return MatomoUrl.parsed.value.subcategory;
    },
  },
  created() {
    ReportingMenuStoreInstance.fetchMenuItems().then((menu) => {
      if (!MatomoUrl.parsed.value.subcategory) {
        const categoryToLoad = menu[0];
        const subcategoryToLoad = (categoryToLoad as CategoryContainer).subcategories[0];

        // load first, initial page if no subcategory is present
        ReportingMenuStoreInstance.enterSubcategory(categoryToLoad, subcategoryToLoad);
        this.propagateUrlChange(categoryToLoad, subcategoryToLoad);
      }
    });

    watch(() => MatomoUrl.parsed.value, (query) => {
      const found = ReportingMenuStoreInstance.findSubcategory(
        query.category as string,
        query.subcategory as string,
      );

      ReportingMenuStoreInstance.enterSubcategory(
        found.category,
        found.subcategory,
        found.subsubcategory,
      );
    });

    Matomo.on('matomoPageChange', () => {
      if (!this.initialLoad) {
        window.globalAjaxQueue.abort();
      }

      this.helpShownCategory = null;

      if (this.showSubcategoryHelpOnLoad) {
        this.showHelp(
          this.showSubcategoryHelpOnLoad.category,
          this.showSubcategoryHelpOnLoad.subcategory,
        );
        this.showSubcategoryHelpOnLoad = null;
      }

      window.$('#loadingError,#loadingRateLimitError').hide();

      this.initialLoad = false;
    });

    Matomo.on('updateReportingMenu', () => {
      ReportingMenuStoreInstance.reloadMenuItems().then(() => {
        const category = MatomoUrl.parsed.value.category as string;
        const subcategory = MatomoUrl.parsed.value.subcategory as string;

        // we need to make sure to select same categories again
        if (category && subcategory) {
          const found = ReportingMenuStoreInstance.findSubcategory(category, subcategory);
          if (found.category) {
            ReportingMenuStoreInstance.enterSubcategory(
              found.category,
              found.subcategory,
              found.subsubcategory,
            );
          }
        }
      });

      WidgetsStoreInstance.reloadAvailableWidgets();
    });
  },
  methods: {
    propagateUrlChange(category: Category, subcategory: Subcategory) {
      const queryParams = MatomoUrl.parsed.value;
      if (queryParams.category === category.id && queryParams.subcategory === subcategory.id) {
        // we need to manually trigger change as URL would not change and therefore page would not
        // be reloaded
        this.loadSubcategory(category, subcategory);
      } else {
        MatomoUrl.updateHash({
          ...MatomoUrl.hashParsed.value,
          category: category.id,
          subcategory: subcategory.id,
        });
      }
    },
    loadCategory(category: Category) {
      NotificationsStore.remove(REPORTING_HELP_NOTIFICATION_ID);

      const isActive = ReportingMenuStoreInstance.toggleCategory(category);

      // one subcategory or a widget and some subcategories to allow to load the category
      const { subcategories } = category as SubcategoryContainer;
      const categoryCanLoad = (subcategories && subcategories.length === 1)
        || (category.widget && subcategories && subcategories.length);

      if (isActive && categoryCanLoad) {
        this.helpShownCategory = null;

        const subcategory = (category as SubcategoryContainer).subcategories[0];
        this.propagateUrlChange(category, subcategory);
      }
    },
    loadSubcategory(category: Category, subcategory: Subcategory, event?: MouseEvent) {
      if (event
        && (event.shiftKey || event.ctrlKey || event.metaKey)
      ) {
        return;
      }

      NotificationsStore.remove(REPORTING_HELP_NOTIFICATION_ID);

      if (subcategory
        && subcategory.id === MatomoUrl.parsed.value.subcategory
        && category.id === MatomoUrl.parsed.value.category
      ) {
        this.helpShownCategory = null;

        // this menu item is already active, a location change success would not be triggered,
        // instead trigger an event (after the URL changes)
        setTimeout(() => {
          Matomo.postEvent('loadPage', category.id, subcategory.id);
        });
      }
    },
    makeUrl(category: Category, subcategory: Subcategory) {
      const {
        idSite,
        period,
        date,
        segment,
        comparePeriods,
        compareDates,
        compareSegments,
      } = MatomoUrl.parsed.value;

      return MatomoUrl.stringify({
        idSite,
        period,
        date,
        segment,
        comparePeriods,
        compareDates,
        compareSegments,
        category: category.id,
        subcategory: subcategory.id,
      });
    },
    htmlEntities(v: string) {
      return Matomo.helper.htmlEntities(v);
    },
    showHelp(category: Category, subcategory: Subcategory, event?: Event) {
      const parsedUrl = MatomoUrl.parsed.value;
      const currentCategory = parsedUrl.category;
      const currentSubcategory = parsedUrl.subcategory;

      if ((currentCategory !== category.id
        || currentSubcategory !== subcategory.id)
        && event
      ) {
        this.showSubcategoryHelpOnLoad = { category, subcategory };
        MatomoUrl.updateHash({
          ...MatomoUrl.hashParsed.value,
          category: category.id,
          subcategory: subcategory.id,
        });
        return;
      }

      if (this.helpShownCategory
        && category.id === this.helpShownCategory.category
        && subcategory.id === this.helpShownCategory.subcategory
      ) {
        NotificationsStore.remove(REPORTING_HELP_NOTIFICATION_ID);
        this.helpShownCategory = null;
        return;
      }

      const prefixText = translate('CoreHome_ReportingCategoryHelpPrefix',
        category.name, subcategory.name);
      const prefix = `<strong>${prefixText}</strong><br/>`;

      NotificationsStore.show({
        context: 'info',
        id: REPORTING_HELP_NOTIFICATION_ID,
        type: 'help',
        noclear: true,
        class: 'help-notification',
        message: prefix + subcategory.help,
        placeat: '#notificationContainer',
        prepend: true,
      });

      this.helpShownCategory = {
        category: category.id,
        subcategory: subcategory.id,
      };
    },
  },
});
</script>