feat(lite): settings page (#6418)
This commit is contained in:
parent
b2cebbfaf4
commit
c7227d2f50
@ -3,6 +3,7 @@
|
|||||||
## **0.2.0**
|
## **0.2.0**
|
||||||
|
|
||||||
- Invalidate sessionId token after logout (PR [#6480](https://github.com/vatesfr/xen-orchestra/pull/6480))
|
- Invalidate sessionId token after logout (PR [#6480](https://github.com/vatesfr/xen-orchestra/pull/6480))
|
||||||
|
- Settings page (PR [#6418](https://github.com/vatesfr/xen-orchestra/pull/6418))
|
||||||
|
|
||||||
## **0.1.0**
|
## **0.1.0**
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@xen-orchestra/lite",
|
"name": "@xen-orchestra/lite",
|
||||||
"version": "0.0.0",
|
"version": "0.1.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "GIT_HEAD=$(git rev-parse HEAD) vite",
|
||||||
"build": "run-p type-check build-only",
|
"build": "run-p type-check build-only",
|
||||||
"preview": "vite preview --port 4173",
|
"preview": "vite preview --port 4173",
|
||||||
"build-only": "vite build",
|
"build-only": "GIT_HEAD=$(git rev-parse HEAD) vite build",
|
||||||
"deploy": "./scripts/deploy.sh",
|
"deploy": "./scripts/deploy.sh",
|
||||||
"type-check": "vue-tsc --noEmit",
|
"type-check": "vue-tsc --noEmit",
|
||||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||||
|
@ -11,3 +11,17 @@ body {
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
color: var(--color-blue-scale-100);
|
color: var(--color-blue-scale-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-extra-blue-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-view {
|
||||||
|
padding: 1.2rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
<UiIcon :icon="faAngleDown" class="dropdown-icon" />
|
<UiIcon :icon="faAngleDown" class="dropdown-icon" />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<MenuItem :icon="faGear">{{ $t("settings") }}</MenuItem>
|
<MenuItem :icon="faGear" @click="openSettings">{{
|
||||||
|
$t("settings")
|
||||||
|
}}</MenuItem>
|
||||||
<MenuItem :icon="faMessage" @click="openFeedbackUrl">
|
<MenuItem :icon="faMessage" @click="openFeedbackUrl">
|
||||||
{{ $t("send-us-feedback") }}
|
{{ $t("send-us-feedback") }}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -50,6 +52,8 @@ const openFeedbackUrl = () => {
|
|||||||
"noopener"
|
"noopener"
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openSettings = () => router.push({ name: "settings" });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -5,50 +5,13 @@
|
|||||||
</RouterLink>
|
</RouterLink>
|
||||||
<slot />
|
<slot />
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<FontAwesomeIcon
|
|
||||||
:icon="colorModeIcon"
|
|
||||||
style="font-size: 1.5em; cursor: pointer"
|
|
||||||
@click="toggleTheme"
|
|
||||||
/>
|
|
||||||
<FormWidget :before="faEarthAmericas">
|
|
||||||
<select v-model="$i18n.locale">
|
|
||||||
<option v-for="locale in $i18n.availableLocales" :key="locale">
|
|
||||||
{{ locale }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</FormWidget>
|
|
||||||
<AccountButton />
|
<AccountButton />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch } from "vue";
|
import AccountButton from '@/components/AccountButton.vue'
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import {
|
|
||||||
faEarthAmericas,
|
|
||||||
faMoon,
|
|
||||||
faSun,
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
|
||||||
import AccountButton from "@/components/AccountButton.vue";
|
|
||||||
import FormWidget from "@/components/FormWidget.vue";
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { locale } = useI18n();
|
|
||||||
|
|
||||||
watch(locale, (newLocale) => localStorage.setItem("lang", newLocale));
|
|
||||||
|
|
||||||
const colorMode = useLocalStorage<string>("colorMode", "dark");
|
|
||||||
const toggleTheme = () => {
|
|
||||||
colorMode.value = document.documentElement.classList.toggle("dark")
|
|
||||||
? "dark"
|
|
||||||
: "light";
|
|
||||||
};
|
|
||||||
const colorModeIcon = computed(() =>
|
|
||||||
colorMode.value === "light" ? faMoon : faSun
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="postcss" scoped>
|
<style lang="postcss" scoped>
|
||||||
|
14
@xen-orchestra/lite/src/components/ui/UiKeyValueList.vue
Normal file
14
@xen-orchestra/lite/src/components/ui/UiKeyValueList.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ui-key-value-list"><slot /></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup></script>
|
||||||
|
|
||||||
|
<style lang="postcss" scoped>
|
||||||
|
.ui-key-value-list {
|
||||||
|
margin-top: 2rem;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
/* UiKeyValueRow: 15em (key) + 15em (value) + 1rem (gap) */
|
||||||
|
min-width: calc(30em + 1rem);
|
||||||
|
}
|
||||||
|
</style>
|
37
@xen-orchestra/lite/src/components/ui/UiKeyValueRow.vue
Normal file
37
@xen-orchestra/lite/src/components/ui/UiKeyValueRow.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ui-key-value-row">
|
||||||
|
<span class="key" v-if="$slots.key">
|
||||||
|
<slot name="key" />
|
||||||
|
</span>
|
||||||
|
<span class="value">
|
||||||
|
<slot name="value" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup></script>
|
||||||
|
|
||||||
|
<style lang="postcss" scoped>
|
||||||
|
.ui-key-value-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: baseline;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
.key {
|
||||||
|
color: var(--color-blue-scale-300);
|
||||||
|
width: 15%;
|
||||||
|
max-width: 15em;
|
||||||
|
max-width: 30em;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.key,
|
||||||
|
.value {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 15em;
|
||||||
|
max-width: 30em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -2,6 +2,24 @@ import { createI18n } from "vue-i18n";
|
|||||||
import en from "@/locales/en.json";
|
import en from "@/locales/en.json";
|
||||||
import fr from "@/locales/fr.json";
|
import fr from "@/locales/fr.json";
|
||||||
|
|
||||||
|
interface Locales {
|
||||||
|
[key: string]: {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const locales: Locales = {
|
||||||
|
en: {
|
||||||
|
code: "en",
|
||||||
|
name: "English",
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
code: "fr",
|
||||||
|
name: "Français",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default createI18n<[typeof en], "en" | "fr">({
|
export default createI18n<[typeof en], "en" | "fr">({
|
||||||
locale: localStorage.getItem("lang") ?? "en",
|
locale: localStorage.getItem("lang") ?? "en",
|
||||||
fallbackLocale: "en",
|
fallbackLocale: "en",
|
||||||
|
@ -4,26 +4,34 @@
|
|||||||
"add-or": "+OR",
|
"add-or": "+OR",
|
||||||
"add-sort": "Add sort",
|
"add-sort": "Add sort",
|
||||||
"alarms": "Alarms",
|
"alarms": "Alarms",
|
||||||
|
"appearance": "Appearance",
|
||||||
"ascending": "ascending",
|
"ascending": "ascending",
|
||||||
"available-properties-for-advanced-filter": "Available properties for advanced filter:",
|
"available-properties-for-advanced-filter": "Available properties for advanced filter:",
|
||||||
"backup": "Backup",
|
"backup": "Backup",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"change-power-state": "Change power state",
|
"change-power-state": "Change power state",
|
||||||
|
"community": "Community",
|
||||||
|
"community-name": "{name} community",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"cpu-usage":"CPU usage",
|
"cpu-usage":"CPU usage",
|
||||||
|
"dark-mode": "Dark mode",
|
||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"descending": "descending",
|
"descending": "descending",
|
||||||
|
"display": "Display",
|
||||||
"edit-config": "Edit config",
|
"edit-config": "Edit config",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"export-table-to": "Export table to {type}",
|
"export-table-to": "Export table to {type}",
|
||||||
"export-vms": "Export VMs",
|
"export-vms": "Export VMs",
|
||||||
"hosts": "Hosts",
|
"hosts": "Hosts",
|
||||||
|
"language": "Language",
|
||||||
"loading-hosts": "Loading hosts…",
|
"loading-hosts": "Loading hosts…",
|
||||||
"log-out": "Log out",
|
"log-out": "Log out",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"migrate": "Migrate",
|
"migrate": "Migrate",
|
||||||
"network": "Network",
|
"network": "Network",
|
||||||
|
"news": "News",
|
||||||
|
"news-name": "{name} news",
|
||||||
"or": "Or",
|
"or": "Or",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"property": "Property",
|
"property": "Property",
|
||||||
@ -41,5 +49,6 @@
|
|||||||
"top-#": "Top {n}",
|
"top-#": "Top {n}",
|
||||||
"total-free": "Total free",
|
"total-free": "Total free",
|
||||||
"total-used": "Total used",
|
"total-used": "Total used",
|
||||||
|
"version": "Version",
|
||||||
"vms": "VMs"
|
"vms": "VMs"
|
||||||
}
|
}
|
||||||
|
@ -4,26 +4,34 @@
|
|||||||
"add-or": "+OU",
|
"add-or": "+OU",
|
||||||
"add-sort": "Ajouter un tri",
|
"add-sort": "Ajouter un tri",
|
||||||
"alarms": "Alarmes",
|
"alarms": "Alarmes",
|
||||||
|
"appearance": "Apparence",
|
||||||
"ascending": "ascendant",
|
"ascending": "ascendant",
|
||||||
"available-properties-for-advanced-filter": "Propriétés disponibles pour le filtrage avancé :",
|
"available-properties-for-advanced-filter": "Propriétés disponibles pour le filtrage avancé :",
|
||||||
"backup": "Sauvegarde",
|
"backup": "Sauvegarde",
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
"change-power-state": "Changer l'état d'alimentation",
|
"change-power-state": "Changer l'état d'alimentation",
|
||||||
|
"community": "Communauté",
|
||||||
|
"community-name": "Communauté {name}",
|
||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
"cpu-usage":"Utilisation CPU",
|
"cpu-usage":"Utilisation CPU",
|
||||||
|
"dark-mode": "Mode sombre",
|
||||||
"dashboard": "Tableau de bord",
|
"dashboard": "Tableau de bord",
|
||||||
"delete": "Supprimer",
|
"delete": "Supprimer",
|
||||||
"descending": "descendant",
|
"descending": "descendant",
|
||||||
|
"display": "Affichage",
|
||||||
"edit-config": "Modifier config",
|
"edit-config": "Modifier config",
|
||||||
"export": "Exporter",
|
"export": "Exporter",
|
||||||
"export-table-to": "Exporter le tableau en {type}",
|
"export-table-to": "Exporter le tableau en {type}",
|
||||||
"export-vms": "Exporter les VMs",
|
"export-vms": "Exporter les VMs",
|
||||||
"hosts": "Hôtes",
|
"hosts": "Hôtes",
|
||||||
|
"language": "Langue",
|
||||||
"loading-hosts": "Chargement des hôtes…",
|
"loading-hosts": "Chargement des hôtes…",
|
||||||
"log-out": "Se déconnecter",
|
"log-out": "Se déconnecter",
|
||||||
"login": "Connexion",
|
"login": "Connexion",
|
||||||
"migrate": "Migrer",
|
"migrate": "Migrer",
|
||||||
"network": "Réseau",
|
"network": "Réseau",
|
||||||
|
"news": "Actualités",
|
||||||
|
"news-name": "Actualités {name}",
|
||||||
"or": "Ou",
|
"or": "Ou",
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"property": "Propriété",
|
"property": "Propriété",
|
||||||
@ -41,5 +49,6 @@
|
|||||||
"top-#": "Top {n}",
|
"top-#": "Top {n}",
|
||||||
"total-free": "Total libre",
|
"total-free": "Total libre",
|
||||||
"total-used": "Total utilisé",
|
"total-used": "Total utilisé",
|
||||||
|
"version": "Version",
|
||||||
"vms": "VMs"
|
"vms": "VMs"
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import pool from "@/router/pool";
|
|||||||
import HomeView from "@/views/HomeView.vue";
|
import HomeView from "@/views/HomeView.vue";
|
||||||
import HostDashboardView from "@/views/host/HostDashboardView.vue";
|
import HostDashboardView from "@/views/host/HostDashboardView.vue";
|
||||||
import HostRootView from "@/views/host/HostRootView.vue";
|
import HostRootView from "@/views/host/HostRootView.vue";
|
||||||
|
import SettingsView from "@/views/settings/SettingsView.vue";
|
||||||
import VmConsoleView from "@/views/vm/VmConsoleView.vue";
|
import VmConsoleView from "@/views/vm/VmConsoleView.vue";
|
||||||
import VmRootView from "@/views/vm/VmRootView.vue";
|
import VmRootView from "@/views/vm/VmRootView.vue";
|
||||||
|
|
||||||
@ -14,6 +15,11 @@ const router = createRouter({
|
|||||||
name: "home",
|
name: "home",
|
||||||
component: HomeView,
|
component: HomeView,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/settings",
|
||||||
|
name: "settings",
|
||||||
|
component: SettingsView,
|
||||||
|
},
|
||||||
pool,
|
pool,
|
||||||
{
|
{
|
||||||
path: "/host/:uuid",
|
path: "/host/:uuid",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="pool-dashboard-view">
|
<div class="pool-dashboard-view card-view">
|
||||||
<PoolDashboardStatus class="item" />
|
<PoolDashboardStatus class="item" />
|
||||||
<PoolDashboardStorageUsage class="item" />
|
<PoolDashboardStorageUsage class="item" />
|
||||||
<PoolDashboardCpuUsage class="item" />
|
<PoolDashboardCpuUsage class="item" />
|
||||||
@ -64,11 +64,6 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="postcss" scoped>
|
<style lang="postcss" scoped>
|
||||||
.pool-dashboard-view {
|
|
||||||
display: flex;
|
|
||||||
gap: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
min-width: 37rem;
|
min-width: 37rem;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="pool-root-view">
|
<div class="pool-root-view">
|
||||||
<PoolHeader />
|
<PoolHeader />
|
||||||
<PoolTabBar />
|
<PoolTabBar />
|
||||||
<div class="view">
|
<div class="card-view">
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -13,8 +13,4 @@ import PoolHeader from "@/components/pool/PoolHeader.vue";
|
|||||||
import PoolTabBar from "@/components/pool/PoolTabBar.vue";
|
import PoolTabBar from "@/components/pool/PoolTabBar.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="postcss" scoped>
|
<style lang="postcss" scoped></style>
|
||||||
.view {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
124
@xen-orchestra/lite/src/views/settings/SettingsView.vue
Normal file
124
@xen-orchestra/lite/src/views/settings/SettingsView.vue
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<TitleBar :icon="faGear">{{ $t("settings") }}</TitleBar>
|
||||||
|
<div class="card-view">
|
||||||
|
<UiCard class="group">
|
||||||
|
<UiTitle type="h4">Xen Orchestra Lite</UiTitle>
|
||||||
|
<UiKeyValueList>
|
||||||
|
<UiKeyValueRow>
|
||||||
|
<template #key>{{ $t("version") }}</template>
|
||||||
|
<template #value
|
||||||
|
>v{{ version
|
||||||
|
}}<code v-if="gitHead"> ({{ gitHead.slice(0, 5) }})</code></template
|
||||||
|
>
|
||||||
|
</UiKeyValueRow>
|
||||||
|
<UiKeyValueRow>
|
||||||
|
<template #key>{{ $t("news") }}</template>
|
||||||
|
<template #value
|
||||||
|
><a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://xcp-ng.org/blog/"
|
||||||
|
>{{ $t("news-name", { name: "XCP-ng" }) }}</a
|
||||||
|
> - <a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://xen-orchestra.com/blog/"
|
||||||
|
>{{ $t("news-name", { name: "Xen Orchestra" }) }}</a
|
||||||
|
></template
|
||||||
|
>
|
||||||
|
</UiKeyValueRow>
|
||||||
|
<UiKeyValueRow>
|
||||||
|
<template #key>{{ $t("community") }}</template>
|
||||||
|
<template #value
|
||||||
|
><a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://xcp-ng.org/forum"
|
||||||
|
>{{ $t("community-name", { name: "XCP-ng" }) }}</a
|
||||||
|
> - <a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://xcp-ng.org/forum/category/12/xen-orchestra"
|
||||||
|
>{{ $t("community-name", { name: "Xen Orchestra" }) }}</a
|
||||||
|
></template
|
||||||
|
>
|
||||||
|
</UiKeyValueRow>
|
||||||
|
</UiKeyValueList>
|
||||||
|
</UiCard>
|
||||||
|
<UiCard class="group">
|
||||||
|
<UiTitle type="h4">{{ $t("display") }}</UiTitle>
|
||||||
|
<UiKeyValueList>
|
||||||
|
<UiKeyValueRow>
|
||||||
|
<template #key>{{ $t("appearance") }}</template>
|
||||||
|
<template #value
|
||||||
|
><FormLabel>
|
||||||
|
<FormToggle
|
||||||
|
:modelValue="darkMode"
|
||||||
|
@update:modelValue="setDarkMode"
|
||||||
|
/>{{ $t("dark-mode") }}</FormLabel
|
||||||
|
></template
|
||||||
|
>
|
||||||
|
</UiKeyValueRow>
|
||||||
|
</UiKeyValueList>
|
||||||
|
</UiCard>
|
||||||
|
<UiCard class="group">
|
||||||
|
<UiTitle type="h4">{{ $t("language") }}</UiTitle>
|
||||||
|
<UiKeyValueList>
|
||||||
|
<UiKeyValueRow>
|
||||||
|
<template #value>
|
||||||
|
<FormWidget class="full-length" :before="faEarthAmericas">
|
||||||
|
<select v-model="$i18n.locale">
|
||||||
|
<option
|
||||||
|
:value="locale"
|
||||||
|
v-for="locale in $i18n.availableLocales"
|
||||||
|
:key="locale"
|
||||||
|
>
|
||||||
|
{{ locales[locale].name ?? locale }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</FormWidget></template
|
||||||
|
>
|
||||||
|
</UiKeyValueRow>
|
||||||
|
</UiKeyValueList>
|
||||||
|
</UiCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, watch } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { locales } from "@/i18n";
|
||||||
|
import { faEarthAmericas, faGear } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
|
import FormWidget from "@/components/FormWidget.vue";
|
||||||
|
import TitleBar from "@/components/TitleBar.vue";
|
||||||
|
import FormLabel from "@/components/form/FormLabel.vue";
|
||||||
|
import FormToggle from "@/components/form/FormToggle.vue";
|
||||||
|
import UiCard from "@/components/ui/UiCard.vue";
|
||||||
|
import UiKeyValueList from "@/components/ui/UiKeyValueList.vue";
|
||||||
|
import UiKeyValueRow from "@/components/ui/UiKeyValueRow.vue";
|
||||||
|
import UiTitle from "@/components/ui/UiTitle.vue";
|
||||||
|
|
||||||
|
const version = XO_LITE_VERSION;
|
||||||
|
const gitHead = XO_LITE_GIT_HEAD;
|
||||||
|
const { locale } = useI18n();
|
||||||
|
|
||||||
|
watch(locale, (newLocale) => localStorage.setItem("lang", newLocale));
|
||||||
|
|
||||||
|
const colorMode = useLocalStorage<string>("colorMode", "dark");
|
||||||
|
const darkMode = computed(() => colorMode.value !== "light");
|
||||||
|
const setDarkMode = (enabled: boolean) => {
|
||||||
|
colorMode.value = enabled ? "dark" : "light";
|
||||||
|
document.documentElement.classList[enabled ? "add" : "remove"]("dark");
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="postcss" scoped>
|
||||||
|
.card-view {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-length {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -6,6 +6,10 @@ import vue from "@vitejs/plugin-vue";
|
|||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue(), vueI18n()],
|
plugins: [vue(), vueI18n()],
|
||||||
|
define: {
|
||||||
|
XO_LITE_VERSION: JSON.stringify(process.env.npm_package_version),
|
||||||
|
XO_LITE_GIT_HEAD: JSON.stringify(process.env.GIT_HEAD),
|
||||||
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
|
Loading…
Reference in New Issue
Block a user