* Rename vue component for app index - refs BT#20919 * Internal: Allow to use environment variables in JS modules - refs BT#20919 * Allow to use custom templates for vue components - refs BT#20919 * Display: Add custom layout for not logged in page - refs BT#20919pull/5156/head
							parent
							
								
									1de45dc801
								
							
						
					
					
						commit
						be2003ea0d
					
				@ -0,0 +1 @@ | 
				
			||||
export const customVueTemplateEnabled = '1' === ENV_CUSTOM_VUE_TEMPLATE | 
				
			||||
@ -0,0 +1,116 @@ | 
				
			||||
<script setup> | 
				
			||||
import { computed, ref } from "vue" | 
				
			||||
import { useStore } from "vuex" | 
				
			||||
import { useI18n } from "vue-i18n" | 
				
			||||
import InputText from "primevue/inputtext" | 
				
			||||
import Password from "primevue/password" | 
				
			||||
import Button from "primevue/button" | 
				
			||||
import InputSwitch from "primevue/inputswitch" | 
				
			||||
import { useRoute, useRouter } from "vue-router" | 
				
			||||
import { useSecurityStore } from "../../../assets/vue/store/securityStore" | 
				
			||||
 | 
				
			||||
const route = useRoute() | 
				
			||||
const router = useRouter() | 
				
			||||
const store = useStore() | 
				
			||||
const { t } = useI18n() | 
				
			||||
const securityStore = useSecurityStore() | 
				
			||||
 | 
				
			||||
const login = ref("") | 
				
			||||
const password = ref("") | 
				
			||||
const remember = ref(false) | 
				
			||||
 | 
				
			||||
const isLoading = computed(() => store.getters["security/isLoading"]) | 
				
			||||
 | 
				
			||||
async function performLogin() { | 
				
			||||
  let payload = { login: login.value, password: password.value } | 
				
			||||
  let redirect = route.query.redirect | 
				
			||||
 | 
				
			||||
  await store.dispatch("security/login", payload) | 
				
			||||
 | 
				
			||||
  if (!store.getters["security/hasError"]) { | 
				
			||||
    securityStore.user = store.state["security/user"] | 
				
			||||
    const responseData = await store.dispatch("security/login", payload); | 
				
			||||
 | 
				
			||||
    if (typeof redirect !== "undefined") { | 
				
			||||
      await router.push({ path: redirect.toString() }) | 
				
			||||
    } else { | 
				
			||||
      if (responseData.load_terms) { | 
				
			||||
        window.location.href = responseData.redirect; | 
				
			||||
      } else { | 
				
			||||
        window.location.href = "/home" | 
				
			||||
      } | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
<template> | 
				
			||||
  <div class="sidebar__login-form py-3 px-6"> | 
				
			||||
    <form | 
				
			||||
      class="login-section__form p-input-filled" | 
				
			||||
      @submit.prevent="performLogin" | 
				
			||||
    > | 
				
			||||
      <div class="mb-2"> | 
				
			||||
        <InputText | 
				
			||||
          id="login" | 
				
			||||
          v-model="login" | 
				
			||||
          :placeholder="t('Username')" | 
				
			||||
          type="text" | 
				
			||||
        /> | 
				
			||||
      </div> | 
				
			||||
 | 
				
			||||
      <div class="mb-3"> | 
				
			||||
        <Password | 
				
			||||
          v-model="password" | 
				
			||||
          :feedback="false" | 
				
			||||
          :placeholder="t('Password')" | 
				
			||||
          input-id="password" | 
				
			||||
          toggle-mask | 
				
			||||
        /> | 
				
			||||
      </div> | 
				
			||||
 | 
				
			||||
      <div class="mb-3 flex flex-row gap-2"> | 
				
			||||
        <InputSwitch | 
				
			||||
          v-model="remember" | 
				
			||||
          input-id="binary" | 
				
			||||
          name="remember_me" | 
				
			||||
          tabindex="4" | 
				
			||||
        /> | 
				
			||||
        <label | 
				
			||||
          v-t="'Remember me'" | 
				
			||||
          for="binary" | 
				
			||||
        /> | 
				
			||||
      </div> | 
				
			||||
 | 
				
			||||
      <div class="mb-2 flex flex-col gap-2"> | 
				
			||||
        <Button | 
				
			||||
          :label="t('Sign in')" | 
				
			||||
          :loading="isLoading" | 
				
			||||
          type="submit" | 
				
			||||
        /> | 
				
			||||
 | 
				
			||||
        <a | 
				
			||||
          v-t="'Register oneself'" | 
				
			||||
          class="btn btn--primary-outline" | 
				
			||||
          href="/main/auth/inscription.php" | 
				
			||||
          tabindex="3" | 
				
			||||
        /> | 
				
			||||
      </div> | 
				
			||||
 | 
				
			||||
      <a | 
				
			||||
        id="forgot" | 
				
			||||
        v-t="'Forgot your password ?'" | 
				
			||||
        class="field" | 
				
			||||
        href="/main/auth/lostPassword.php" | 
				
			||||
        tabindex="5" | 
				
			||||
      /> | 
				
			||||
    </form> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<style scoped lang="scss"> | 
				
			||||
.p-panelmenu-content .sidebar__login-form { | 
				
			||||
  @apply border-t border-b border-gray-25; | 
				
			||||
  background-color: #f1f1f1; | 
				
			||||
} | 
				
			||||
</style> | 
				
			||||
@ -0,0 +1,24 @@ | 
				
			||||
<script setup> | 
				
			||||
import { useSecurityStore } from "../../../../assets/vue/store/securityStore" | 
				
			||||
import Topbar from "./Topbar.vue" | 
				
			||||
import Sidebar from "../../../../assets/vue/components/layout/Sidebar.vue" | 
				
			||||
import SidebarNotLoggedIn from "./SidebarNotLoggedIn.vue" | 
				
			||||
 | 
				
			||||
const securityStore = useSecurityStore() | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
<template> | 
				
			||||
  <Topbar /> | 
				
			||||
  <Sidebar | 
				
			||||
    v-if="securityStore.isAuthenticated" | 
				
			||||
  /> | 
				
			||||
  <SidebarNotLoggedIn | 
				
			||||
    v-else | 
				
			||||
  /> | 
				
			||||
  <div | 
				
			||||
    class="app-main" | 
				
			||||
  > | 
				
			||||
    <slot /> | 
				
			||||
    <router-view /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
@ -0,0 +1,144 @@ | 
				
			||||
<script setup> | 
				
			||||
import { computed, ref, watch } from "vue" | 
				
			||||
import { useI18n } from "vue-i18n" | 
				
			||||
import { useRoute, useRouter } from "vue-router" | 
				
			||||
import PanelMenu from "primevue/panelmenu" | 
				
			||||
import Dropdown from "primevue/dropdown" | 
				
			||||
import Login from "../../../../assets/vue/components/Login.vue" | 
				
			||||
import SidebarLogin from "../SidebarLogin.vue" | 
				
			||||
 | 
				
			||||
const { t } = useI18n() | 
				
			||||
const router = useRouter() | 
				
			||||
const route = useRoute() | 
				
			||||
 | 
				
			||||
const currentLanguage = window.languages.find((language) => document.querySelector("html").lang === language.isocode) | 
				
			||||
 | 
				
			||||
const selectedCity = ref({ | 
				
			||||
  label: currentLanguage ? currentLanguage.originalName : "English", | 
				
			||||
  isoCode: currentLanguage ? currentLanguage.isocode : "en_US", | 
				
			||||
}) | 
				
			||||
 | 
				
			||||
watch(selectedCity, ({ isoCode }) => { | 
				
			||||
  const newUrl = router.resolve({ | 
				
			||||
    path: route.path, | 
				
			||||
    query: { | 
				
			||||
      _locale: isoCode, | 
				
			||||
    }, | 
				
			||||
  }) | 
				
			||||
 | 
				
			||||
  window.location.href = newUrl.fullPath | 
				
			||||
}) | 
				
			||||
 | 
				
			||||
const languageItems = window.languages.map((language) => ({ | 
				
			||||
  label: language.originalName, | 
				
			||||
  isoCode: language.isocode, | 
				
			||||
})) | 
				
			||||
 | 
				
			||||
const menuItems = computed(() => [ | 
				
			||||
  { | 
				
			||||
    label: t("Home"), | 
				
			||||
    command: async () => await router.push({ name: "Index" }), | 
				
			||||
  }, | 
				
			||||
  { | 
				
			||||
    id: "login-header-item", | 
				
			||||
    label: t("Login"), | 
				
			||||
    items: [ | 
				
			||||
      { | 
				
			||||
        id: 'login-form-item', | 
				
			||||
      } | 
				
			||||
    ], | 
				
			||||
  }, | 
				
			||||
  { | 
				
			||||
    label: t("FAQ"), | 
				
			||||
    command: async () => await router.push({ name: "Faq" }), | 
				
			||||
  }, | 
				
			||||
  { | 
				
			||||
    label: t("Registration"), | 
				
			||||
    url: "/main/auth/inscription.php", | 
				
			||||
  }, | 
				
			||||
  { | 
				
			||||
    label: t("Demo"), | 
				
			||||
    command: async () => await router.push({ name: "Demo" }), | 
				
			||||
  }, | 
				
			||||
  { | 
				
			||||
    label: t("Contact"), | 
				
			||||
    url: "/contact", | 
				
			||||
  }, | 
				
			||||
]) | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
<template> | 
				
			||||
  <aside class="app-sidebar"> | 
				
			||||
    <div class="app-sidebar__container"> | 
				
			||||
      <h3 class="app-sidebar__top"> | 
				
			||||
        {{ t("Menu") }} | 
				
			||||
      </h3> | 
				
			||||
 | 
				
			||||
      <div class="app-sidebar__panel"> | 
				
			||||
        <div class="px-6 my-4"> | 
				
			||||
          <Dropdown | 
				
			||||
            v-model="selectedCity" | 
				
			||||
            :options="languageItems" | 
				
			||||
            option-label="label" | 
				
			||||
          /> | 
				
			||||
        </div> | 
				
			||||
 | 
				
			||||
        <PanelMenu :model="menuItems"> | 
				
			||||
          <template #item="{ item, active }"> | 
				
			||||
            <a | 
				
			||||
              v-if="item.id && 'login-header-item' === item.id" | 
				
			||||
              class="p-panelmenu-header-action" | 
				
			||||
              tabindex="-1" | 
				
			||||
              data-pc-section="headeraction" | 
				
			||||
            > | 
				
			||||
              <svg | 
				
			||||
                width="14" | 
				
			||||
                height="14" | 
				
			||||
                viewBox="0 0 14 14" | 
				
			||||
                fill="none" | 
				
			||||
                xmlns="http://www.w3.org/2000/svg" | 
				
			||||
                class="p-icon p-submenu-icon" | 
				
			||||
                aria-hidden="true" | 
				
			||||
                data-pc-section="submenuicon" | 
				
			||||
              > | 
				
			||||
                <path | 
				
			||||
                  v-if="active" | 
				
			||||
                  d="M7.01744 10.398C6.91269 10.3985 6.8089 10.378 6.71215 10.3379C6.61541 10.2977 6.52766 10.2386 6.45405 10.1641L1.13907 4.84913C1.03306 4.69404 0.985221 4.5065 1.00399 4.31958C1.02276 4.13266 1.10693 3.95838 1.24166 3.82747C1.37639 3.69655 1.55301 3.61742 1.74039 3.60402C1.92777 3.59062 2.11386 3.64382 2.26584 3.75424L7.01744 8.47394L11.769 3.75424C11.9189 3.65709 12.097 3.61306 12.2748 3.62921C12.4527 3.64535 12.6199 3.72073 12.7498 3.84328C12.8797 3.96582 12.9647 4.12842 12.9912 4.30502C13.0177 4.48162 12.9841 4.662 12.8958 4.81724L7.58083 10.1322C7.50996 10.2125 7.42344 10.2775 7.32656 10.3232C7.22968 10.3689 7.12449 10.3944 7.01744 10.398Z" | 
				
			||||
                  fill="currentColor" | 
				
			||||
                /> | 
				
			||||
                <path | 
				
			||||
                  v-else | 
				
			||||
                  d="M4.38708 13C4.28408 13.0005 4.18203 12.9804 4.08691 12.9409C3.99178 12.9014 3.9055 12.8433 3.83313 12.7701C3.68634 12.6231 3.60388 12.4238 3.60388 12.2161C3.60388 12.0084 3.68634 11.8091 3.83313 11.6622L8.50507 6.99022L3.83313 2.31827C3.69467 2.16968 3.61928 1.97313 3.62287 1.77005C3.62645 1.56698 3.70872 1.37322 3.85234 1.22959C3.99596 1.08597 4.18972 1.00371 4.3928 1.00012C4.59588 0.996539 4.79242 1.07192 4.94102 1.21039L10.1669 6.43628C10.3137 6.58325 10.3962 6.78249 10.3962 6.99022C10.3962 7.19795 10.3137 7.39718 10.1669 7.54416L4.94102 12.7701C4.86865 12.8433 4.78237 12.9014 4.68724 12.9409C4.59212 12.9804 4.49007 13.0005 4.38708 13Z" | 
				
			||||
                  fill="currentColor" | 
				
			||||
                /> | 
				
			||||
              </svg> | 
				
			||||
 | 
				
			||||
              <span | 
				
			||||
                class="p-menuitem-text" | 
				
			||||
                data-pc-section="headerlabel" | 
				
			||||
                v-text="item.label" | 
				
			||||
              /> | 
				
			||||
            </a> | 
				
			||||
 | 
				
			||||
            <SidebarLogin | 
				
			||||
              v-else-if="item.id && 'login-form-item' === item.id" | 
				
			||||
            /> | 
				
			||||
 | 
				
			||||
            <a | 
				
			||||
              v-else | 
				
			||||
              class="p-panelmenu-header-action" | 
				
			||||
              tabindex="-1" | 
				
			||||
              data-pc-section="headeraction" | 
				
			||||
            > | 
				
			||||
              <span | 
				
			||||
                class="p-menuitem-text" | 
				
			||||
                data-pc-section="headerlabel" | 
				
			||||
                v-text="item.label" | 
				
			||||
              /> | 
				
			||||
            </a> | 
				
			||||
          </template> | 
				
			||||
        </PanelMenu> | 
				
			||||
      </div> | 
				
			||||
    </div> | 
				
			||||
  </aside> | 
				
			||||
</template> | 
				
			||||
@ -0,0 +1,15 @@ | 
				
			||||
<script setup> | 
				
			||||
import { useSecurityStore } from "../../../../assets/vue/store/securityStore" | 
				
			||||
import TopbarNotLoggedIn from "./TopbarNotLoggedIn.vue" | 
				
			||||
import TopbarLoggedIn from "../../../../assets/vue/components/layout/TopbarLoggedIn.vue" | 
				
			||||
 | 
				
			||||
const securityStore = useSecurityStore() | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
<template> | 
				
			||||
  <TopbarNotLoggedIn v-if="!securityStore.isAuthenticated" /> | 
				
			||||
  <TopbarLoggedIn | 
				
			||||
    v-else | 
				
			||||
    :current-user="securityStore.user" | 
				
			||||
  /> | 
				
			||||
</template> | 
				
			||||
@ -0,0 +1,29 @@ | 
				
			||||
<script setup> | 
				
			||||
import { computed } from "vue" | 
				
			||||
import Menubar from "primevue/menubar" | 
				
			||||
import headerLogoPath from "../../../../assets/css/themes/chamilo/images/header-logo.svg" | 
				
			||||
 | 
				
			||||
const headerLogo = headerLogoPath | 
				
			||||
 | 
				
			||||
const menuItems = computed(() => []) | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
<template> | 
				
			||||
  <div class="app-topbar"> | 
				
			||||
    <Menubar :model="menuItems"> | 
				
			||||
      <template #start> | 
				
			||||
        <img | 
				
			||||
          :src="headerLogo" | 
				
			||||
          alt="Chamilo LMS" | 
				
			||||
        /> | 
				
			||||
      </template> | 
				
			||||
    </Menubar> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<style scoped> | 
				
			||||
.app-topbar .p-menubar { | 
				
			||||
  margin-left: 0; | 
				
			||||
  margin-right: 0; | 
				
			||||
} | 
				
			||||
</style> | 
				
			||||
@ -0,0 +1,39 @@ | 
				
			||||
<script setup> | 
				
			||||
import { ref } from "vue" | 
				
			||||
import { useStore } from "vuex" | 
				
			||||
import { useI18n } from "vue-i18n" | 
				
			||||
import SystemAnnouncementCardList from "../../../assets/vue/components/systemannouncement/SystemAnnouncementCardList.vue" | 
				
			||||
import PageCardList from "../../../assets/vue/components/page/PageCardList.vue" | 
				
			||||
 | 
				
			||||
const store = useStore() | 
				
			||||
const { locale } = useI18n() | 
				
			||||
 | 
				
			||||
const pages = ref([]) | 
				
			||||
 | 
				
			||||
const findAllPages = () => { | 
				
			||||
  pages.value = [] | 
				
			||||
 | 
				
			||||
  store | 
				
			||||
    .dispatch("page/findAll", { | 
				
			||||
      "category.title": "index", | 
				
			||||
      enabled: "1", | 
				
			||||
      locale: locale.value, | 
				
			||||
    }) | 
				
			||||
    .then((response) => (pages.value = response)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
findAllPages() | 
				
			||||
</script> | 
				
			||||
 | 
				
			||||
<template> | 
				
			||||
  <div class="container mx-auto flex flex-columm md:flex-row gap-8"> | 
				
			||||
    <div class="flex-1 md:w-8/12 lg:order-0"> | 
				
			||||
      <SystemAnnouncementCardList /> | 
				
			||||
 | 
				
			||||
      <PageCardList | 
				
			||||
        :pages="pages" | 
				
			||||
        class="grid gap-4 grid-cols-1" | 
				
			||||
      /> | 
				
			||||
    </div> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
					Loading…
					
					
				
		Reference in new issue