Skip to content

Commit

Permalink
feat: add i18n, config file, improve repsonsive
Browse files Browse the repository at this point in the history
  • Loading branch information
f-necas committed Dec 6, 2024
1 parent 47aa4a1 commit 0a49d94
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 83 deletions.
3 changes: 1 addition & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
</head>
<body style="margin: 0">
<geor-header
logo-url="https://www.georchestra.org/public/georchestra-logo.svg"
active-app="datahub"
menu-file="/menu.json"
config-file="/config.json"
></geor-header>
<script type="module" src="/src/main.ts"></script>
</body>
Expand Down
19 changes: 16 additions & 3 deletions public/menu.json → public/config.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
{
"config": {

"legacyHeader": "false",
"legacyUrl": "/header",
"stylesheet": "https://data.lillemetropole.fr/public/georchestra.css",
"logoUrl": "https://data.lillemetropole.fr/public/logo-mel.jpg",
"hideLogin": false,
"lang": "es"
},
"menu": [
{
"label": "Catalogue",
"i18n": "catalogue",
"i18n": "hello",
"url": "/datahub/",
"active-app" : "datahub"
},
Expand All @@ -26,5 +31,13 @@
}
]
}
]
],
"i18n": {
"en": {
"hello": "Hello"
},
"fr": {
"hello": "Bonjour"
}
}
}
6 changes: 6 additions & 0 deletions src/menu.json → src/config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"defaultConfig": {
"legacyHeader": false,
"legacyUrl": "/header/",
"hideLogin": false,
"style": ""
},
"defaultMenu": [
{
"label": "Catalogue",
Expand Down
148 changes: 84 additions & 64 deletions src/header.ce.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,38 @@ import { computed, onMounted, reactive } from 'vue'
import { getUserDetails, getPlatformInfos } from './auth'
import type { User, PlatformInfos } from './auth'
import UserIcon from './ui/UserIcon.vue'
import GeorchestraLogo from './ui/GeorchestraLogo.vue'
import GeorchestraLogo from '@/ui/GeorchestraLogo.vue'
import ChevronDownIcon from '@/ui/ChevronDownIcon.vue'
import { LANG_2_TO_3_MAPPER, t } from '@/i18n'
import { defaultMenu } from '@/menu.json'
import { getI18n, i18n } from '@/i18n'
import { defaultMenu, defaultConfig } from '@/config.json'
const props = defineProps<{
hideLogin?: string
lang?: string
activeApp?: string
logoUrl?: string
//legacy option : using old iframe option
legacyHeader?: string
legacyUrl?: string
style?: string
configFile?: string
stylesheet?: string
menuFile?: string
}>()
const state = reactive({
user: null as null | User,
mobileMenuOpen: false,
lang3: props.lang,
platformInfos: null as null | PlatformInfos,
menu: defaultMenu,
config: defaultConfig,
lang3: defaultConfig.lang,
legacyHeader: false,
loaded: false,
})
const isAnonymous = computed(() => !state.user || state.user.anonymous)
const isWarned = computed(() => state.user?.warned)
const remainingDays = computed(() => state.user?.remainingDays)
const loginUrl = computed(() => {
const current = new URL(window.location.href)
current.searchParams.set('login', '')
return current.toString()
})
const logoutUrl = computed(() => '/logout')
function toggleMenu(): void {
state.mobileMenuOpen = !state.mobileMenuOpen
}
function checkCondition(item: object): boolean {
const hasRole = item.hasRole
if (!state.user) return false
Expand All @@ -58,58 +49,87 @@ function checkCondition(item: object): boolean {
function computeUrl(url: string): string {
return url.replace(/:lang3/, state.lang3)
}
function t(msg: string) {
return i18n.global.t(msg)
}
function setI18n(externalI18n: object): void {
state.lang3 = getI18n(
externalI18n,
state.config.lang || navigator.language.substring(0, 2) || 'en'
)
state.loaded = true
}
onMounted(() => {
if (props.menuFile)
fetch(props.menuFile)
.then(res => res.json())
.then(file => {
state.menu = file.menu
})
state.lang3 =
LANG_2_TO_3_MAPPER[props.lang || navigator.language.substring(0, 2)] ||
'eng'
getUserDetails().then(user => {
state.user = user
if (user?.adminRoles?.admin) {
getPlatformInfos().then(
platformInfos => (state.platformInfos = platformInfos)
)
}
})
getUserDetails()
.then(user => {
state.user = user
if (user?.adminRoles?.admin) {
getPlatformInfos().then(
platformInfos => (state.platformInfos = platformInfos)
)
}
})
.then(() => {
if (props.configFile)
fetch(props.configFile)
.then(res => res.json())
.then(json => {
console.log(json)
state.config = json.config
if (json.menu) {
state.menu = json.menu
}
setI18n(json.i18n)
})
else setI18n({})
})
})
</script>
<template>
<div v-if="props.legacyHeader === 'true'">
<div v-if="state.config.legacyHeader && state.loaded">
<iframe
class="w-full"
v-bind:src="`${props.legacyUrl}${
v-bind:src="`${state.config.legacyUrl}${
props.activeApp ? `?active=${props.activeApp}` : ''
}`"
v-bind:style="props.style"
v-bind:style="state.config.style"
></iframe>
</div>
<header v-else class="host h-[80px] text-base" v-bind:style="props.style">
<link rel="stylesheet" :href="props.stylesheet" v-if="props.stylesheet" />
<component :is="'style'" v-if="!props.stylesheet">
<header
v-else-if="state.loaded"
class="host h-[80px] text-base"
v-bind:style="state.config.style"
>
<link
rel="stylesheet"
:href="props.stylesheet || state.config.stylesheet"
v-if="props.stylesheet || state.config.stylesheet"
/>
<component :is="'style'" v-if="!state.config.stylesheet">
header { --georchestra-header-primary: #85127e;
--georchestra-header-secondary: #1b1f3b;
--georchestra-header-primary-light: #85127e1a;
--georchestra-header-secondary-light: #1b1f3b1a; }
</component>
<div class="justify-between text-slate-600 sm:flex hidden h-full bg-white">
<div
class="justify-between text-slate-600 md:flex hidden h-full bg-white md:text-sm"
>
<div class="flex">
<a href="/" class="flex justify-center items-center px-8 py-2">
<a
href="/"
class="flex justify-center items-center lg:px-3 md:px-2 py-2"
>
<img
v-if="props.logoUrl"
:src="props.logoUrl"
v-if="state.config.logoUrl"
:src="state.config.logoUrl"
alt="geOrchestra logo"
class="w-32"
/>
<GeorchestraLogo
v-else
class="w-full h-12 my-auto block"
></GeorchestraLogo>
<template v-else>
<GeorchestraLogo class="w-full h-12 my-auto"></GeorchestraLogo>
</template>
</a>
<nav class="flex justify-center items-center font-semibold">
<template v-for="(item, index) in state.menu" :key="index">
Expand All @@ -133,7 +153,7 @@ onMounted(() => {
<button
class="nav-item after:hover:scale-x-0 flex items-center"
>
<span class="mr-2 first-letter:capitalize">{{
<span class="lg:mr-2 md:mr-1 first-letter:capitalize">{{
item.i18n ? t(item.i18n) : item.label
}}</span>
<ChevronDownIcon
Expand Down Expand Up @@ -188,20 +208,23 @@ onMounted(() => {
>
</div>
<a
v-if="props.hideLogin !== 'true' && isAnonymous"
v-if="!state.config.hideLogin && isAnonymous"
class="btn"
:href="loginUrl"
>{{ t('login') }}</a
>
</div>
</div>
<div class="flex-col sm:hidden w-full h-full">
<div class="flex-col md:hidden w-full h-full">
<div
class="h-full inline-flex items-center justify-start align-middle px-6 py-8 shrink-0 w-full bg-primary/10"
>
<div class="grow flex justify-start items-center py-3">
<span class="inline-flex items-center rounded-full">
<button type="button" @click="toggleMenu">
<button
type="button"
@click="state.mobileMenuOpen = !state.mobileMenuOpen"
>
<svg
v-if="state.mobileMenuOpen"
xmlns="http://www.w3.org/2000/svg"
Expand All @@ -225,17 +248,14 @@ onMounted(() => {
/>
</svg>
</button>
<a href="/">
<a href="/" class="block ml-3">
<img
v-if="props.logoUrl"
:src="props.logoUrl"
v-if="state.config.logoUrl"
:src="state.config.logoUrl"
alt="geOrchestra logo"
class="w-24 ml-4"
class="w-24"
/>
<GeorchestraLogo
v-else
class="w-full h-12 ml-4"
></GeorchestraLogo>
<GeorchestraLogo v-else></GeorchestraLogo>
</a>
</span>
</div>
Expand All @@ -247,9 +267,9 @@ onMounted(() => {
`${state.user?.firstname} ${state.user?.lastname}`
}}</span></a
>
<a class="link-btn" :href="logoutUrl">logout</a>
<a class="link-btn" :href="logoutUrl">{{ t('logout') }}</a>
</div>
<a v-else class="btn" :href="loginUrl">login</a>
<a v-else class="btn" :href="loginUrl">{{ t('login') }}</a>
</div>
</div>

Expand Down Expand Up @@ -288,13 +308,13 @@ onMounted(() => {
@apply text-xl block text-center py-3 w-full border-b border-b-slate-300 first-letter:capitalize;
}
.nav-item {
@apply relative text-lg w-fit block after:hover:scale-x-[82%] px-2 mx-2 hover:text-black first-letter:capitalize;
@apply relative text-lg w-fit block after:hover:scale-x-100 lg:mx-3 md:mx-2 hover:text-black first-letter:capitalize text-base;
}
.nav-item:after {
@apply block content-[''] absolute h-[3px] bg-primary w-full scale-x-0 transition duration-300 origin-left;
}
.nav-item.active {
@apply after:scale-x-[82%] after:bg-primary after:bg-none text-gray-900;
@apply after:scale-x-100 after:bg-primary after:bg-none text-gray-900;
}
.btn {
@apply px-4 py-2 mx-2 text-slate-100 bg-primary rounded hover:bg-slate-700 transition-colors first-letter:capitalize;
Expand Down
32 changes: 18 additions & 14 deletions src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ import ru from './i18n/ru.json'
import pt from './i18n/pt.json'
import { createI18n } from 'vue-i18n'

const i18n = createI18n({
locale: navigator.language.substring(0, 2),
fallbackLocale: 'fr',
messages: {
en: en,
de: de,
fr: fr,
es: es,
nl: nl,
ru: ru,
pt: pt,
},
})
export let i18n: any

export const t = i18n.global.t.bind(i18n.global)
export const getI18n = (messages: any, lang: string) => {
i18n = createI18n({
locale: navigator.language.substring(0, 2),
fallbackLocale: 'en',
messages: {
en: { ...en, ...messages.en },
de: { ...de, ...messages.de },
fr: { ...fr, ...messages.fr },
es: { ...es, ...messages.es },
nl: { ...nl, ...messages.nl },
ru: { ...ru, ...messages.ru },
pt: { ...pt, ...messages.pt },
},
})
i18n.global.locale = lang
return LANG_2_TO_3_MAPPER[lang]
}

export const LANG_2_TO_3_MAPPER: { [index: string]: any } = {
en: 'eng',
Expand Down
Loading

0 comments on commit 0a49d94

Please sign in to comment.