update home page builder

This commit is contained in:
Jordan Blasenhauer 2024-05-22 16:43:53 +02:00
parent ccc05e9bab
commit b417c83474
9 changed files with 393 additions and 335 deletions

View file

@ -454,11 +454,11 @@ body {
/* MENU */
.menu-svg {
@apply h-6 w-6 relative;
@apply h-6 w-6 relative;
}
.menu-account-title-container {
@apply mt-2 w-full px-1
@apply mt-2 w-full px-1;
}
.menu-account-title {
@ -494,7 +494,7 @@ body {
}
.menu-container {
@apply transition-all mt-[4.5rem] fixed flex flex-col justify-between inset-y-0 max-h-screen w-full p-0 my-4 antialiased duration-200 bg-white border-0 shadow-xl dark:shadow-none dark:bg-slate-850 dark:brightness-110 max-w-64 z-[1000] xl:ml-6 rounded-2xl xl:left-0 ;
@apply transition-all mt-[4.5rem] fixed flex flex-col justify-between inset-y-0 max-h-screen w-full p-0 my-4 antialiased duration-200 bg-white border-0 shadow-xl dark:shadow-none dark:bg-slate-850 dark:brightness-110 max-w-64 z-[1000] xl:ml-6 rounded-2xl xl:left-0;
}
.no-banner.menu-container {
@ -518,7 +518,7 @@ body {
}
.menu-logo-link-container {
@apply flex justify-center px-8 m-0 text-sm whitespace-nowrap dark:text-white text-slate-700
@apply flex justify-center px-8 m-0 text-sm whitespace-nowrap dark:text-white text-slate-700;
}
.menu-logo-dark {
@ -633,7 +633,6 @@ body {
@apply mx-2 w-6;
}
.menu-logout {
@apply tracking-wide dark:brightness-125 hover:brightness-75 w-full inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-gradient-to-tl bg-primary leading-normal text-xs ease-in shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md;
}
@ -717,7 +716,7 @@ body {
}
.news-sidebar-post-tag {
@apply my-0 mr-1 rounded bg-secondary hover:brightness-90 hover:-translate-y-0.4 text-white py-1 px-2 text-sm;
@apply my-0 mr-1 rounded bg-secondary hover:brightness-90 hover:-translate-y-0.4 text-white py-1 px-2 text-sm;
}
.news-sidebar-post-date-container {
@ -827,11 +826,11 @@ body {
}
.stat-value {
@apply mb-1 font-bold dark:text-white/90;
@apply mb-1 font-bold dark:text-white/90 uppercase;
}
.stat-subtitle {
@apply font-bold leading-normal text-sm mb-0;
@apply font-bold leading-normal text-sm mb-0 lowercase;
}
.stat-svg {
@ -842,11 +841,10 @@ body {
@apply absolute top-0 right-0 min-w-12 dark:brightness-90 flex justify-center items-center w-12 h-12 text-center rounded-circle;
}
/* LAYOUT COMPONENT */
.card {
@apply transition dark:brightness-110 shadow-md bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border;
@apply transition dark:brightness-110 shadow-md bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border hover:scale-[1.01] transform duration-300 ease-in-out;
}
/* CARD INFO */
@ -1205,7 +1203,7 @@ body {
}
.feedback-alert-container.is-fixed {
@apply fixed right-0 bottom-0 sm:max-w-[300px] z-[1000];
@apply fixed right-0 bottom-0 sm:max-w-[300px] z-[1000];
}
.feedback-alert-wrap {
@ -1232,10 +1230,8 @@ body {
@apply text-white mt-2 mb-0 text-sm;
}
/* TEXT COLOR MODIFIER */
.success.stat-subtitle {
@apply text-green-500;
}
@ -1383,218 +1379,182 @@ body {
/* BACKGROUND COLOR MODIFIER */
.success.stat-svg-container,
.success.feedback-alert-wrap
{
.success.feedback-alert-wrap {
@apply bg-green-500;
}
.error.stat-svg-container,
.error.feedback-alert-wrap
{
.error.feedback-alert-wrap {
@apply bg-red-500;
}
.warning.stat-svg-container,
.warning.feedback-alert-wrap
{
.warning.feedback-alert-wrap {
@apply bg-yellow-500;
}
.info.stat-svg-container,
.info.feedback-alert-wrap
{
.info.feedback-alert-wrap {
@apply bg-sky-500;
}
.purple.stat-svg-container,
.purple.feedback-alert-wrap
{
.purple.feedback-alert-wrap {
@apply bg-purple-500;
}
.green.stat-svg-container,
.green.feedback-alert-wrap
{
.green.feedback-alert-wrap {
@apply bg-green-500;
}
.red.stat-svg-container,
.red.feedback-alert-wrap
{
.red.feedback-alert-wrap {
@apply bg-red-500;
}
.orange.stat-svg-container,
.orange.feedback-alert-wrap
{
.orange.feedback-alert-wrap {
@apply bg-orange-500;
}
.blue.stat-svg-container,
.blue.feedback-alert-wrap
{
.blue.feedback-alert-wrap {
@apply bg-blue-500;
}
.yellow.stat-svg-container,
.yellow.feedback-alert-wrap
{
.yellow.feedback-alert-wrap {
@apply bg-yellow-500;
}
.gray.stat-svg-container,
.gray.feedback-alert-wrap
{
.gray.feedback-alert-wrap {
@apply bg-gray-500;
}
.dark.stat-svg-container,
.dark.feedback-alert-wrap
{
.dark.feedback-alert-wrap {
@apply bg-slate-500;
}
.amber.stat-svg-container,
.amber.feedback-alert-wrap
{
.amber.feedback-alert-wrap {
@apply bg-amber-500;
}
.emerald.stat-svg-container,
.emerald.feedback-alert-wrap
{
.emerald.feedback-alert-wrap {
@apply bg-emerald-500;
}
.teal.stat-svg-container,
.teal.feedback-alert-wrap
{
.teal.feedback-alert-wrap {
@apply bg-teal-500;
}
.indigo.stat-svg-container,
.indigo.feedback-alert-wrap
{
.indigo.feedback-alert-wrap {
@apply bg-indigo-500;
}
.cyan.stat-svg-container,
.cyan.feedback-alert-wrap
{
.cyan.feedback-alert-wrap {
@apply bg-cyan-500;
}
.sky.stat-svg-container,
.sky.feedback-alert-wrap
{
.sky.feedback-alert-wrap {
@apply bg-sky-500;
}
.pink.stat-svg-container,
.pink.feedback-alert-wrap
{
.pink.feedback-alert-wrap {
@apply bg-pink-500;
}
.lime.stat-svg-container,
.lime.feedback-alert-wrap
{
.lime.feedback-alert-wrap {
@apply bg-lime-500;
}
.purple-darker.stat-svg-container,
.purle-darker.feedback-alert-wrap
{
.purle-darker.feedback-alert-wrap {
@apply bg-purple-600;
}
.green-darker.stat-svg-container,
.green-darker.feedback-alert-wrap
{
.green-darker.feedback-alert-wrap {
@apply bg-green-700;
}
.red-darker.stat-svg-container,
.red-darker.feedback-alert-wrap
{
.red-darker.feedback-alert-wrap {
@apply bg-red-700;
}
.orange-darker.stat-svg-container,
.orange-darker.feedback-alert-wrap
{
.orange-darker.feedback-alert-wrap {
@apply bg-orange-600;
}
.blue-darker.stat-svg-container,
.blue-darker.feedback-alert-wrap
{
.blue-darker.feedback-alert-wrap {
@apply bg-blue-600;
}
.yellow-darker.stat-svg-container,
.yellow-darker.feedback-alert-wrap
{
.yellow-darker.feedback-alert-wrap {
@apply bg-yellow-600;
}
.gray-darker.stat-svg-container,
.gray-darker.feedback-alert-wrap
{
.gray-darker.feedback-alert-wrap {
@apply bg-gray-600;
}
.dark-darker.stat-svg-container,
.dark-darker.feedback-alert-wrap
{
.dark-darker.feedback-alert-wrap {
@apply bg-slate-600;
}
.amber-darker.stat-svg-container,
.amber-darker.feedback-alert-wrap
{
.amber-darker.feedback-alert-wrap {
@apply bg-amber-600;
}
.emerald-darker.stat-svg-container,
.emerald-darker.feedback-alert-wrap
{
.emerald-darker.feedback-alert-wrap {
@apply bg-emerald-600;
}
.teal-darker.stat-svg-container,
.teal-darker.feedback-alert-wrap
{
.teal-darker.feedback-alert-wrap {
@apply bg-teal-600;
}
.indigo-darker.stat-svg-container,
.indigo-darker.feedback-alert-wrap
{
.indigo-darker.feedback-alert-wrap {
@apply bg-indigo-600;
}
.cyan-darker.stat-svg-container,
.cyan-darker.feedback-alert-wrap
{
.cyan-darker.feedback-alert-wrap {
@apply bg-cyan-600;
}
.sky-darker.stat-svg-container,
.sky-darker.feedback-alert-wrap
{
.sky-darker.feedback-alert-wrap {
@apply bg-sky-700;
}
.pink-darker.stat-svg-container,
.pink-darker.feedback-alert-wrap
{
.pink-darker.feedback-alert-wrap {
@apply bg-pink-600;
}
.lime-darker.stat-svg-container,
.lime-darker.feedback-alert-wrap
{
.lime-darker.feedback-alert-wrap {
@apply bg-lime-600;
}
@ -1604,8 +1564,7 @@ body {
.success.btn-svg,
.success.menu-svg,
.success.social-svg,
.success.stat-svg
{
.success.stat-svg {
@apply fill-green-500;
}
@ -1613,8 +1572,7 @@ body {
.error.btn-svg,
.error.menu-svg,
.error.social-svg,
.error.stat-svg
{
.error.stat-svg {
@apply fill-red-500;
}
@ -1622,8 +1580,7 @@ body {
.warning.btn-svg,
.warning.menu-svg,
.warning.social-svg,
.warning.stat-svg
{
.warning.stat-svg {
@apply fill-yellow-500;
}
@ -1631,8 +1588,7 @@ body {
.info.btn-svg,
.info.menu-svg,
.info.social-svg,
.info.stat-svg
{
.info.stat-svg {
@apply fill-sky-500;
}
@ -1640,8 +1596,7 @@ body {
.white.btn-svg,
.white.menu-svg,
.white.social-svg,
.white.stat-svg
{
.white.stat-svg {
@apply fill-white;
}
@ -1649,8 +1604,7 @@ body {
.purple.btn-svg,
.purple.menu-svg,
.purple.social-svg,
.purple.stat-svg
{
.purple.stat-svg {
@apply fill-purple-500;
}
@ -1658,8 +1612,7 @@ body {
.green.btn-svg,
.green.menu-svg,
.green.social-svg,
.green.stat-svg
{
.green.stat-svg {
@apply fill-green-500;
}
@ -1667,8 +1620,7 @@ body {
.red.btn-svg,
.red.menu-svg,
.red.social-svg,
.red.stat-svg
{
.red.stat-svg {
@apply fill-red-500;
}
@ -1676,8 +1628,7 @@ body {
.orange.btn-svg,
.orange.menu-svg,
.orange.social-svg,
.orange.stat-svg
{
.orange.stat-svg {
@apply fill-orange-500;
}
@ -1685,8 +1636,7 @@ body {
.blue.btn-svg,
.blue.menu-svg,
.blue.social-svg,
.blue.stat-svg
{
.blue.stat-svg {
@apply fill-blue-500;
}
@ -1694,8 +1644,7 @@ body {
.yellow.btn-svg,
.yellow.menu-svg,
.yellow.social-svg,
.yellow.stat-svg
{
.yellow.stat-svg {
@apply fill-yellow-500;
}
@ -1703,8 +1652,7 @@ body {
.gray.btn-svg,
.gray.menu-svg,
.gray.social-svg,
.gray.stat-svg
{
.gray.stat-svg {
@apply fill-gray-500;
}
@ -1712,8 +1660,7 @@ body {
.dark.btn-svg,
.dark.menu-svg,
.dark.social-svg,
.dark.stat-svg
{
.dark.stat-svg {
@apply fill-slate-500;
}
@ -1721,8 +1668,7 @@ body {
.amber.btn-svg,
.amber.menu-svg,
.amber.social-svg,
.amber.stat-svg
{
.amber.stat-svg {
@apply fill-amber-500;
}
@ -1730,8 +1676,7 @@ body {
.emerald.btn-svg,
.emerald.menu-svg,
.emerald.social-svg,
.emerald.stat-svg
{
.emerald.stat-svg {
@apply fill-emerald-500;
}
@ -1739,8 +1684,7 @@ body {
.teal.btn-svg,
.teal.menu-svg,
.teal.social-svg,
.teal.stat-svg
{
.teal.stat-svg {
@apply fill-teal-500;
}
@ -1748,8 +1692,7 @@ body {
.indigo.btn-svg,
.indigo.menu-svg,
.indigo.social-svg,
.indigo.stat-svg
{
.indigo.stat-svg {
@apply fill-indigo-500;
}
@ -1757,8 +1700,7 @@ body {
.cyan.btn-svg,
.cyan.menu-svg,
.cyan.social-svg,
.cyan.stat-svg
{
.cyan.stat-svg {
@apply fill-cyan-500;
}
@ -1766,8 +1708,7 @@ body {
.sky.btn-svg,
.sky.menu-svg,
.sky.social-svg,
.sky.stat-svg
{
.sky.stat-svg {
@apply fill-sky-500;
}
@ -1775,8 +1716,7 @@ body {
.pink.btn-svg,
.pink.menu-svg,
.pink.social-svg,
.pink.stat-svg
{
.pink.stat-svg {
@apply fill-pink-500;
}
@ -1784,8 +1724,7 @@ body {
.lime.btn-svg,
.lime.menu-svg,
.lime.social-svg,
.lime.stat-svg
{
.lime.stat-svg {
@apply fill-lime-500;
}
@ -1793,8 +1732,7 @@ body {
.twitter.btn-svg,
.twitter.menu-svg,
.twitter.social-svg,
.twitter.stat-svg
{
.twitter.stat-svg {
@apply fill-[#1DA1F2];
}
@ -1802,8 +1740,7 @@ body {
.linkedin.btn-svg,
.linkedin.menu-svg,
.linkedin.social-svg,
.linkedin.stat-svg
{
.linkedin.stat-svg {
@apply fill-[#0A63BC];
}
@ -1811,8 +1748,7 @@ body {
.discord.btn-svg,
.discord.menu-svg,
.discord.social-svg,
.discord.stat-svg
{
.discord.stat-svg {
@apply fill-[#5562EA];
}
@ -1820,8 +1756,7 @@ body {
.github.btn-svg,
.github.menu-svg,
.github.social-svg,
.github.stat-svg
{
.github.stat-svg {
@apply fill-[#171A1F] dark:fill-gray-300;
}
@ -1829,8 +1764,7 @@ body {
.purle-darker.btn-svg,
.purple-darker.menu-svg,
.purple-darker.social-svg,
.purple-darker.stat-svg
{
.purple-darker.stat-svg {
@apply fill-purple-600;
}
@ -1838,8 +1772,7 @@ body {
.green-darker.btn-svg,
.green-darker.menu-svg,
.green-darker.social-svg,
.green-darker.stat-svg
{
.green-darker.stat-svg {
@apply fill-green-700;
}
@ -1847,8 +1780,7 @@ body {
.red-darker.btn-svg,
.red-darker.menu-svg,
.red-darker.social-svg,
.red-darker.stat-svg
{
.red-darker.stat-svg {
@apply fill-red-700;
}
@ -1856,8 +1788,7 @@ body {
.orange-darker.btn-svg,
.orange-darker.menu-svg,
.orange-darker.social-svg,
.orange-darker.stat-svg
{
.orange-darker.stat-svg {
@apply fill-orange-600;
}
@ -1865,8 +1796,7 @@ body {
.blue-darker.btn-svg,
.blue-darker.menu-svg,
.blue-darker.social-svg,
.blue-darker.stat-svg
{
.blue-darker.stat-svg {
@apply fill-blue-600;
}
@ -1874,8 +1804,7 @@ body {
.yellow-darker.btn-svg,
.yellow-darker.menu-svg,
.yellow-darker.social-svg,
.yellow-darker.stat-svg
{
.yellow-darker.stat-svg {
@apply fill-yellow-600;
}
@ -1883,8 +1812,7 @@ body {
.gray-darker.btn-svg,
.gray-darker.menu-svg,
.gray-darker.social-svg,
.gray-darker.stat-svg
{
.gray-darker.stat-svg {
@apply fill-gray-600;
}
@ -1892,8 +1820,7 @@ body {
.dark-darker.btn-svg,
.dark-darker.menu-svg,
.dark-darker.social-svg,
.dark-darker.stat-svg
{
.dark-darker.stat-svg {
@apply fill-slate-600;
}
@ -1901,8 +1828,7 @@ body {
.amber-darker.btn-svg,
.amber-darker.menu-svg,
.amber-darker.social-svg,
.amber-darker.stat-svg
{
.amber-darker.stat-svg {
@apply fill-amber-600;
}
@ -1910,8 +1836,7 @@ body {
.emerald-darker.btn-svg,
.emerald-darker.menu-svg,
.emerald-darker.social-svg,
.emerald-darker.stat-svg
{
.emerald-darker.stat-svg {
@apply fill-emerald-600;
}
@ -1919,8 +1844,7 @@ body {
.teal-darker.btn-svg,
.teal-darker.menu-svg,
.teal-darker.social-svg,
.teal-darker.stat-svg
{
.teal-darker.stat-svg {
@apply fill-teal-600;
}
@ -1928,8 +1852,7 @@ body {
.indigo-darker.btn-svg,
.indigo-darker.menu-svg,
.indigo-darker.social-svg,
.indigo-darker.stat-svg
{
.indigo-darker.stat-svg {
@apply fill-indigo-600;
}
@ -1937,8 +1860,7 @@ body {
.cyan-darker.btn-svg,
.cyan-darker.menu-svg,
.cyan-darker.social-svg,
.cyan-darker.stat-svg
{
.cyan-darker.stat-svg {
@apply fill-cyan-600;
}
@ -1946,8 +1868,7 @@ body {
.sky-darker.btn-svg,
.sky-darker.menu-svg,
.sky-darker.social-svg,
.sky-darker.stat-svg
{
.sky-darker.stat-svg {
@apply fill-sky-700;
}
@ -1955,8 +1876,7 @@ body {
.pink-darker.btn-svg,
.pink-darker.menu-svg,
.pink-darker.social-svg,
.pink-darker.stat-svg
{
.pink-darker.stat-svg {
@apply fill-pink-600;
}
@ -1964,7 +1884,6 @@ body {
.lime-darker.btn-svg,
.lime-darker.menu-svg,
.lime-darker.social-svg,
.lime-darker.stat-svg
{
.lime-darker.stat-svg {
@apply fill-lime-600;
}
}

File diff suppressed because one or more lines are too long

View file

@ -46,31 +46,41 @@ import Stat from "@components/Widget/Stat.vue";
*/
const props = defineProps({
builder : {
type: Array,
required: true,
},
})
builder: {
type: Array,
required: true,
},
});
</script>
<template>
<!-- top level grid (layout) -->
<GridLayout v-for="(container, index) in props.builder" :key="index"
:gridLayoutClass="container.containerClass"
:type="container.type"
:title="container.title"
:columns="container.containerColumns">
<!-- widget grid -->
<Grid>
<!-- widget element -->
<template v-for="(widget, index) in container.widgets" :key="index">
<Checkbox v-if="widget.type === 'Checkbox'" v-bind="widget.data"></Checkbox>
<Select v-if="widget.type === 'Select'" v-bind="widget.data"></Select>
<Input v-if="widget.type === 'Input'" v-bind="widget.data"></Input>
<Datepicker v-if="widget.type === 'Datepicker'" v-bind="widget.data"></Datepicker>
<Button v-if="widget.type === 'Button'" v-bind="widget.data"></Button>
<Stat v-if="widget.type === 'Stat'" v-bind="widget.data"></Stat>
</template>
</Grid>
</GridLayout>
<!-- top level grid (layout) -->
<GridLayout
v-for="(container, index) in props.builder"
:key="index"
:gridLayoutClass="container.containerClass"
:type="container.type"
:title="container.title"
:link="container.link"
:columns="container.containerColumns"
>
<!-- widget grid -->
<Grid>
<!-- widget element -->
<template v-for="(widget, index) in container.widgets" :key="index">
<Checkbox
v-if="widget.type === 'Checkbox'"
v-bind="widget.data"
></Checkbox>
<Select v-if="widget.type === 'Select'" v-bind="widget.data"></Select>
<Input v-if="widget.type === 'Input'" v-bind="widget.data"></Input>
<Datepicker
v-if="widget.type === 'Datepicker'"
v-bind="widget.data"
></Datepicker>
<Button v-if="widget.type === 'Button'" v-bind="widget.data"></Button>
<Stat v-if="widget.type === 'Stat'" v-bind="widget.data"></Stat>
</template>
</Grid>
</GridLayout>
</template>

View file

@ -12,6 +12,10 @@ const header = reactive({
if (header.splitPath.length === 0) return "page";
return header.splitPath[header.splitPath.length - 1];
}),
lastPath: computed(() => {
if (header.splitPath.length === 0) return "page";
return header.splitPath[header.splitPath.length - 1];
}),
});
onMounted(() => {
@ -42,13 +46,15 @@ onMounted(() => {
<div class="header-wrap">
<nav>
<!-- breadcrumb -->
<h2 class="header-title">{{ header.currPath }}</h2>
<h2 class="header-title">
{{ $t(`dashboard_${header.currPath}`, header.currPath) }}
</h2>
<ul class="header-breadcrumb-container">
<li class="header-breadcrumb-item first">
{{ $t("dashboard_bw") }}
</li>
<li class="header-breadcrumb-item slash mobile active">
{{ header.splitPath[header.splitPath.length - 1] }}
{{ $t(`dashboard_${header.lastPath}`, header.lastPath) }}
</li>
</ul>
</nav>

View file

@ -1,5 +1,5 @@
<script setup>
import { computed } from 'vue';
import { computed, ref, onMounted } from "vue";
/**
@name Widget/GridLayout.vue
@ -16,55 +16,81 @@ import { computed } from 'vue';
}
@param {string} [type="card"] - Type of layout component, we can have : card, table, modal and others
@param {string} [title=""] - Title of the layout component, will be displayed at the top if exists. Type of layout component will determine the style of the title.
@param {string} [link=""] - Will transform the container tag from a div to an a tag with the link as href. Useful with card type.
@param {object} [columns={"pc": 12, "tablet": 12, "mobile": 12}] - Work with grid system { pc: 12, tablet: 12, mobile: 12}
@param {string} [gridLayoutClass="items-start"] - Additional class
*/
const props = defineProps({
type : {
type: String,
required: false,
default : "card"
type: {
type: String,
required: false,
default: "card",
},
title: {
type: String,
required: false,
default: "",
},
link: {
type: String,
required: false,
default: "",
},
columns: {
type: Object,
required: false,
default: {
pc: 12,
tablet: 12,
mobile: 12,
},
title : {
type: String,
required: false,
default : ""
},
columns : {
type: Object,
required: false,
default : {
pc: 12,
tablet: 12,
mobile: 12}
},
gridLayoutClass : {
type: String,
required: false,
default: "items-start"
},
})
},
gridLayoutClass: {
type: String,
required: false,
default: "items-start",
},
});
const containerClass = computed(() => {
if(props.type === 'card') return 'card';
return '';
})
if (props.type === "card") return "card";
return "";
});
const gridClass = computed(() => {
return `break-words grid grid-cols-12 w-full col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
})
return `break-words grid grid-cols-12 w-full col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
});
const titleClass = computed(() => {
if(props.type === 'card') return 'text-2xl font-bold mb-2';
return ''
})
if (props.type === "card") return "text-2xl font-bold mb-2";
return "";
});
const gridLayoutEl = ref();
onMounted(() => {
if (props.link) {
gridLayoutEl.value.setAttribute("href", props.link);
gridLayoutEl.value.setAttribute("rel", "noopener");
}
if (props.link && props.link.startsWith("http")) {
gridLayoutEl.value.setAttribute("target", "_blank");
}
});
</script>
<template>
<div data-grid-layout :class="[containerClass, gridClass, props.gridLayoutClass, 'p-4']">
<h1 v-if="props.title" :class="[titleClass, 'col-span-12']">{{ $t(props.title, props.title) }}</h1>
<component
ref="gridLayoutEl"
:is="props.link ? 'a' : 'div'"
data-grid-layout
:class="[containerClass, gridClass, props.gridLayoutClass, 'p-4']"
>
<h1 v-if="props.title" :class="[titleClass, 'col-span-12']">
{{ $t(props.title, props.title) }}
</h1>
<slot></slot>
</div>
</component>
</template>

View file

@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref } from 'vue';
import Icons from '@components/Widget/Icons.vue';
import { onMounted, ref } from "vue";
import Icons from "@components/Widget/Icons.vue";
/**
@name Widget/Stat.vue
@ -12,7 +12,7 @@ import Icons from '@components/Widget/Icons.vue';
title: "Total Users",
value: 100,
subtitle : "Last 30 days",
icon: "user",
iconName: "user",
iconColor: "sky",
link: "/users",
subtitleColor: "info",
@ -22,84 +22,76 @@ import Icons from '@components/Widget/Icons.vue';
@param {string} [subtitle=""] - The subtitle of the stat. Can be a translation key or by default raw text.
@param {string} [iconName=""] - A top-right icon to display between icon available in Icons/Stat. Case falsy value, no icon displayed. The icon name is the name of the file without the extension on lowercase.
@param {string} [iconColor="sky"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink, lime, rose, fuchsia, violet, lightBlue, warmGray, trueGray, coolGray, blueGray, white, black)
@param {string} [link=""] - A link to redirect when the card is clicked. Case falsy value, element is a div, , else it is an anchor.
@param {string} [subtitleColor="info"] - The color of the subtitle between error, success, warning, info
@param {string} [statClass=""] - Additional class
*/
const props = defineProps({
title: {
type: String,
required: true,
},
value: {
type: [String, Number],
required: true,
},
subtitle: {
type: String,
required: false,
default : ""
},
iconName: {
type: String,
required: false,
default : ""
},
iconColor: {
type: String,
required: false,
default: "sky",
},
link: {
type: String,
required: false,
default : ""
},
subtitleColor: {
type: String,
required: false,
default: "info",
},
statClass: {
type: String,
required: false,
default: "",
},
})
const statEl = ref();
onMounted(() => {
if (props.link) {
statEl.value.setAttribute('href', props.link);;
statEl.value.setAttribute('rel', 'noopener')
}
if(props.link && props.link.startsWith('http')) {
statEl.value.setAttribute('target', '_blank');
}
})
title: {
type: String,
required: true,
},
value: {
type: [String, Number],
required: true,
},
subtitle: {
type: String,
required: false,
default: "",
},
iconName: {
type: String,
required: false,
default: "",
},
iconColor: {
type: String,
required: false,
default: "sky",
},
subtitleColor: {
type: String,
required: false,
default: "info",
},
statClass: {
type: String,
required: false,
default: "",
},
});
</script>
<template>
<component ref="statEl" :is="props.link ? 'a' : 'div'"
:class="['stat-container', props.statClass]">
<!-- text -->
<div :class="['stat-content-container', props.iconName ? 'is-icon' : 'no-icon']">
<p class="stat-title">{{ $t(props.title, props.title) }}</p>
<!-- version of user -->
<h5 class="stat-value">{{ $t(props.value, props.value) }}</h5>
<p v-if="props.subtitle" :class="['stat-subtitle', props.subtitleColor]">{{ $t(props.subtitle, props.subtitle) }}</p>
</div>
<!-- end text -->
<!-- icon -->
<div v-if="props.iconName" role="img"
aria-label="version"
:class="['stat-svg-container', props.iconColor]">
<Icons :iconName="props.iconName" :iconClass="'stat-svg'" :iconColor="'white'" />
</div>
<!-- end icon -->
</component>
<template>
<div :class="['stat-container', props.statClass]">
<!-- text -->
<div
:class="[
'stat-content-container',
props.iconName ? 'is-icon' : 'no-icon',
]"
>
<p class="stat-title">{{ $t(props.title, props.title) }}</p>
<!-- version of user -->
<h5 class="stat-value">{{ $t(props.value, props.value) }}</h5>
<p v-if="props.subtitle" :class="['stat-subtitle', props.subtitleColor]">
{{ $t(props.subtitle, props.subtitle) }}
</p>
</div>
<!-- end text -->
<!-- icon -->
<div
v-if="props.iconName"
role="img"
aria-label="version"
:class="['stat-svg-container', props.iconColor]"
>
<Icons
:iconName="props.iconName"
:iconClass="'stat-svg'"
:iconColor="'white'"
/>
</div>
<!-- end icon -->
</div>
</template>

View file

@ -13,7 +13,7 @@
"dashboard_advanced": "advanced",
"dashboard_loading": "loading",
"dashboard_lang_dropdown_button_desc": "Toggle hide/show radio group (dropdown) to change langage.",
"dashboard_manage_account" : "manage account",
"dashboard_manage_account": "manage account",
"dashboard_home": "home",
"dashboard_instances": "instances",
"dashboard_global_config": "global config",
@ -27,8 +27,8 @@
"dashboard_reports": "reports",
"dashboard_cache": "cache",
"dashboard_logs": "logs",
"dashboard_feedback_toggle_sidebar" : "Toggle feedback sidebar.",
"dashboard_feedback_close_sidebar" : "Close feedback sidebar.",
"dashboard_feedback_toggle_sidebar": "Toggle feedback sidebar.",
"dashboard_feedback_close_sidebar": "Close feedback sidebar.",
"dashboard_feedback_title": "feedback",
"dashboard_feedback_subtitle": "BunkerWeb actions",
"dashboard_menu_toggle_sidebar": "Toggle menu sidebar.",
@ -72,22 +72,6 @@
"dashboard_banner_link_text_1": "Check BunkerWeb Panel",
"dashboard_banner_link_text_2": "demo wep app !",
"dashboard_banner_link_text_3": "website !",
"home_version_is_latest": "is the latest version",
"home_version_latest_version": "latest version",
"home_version": "version",
"home_internal": "internal",
"home_external": "external",
"home_card_link_label": "Redirect to page with related data.",
"instances_hostname": "hostname",
"instances_hostname_placeholder": "bwapi",
"instances_method": "method",
"instances_port": "port",
"instances_port_placeholder": "5000",
"instances_active": "Instance is active.",
"instances_inactive": "Instance is inactive.",
"instances_modal_delete_msg": "Are you sure to delete instance with hostname {hostname} ?",
"instances_server_name": "server name",
"instances_server_name_placeholder": "www.example.com",
"action_send": "send {name}",
"action_disable": "disable {name}",
"action_enable": "enable {name}",
@ -104,5 +88,20 @@
"action_ping": "ping {name}",
"action_reload": "reload {name}",
"action_upload": "upload {name}",
"action_delete_all": "delete all {name}"
"action_delete_all": "delete all {name}",
"home_version": "version",
"home_all_features_available": "all features are available",
"home_upgrade_pro": "upgrade to pro",
"home_pro": "pro",
"home_free": "free",
"home_version_number": "version number",
"home_latest_version": "latest version",
"home_upgrade_available": "upgrade available",
"home_instances": "instances",
"home_total_number": "total number",
"home_services": "services",
"home_all_methods_included": "all methods included",
"home_plugins": "plugins",
"home_no_error": "no error",
"home_errors_found": "errors found"
}

View file

@ -3,21 +3,123 @@ import { reactive, onBeforeMount } from "vue";
import DashboardLayout from "@components/Dashboard/Layout.vue";
import Builder from "@components/Builder.vue";
/**
@name Page/Home.vue
@description This component is the home page.
This page displays an overview of multiple stats related to BunkerWeb.
*/
const home = reactive({
builder : ""
})
builder: "",
});
onBeforeMount(() => {
// Get builder data
const dataAtt = 'data-server-builder';
const dataAtt = "data-server-builder";
const dataEl = document.querySelector(`[${dataAtt}]`);
const data = dataEl && !dataEl.getAttribute(dataAtt).includes(dataAtt) ? JSON.parse(dataEl.getAttribute(dataAtt)) : {};
const data =
dataEl && !dataEl.getAttribute(dataAtt).includes(dataAtt)
? JSON.parse(dataEl.getAttribute(dataAtt))
: {};
home.builder = data;
});
// const data = [
// {
// type: "card",
// link : "https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui"
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_version",
// subtitle: "home_all_features_available" if is_pro_version else "home_upgrade_pro",
// subtitleColor: "success" is is_pro_version else "warning",
// value: "home_pro" if is_pro_version else "home_free",
// iconName: "crown" if is_pro_version else "core",
// iconColor: "amber",
// },
// },
// ],
// },
// {
// type: "card",
// link: "https://github.com/bunkerity/bunkerweb",
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_version_number",
// subtitle: "home_latest_version" if is_latest_version else "home_upgrade_available",
// subtitleColor: "success" if is_latest_version else "warning",
// value: <current_version>,
// iconName: "wire",
// iconColor: "teal",
// },
// },
// ],
// },
// {
// type: "card",
// link: "/instances",
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_instances",
// subtitle: "home_total_number",
// subtitleColor: "info",
// value: "<instances_total>",
// iconName: "box",
// iconColor: "dark",
// },
// },
// ],
// },
// {
// type: "card",
// link: "/services",
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_services",
// subtitle: "home_all_methods_included",
// subtitleColor: "info",
// value: "<services_total>",
// iconName: "disk",
// iconColor: "orange",
// },
// },
// ],
// },
// {
// type: "card",
// link: "/plugins",
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_plugins",
// subtitle: "home_no_error" if all_plugins_ok else "home_errors_found",
// subtitleColor: "success" if all_plugins_ok else "error",
// value: "<plugins_total>",
// iconName: "puzzle",
// iconColor: "yellow",
// },
// },
// ],
// },
// ];
</script>
<template>
<DashboardLayout >
<DashboardLayout>
<Builder v-if="home.builder" :builder="home.builder" />
</DashboardLayout>
</template>

View file

@ -10,8 +10,12 @@
</head>
<body>
<div class="hidden" data-server-global='{"username" : "admin"}'></div>
<div class="hidden" data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'></div>
<div class="hidden" data-server-builder='[{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"Version","subtitle":"all features available","subtitleColor":"success","value":"PRO","iconName":"crown","iconColor":"amber"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"INSTANCES","subtitle":"1 ui, 1 autoconf","subtitleColor":"info","value":"2","iconName":"task","iconColor":"orange"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"PLUGINS","subtitle":"0 error","subtitleColor":"success","value":"48","iconName":"plus","iconColor":"blue"}}]}]'></div>
<div class="hidden"
data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'>
</div>
<div class="hidden"
data-server-builder='[{"type":"card", "link" : "/services", "containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat", "link": "https://github.com/bunkerity/bunkerweb","data":{"title":"home_version","subtitle":"home_all_features_available","subtitleColor":"success","value":"home_pro","iconName":"crown","iconColor":"amber"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_version_number","subtitle":"home_latest_version","subtitleColor":"success","value":"1.5.7","iconName":"wire","iconColor":"teal"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_instances","subtitle":"home_total_number","subtitleColor":"info", "value":"1","iconName":"box","iconColor":"dark"}}]}, {"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_services","subtitle":"home_all_methods_included","subtitleColor":"info","value":"2","iconName":"disk","iconColor":"orange"}}]}, {"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_plugins","subtitle":"home_no_error","subtitleColor":"success","value":"42","iconName":"puzzle","iconColor":"yellow"}}]}]'>
</div>
<div id="app"></div>
<script type="module" src="home.js"></script>
</body>