mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
fix icons uuid + remain text and list components
* fix icons uuid to avoid duplicate * start plugins page, builder and relate components * remove stat text to handle it on global Text component * ContentDetailList move to a dedicated List component folder and rename it as ListPairs to fit the output * start creating a ListDetails component that will handle a text part and popovers that will be use for plugin item * add new icons * change combobox modes by a select
This commit is contained in:
parent
9ce153ca0c
commit
eb14ae8e2e
36 changed files with 4851 additions and 195 deletions
|
|
@ -510,7 +510,15 @@ body {
|
|||
}
|
||||
|
||||
.popover-svg {
|
||||
@apply pointer-events-none z-0 hover:brightness-95 fill-blue-500 h-5 w-5;
|
||||
@apply pointer-events-none z-0 hover:brightness-95 fill-blue-500;
|
||||
}
|
||||
|
||||
.default.popover-svg {
|
||||
@apply h-7 w-7;
|
||||
}
|
||||
|
||||
.setting.popover-svg {
|
||||
@apply h-5 w-5;
|
||||
}
|
||||
|
||||
/* TABS */
|
||||
|
|
@ -974,28 +982,45 @@ body {
|
|||
@apply leading-normal mb-0 ;
|
||||
}
|
||||
|
||||
/* CONTENT DETAIL LSIT COMPONENT */
|
||||
.text-stat {
|
||||
@apply my-1 font-bold dark:text-white/90 text-black uppercase;
|
||||
}
|
||||
/* LIST COMPONENT */
|
||||
|
||||
.content-detail-list-container {
|
||||
.list-pairs-container {
|
||||
@apply grid grid-cols-12 gap-2 my-4;
|
||||
}
|
||||
|
||||
.content-detail-list-item {
|
||||
.list-pairs-item {
|
||||
@apply flex items-center col-span-1 py-1 sm:py-0;
|
||||
}
|
||||
|
||||
.content-detail-list-title {
|
||||
.list-pairs-title {
|
||||
@apply min-w-fit transition duration-300 ease-in-out font-bold mb-0 font-sans text-sm leading-normal uppercase dark:text-gray-500;
|
||||
}
|
||||
|
||||
.content-detail-list-subtitle {
|
||||
.list-pairs-subtitle {
|
||||
@apply min-w-[2rem] break-all transition duration-300 ease-in-out pl-3 col-span-1 mb-0 font-sans text-sm font-semibold leading-normal uppercase dark:text-gray-100;
|
||||
}
|
||||
|
||||
.content-stat {
|
||||
@apply my-1 font-bold dark:text-white/90 text-black uppercase;
|
||||
.list-details-container {
|
||||
@apply grid grid-cols-12 gap-2 my-4;
|
||||
}
|
||||
|
||||
.list-details {
|
||||
@apply py-3 min-h-12 relative col-span-12 sm:col-span-6 2xl:col-span-4 3xl:col-span-3 p-1 flex justify-between items-center transition rounded;
|
||||
}
|
||||
|
||||
.enabled.list-details {
|
||||
@apply bg-gray-100 hover:bg-gray-300 dark:bg-slate-700 dark:hover:bg-slate-800;
|
||||
}
|
||||
|
||||
.disabled.list-details {
|
||||
@apply cursor-not-allowed bg-gray-300 dark:bg-gray-800;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* TITLE */
|
||||
|
||||
.title-container {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
79
vuejs/client/src/components/Builder/Plugins.vue
Normal file
79
vuejs/client/src/components/Builder/Plugins.vue
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<script setup>
|
||||
// Containers
|
||||
import Grid from "@components/Widget/Grid.vue";
|
||||
import GridLayout from "@components/Widget/GridLayout.vue";
|
||||
import PluginBox from "@components/Widget/PluginBox.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
|
||||
/**
|
||||
@name Builder/pLugin.vue
|
||||
@description This component is lightweight builder containing only the necessary components to create the plugins page.
|
||||
@example
|
||||
[
|
||||
{
|
||||
type: "card",
|
||||
containerColumns: { pc: 12, tablet: 12, mobile: 12 },
|
||||
widgets: [
|
||||
{
|
||||
type: "Title",
|
||||
data : {
|
||||
title: "dashboard_plugins",
|
||||
type: "card"
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "PluginBox",
|
||||
data: {
|
||||
name: "Plugin name",
|
||||
flexClass : "justify-center",
|
||||
columns: { pc: 4, tablet: 6, mobile: 12 },
|
||||
containerClass: "mb-4",
|
||||
popovers: [
|
||||
{
|
||||
text: "This is a popover text",
|
||||
iconName: "info",
|
||||
iconColor: "info",
|
||||
},
|
||||
{
|
||||
text: "This is a popover text",
|
||||
iconName: "info",
|
||||
iconColor: "info",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@param {array} builder - Array of containers and widgets
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
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"
|
||||
:link="container.link"
|
||||
:columns="container.containerColumns"
|
||||
>
|
||||
<!-- widget grid -->
|
||||
<Grid>
|
||||
<!-- widget element -->
|
||||
<template v-for="(widget, index) in container.widgets" :key="index">
|
||||
<Title v-if="widget.type === 'Title'" v-bind="widget.data" />
|
||||
<PluginBox v-if="widget.type === 'PluginBox'" v-bind="widget.data" />
|
||||
</template>
|
||||
</Grid>
|
||||
</GridLayout>
|
||||
</template>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<script setup>
|
||||
import { computed, defineProps } from "vue";
|
||||
/**
|
||||
@name Content/DetailList.vue
|
||||
@description This component is used to display key value information in a list.
|
||||
@example
|
||||
{
|
||||
details : [{key: "Total Users", value: "100"}],
|
||||
itemColumns: {pc: 12, tablet: 12, mobile: 12}
|
||||
}
|
||||
@param {array} details - The list of key value information. The key and value can be a translation key or a raw text.
|
||||
@param {object} [itemColumns={pc: 12, tablet: 12, mobile: 12}] - Determine the position of the items in the grid system.
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
details: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
itemColumns: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: { pc: 12, tablet: 12, mobile: 12 },
|
||||
},
|
||||
});
|
||||
|
||||
const gridClass = computed(() => {
|
||||
return props.itemColumns
|
||||
? `col-span-${props.itemColumns.mobile} md:col-span-${props.itemColumns.tablet} lg:col-span-${props.itemColumns.pc}`
|
||||
: "";
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<ul v-if="props.details" :class="['content-detail-list-container']">
|
||||
<li
|
||||
v-for="item in props.details"
|
||||
:class="['content-detail-list-item', gridClass]"
|
||||
>
|
||||
<span class="content-detail-list-title">
|
||||
{{ $t(item["key"], item["key"]) }}
|
||||
</span>
|
||||
<span class="content-detail-list-subtitle">
|
||||
{{ $t(item["value"], item["value"]) }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
|
@ -3,6 +3,7 @@ import { reactive, defineProps, computed, onBeforeMount } from "vue";
|
|||
import Container from "@components/Widget/Container.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
import Subtitle from "@components/Widget/Subtitle.vue";
|
||||
import Select from "@components/Forms/Field/Select.vue";
|
||||
import Combobox from "@components/Forms/Field/Combobox.vue";
|
||||
import Advanced from "@components/Form/Advanced.vue";
|
||||
import Raw from "@components/Form/Raw.vue";
|
||||
|
|
@ -113,7 +114,7 @@ onBeforeMount(() => {
|
|||
:values="data.templates"
|
||||
@inp="data.currTemplateName = $event"
|
||||
/>
|
||||
<Combobox
|
||||
<Select
|
||||
v-if="data.modes.length > 1"
|
||||
v-bind="comboboxModes"
|
||||
:value="data.currModeName"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
/**
|
||||
@name Icons/Check.vue
|
||||
@description This component is a svg icon representing a check mark.
|
||||
|
|
@ -21,12 +21,16 @@ const props = defineProps({
|
|||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
default: "success",
|
||||
},
|
||||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Core.vue
|
||||
@description This component is a svg icon representing core plugin.
|
||||
|
|
@ -21,12 +21,16 @@ const props = defineProps({
|
|||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
default: "red",
|
||||
},
|
||||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
@ -37,10 +41,12 @@ const icon = reactive({
|
|||
:class="[props.iconClass, props.iconColor]"
|
||||
:aria-labelledby="icon.id"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-6"
|
||||
>
|
||||
<path
|
||||
d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z"
|
||||
d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Cross.vue
|
||||
@description This component is a svg icon representing a cross mark.
|
||||
|
|
@ -26,7 +26,11 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
|
||||
/**
|
||||
@name Icons/Crown.vue
|
||||
|
|
@ -22,12 +22,16 @@ const props = defineProps({
|
|||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
default: "amber",
|
||||
},
|
||||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Discord.vue
|
||||
@description This component is a svg icon representing Discord.
|
||||
|
|
@ -26,7 +26,11 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const props = defineProps({
|
|||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
default: "orange",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const props = defineProps({
|
|||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
default: "orange",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
56
vuejs/client/src/components/Icons/External.vue
Normal file
56
vuejs/client/src/components/Icons/External.vue
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<script setup>
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Core.vue
|
||||
@description This component is a svg icon representing core plugin.
|
||||
@example
|
||||
{
|
||||
iconColor: 'info',
|
||||
}
|
||||
@param {string} [iconClass=""]
|
||||
@param {string} [iconColor="info"] - 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...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "default-svg",
|
||||
},
|
||||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
},
|
||||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<span :id="icon.id" class="sr-only">{{ $t("icons_external_desc") }}</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
data-svg="external"
|
||||
role="img"
|
||||
:class="[props.iconClass, props.iconColor]"
|
||||
:aria-labelledby="icon.id"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm5.845 17.03a.75.75 0 0 0 1.06 0l3-3a.75.75 0 1 0-1.06-1.06l-1.72 1.72V12a.75.75 0 0 0-1.5 0v4.19l-1.72-1.72a.75.75 0 0 0-1.06 1.06l3 3Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Github.vue
|
||||
@description This component is a svg icon representing Github.
|
||||
|
|
@ -26,7 +26,11 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const props = defineProps({
|
|||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
default: "blue",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Linkedin.vue
|
||||
@description This component is a svg icon representing Linkedin.
|
||||
|
|
@ -26,7 +26,11 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Plus.vue
|
||||
@description This component is a svg icon representing addition (+).
|
||||
|
|
@ -21,12 +21,16 @@ const props = defineProps({
|
|||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
default: "success",
|
||||
},
|
||||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
42
vuejs/client/src/components/Icons/Redirect.vue
Normal file
42
vuejs/client/src/components/Icons/Redirect.vue
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<script setup>
|
||||
/**
|
||||
@name Icons/Redirect.vue
|
||||
@description This component is a svg icon representing redirect.
|
||||
@example
|
||||
{
|
||||
iconColor: 'info',
|
||||
}
|
||||
@param {string} [iconClass=""]
|
||||
@param {string} [iconColor="info"] - 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...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "default-svg",
|
||||
},
|
||||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<svg
|
||||
data-svg="redirect"
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
:class="[props.iconClass, props.iconColor]"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M15.75 2.25H21a.75.75 0 0 1 .75.75v5.25a.75.75 0 0 1-1.5 0V4.81L8.03 17.03a.75.75 0 0 1-1.06-1.06L19.19 3.75h-3.44a.75.75 0 0 1 0-1.5Zm-10.5 4.5a1.5 1.5 0 0 0-1.5 1.5v10.5a1.5 1.5 0 0 0 1.5 1.5h10.5a1.5 1.5 0 0 0 1.5-1.5V10.5a.75.75 0 0 1 1.5 0v8.25a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3V8.25a3 3 0 0 1 3-3h8.25a.75.75 0 0 1 0 1.5H5.25Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
/**
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Trespass.vue
|
||||
@description This component is a svg icon representing no trespassing.
|
||||
@example
|
||||
|
|
@ -21,12 +21,16 @@ const props = defineProps({
|
|||
iconColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "info",
|
||||
default: "error",
|
||||
},
|
||||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { defineProps, reactive } from "vue";
|
||||
import { defineProps, reactive, onMounted } from "vue";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
/**
|
||||
@name Icons/Twiiter.vue
|
||||
@description This component is a svg icon representing Twiiter.
|
||||
|
|
@ -26,7 +26,11 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const icon = reactive({
|
||||
id: uuidv4(),
|
||||
id: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
icon.id = useUUID(icon.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
66
vuejs/client/src/components/List/Details.vue
Normal file
66
vuejs/client/src/components/List/Details.vue
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<script setup>
|
||||
import Flex from "@components/Widget/Flex.vue";
|
||||
import PopoverGroup from "@components/Widget/PopoverGroup.vue";
|
||||
import Text from "@components/Widget/Text.vue";
|
||||
|
||||
/**
|
||||
@name List/Popovers.vue
|
||||
@description This component is a list of items separate on two columns : one for the title, and other for a list of popovers related to the plugin (type, link...)
|
||||
@example
|
||||
{
|
||||
details : [{
|
||||
title: "name",
|
||||
disabled : false,
|
||||
popovers: [
|
||||
{
|
||||
text: "This is a popover text",
|
||||
iconName: "info",
|
||||
iconColor: "info",
|
||||
},
|
||||
{
|
||||
text: "This is a popover text",
|
||||
iconName: "info",
|
||||
iconColor: "info",
|
||||
},
|
||||
],
|
||||
}]
|
||||
@param {string} details - List of details item that contains a title and a list of popovers. We can also add a disabled key to disable the item.
|
||||
@param {columns} [columns={pc: 4, tablet: 6, mobile: 12}] - Determine the position of the items in the grid system.
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
details: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
columns: {
|
||||
type: [Object, Boolean],
|
||||
required: false,
|
||||
default: { pc: 4, tablet: 6, mobile: 12 },
|
||||
},
|
||||
});
|
||||
|
||||
const gridClass = computed(() => {
|
||||
return `col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul v-if="props.details" :class="['list-details-container']">
|
||||
<li
|
||||
v-for="item in props.details"
|
||||
:class="[
|
||||
'list-details-item',
|
||||
gridClass,
|
||||
item.disabled ? 'disabled' : 'enabled',
|
||||
]"
|
||||
>
|
||||
<Flex :flexClass="'justify-between items-center'">
|
||||
<Text :tag="'p'" :text="props.name" />
|
||||
<div>
|
||||
<PopoverGroup :popovers="props.popovers" />
|
||||
</div>
|
||||
</Flex>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
42
vuejs/client/src/components/List/Pairs.vue
Normal file
42
vuejs/client/src/components/List/Pairs.vue
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<script setup>
|
||||
import { computed, defineProps } from "vue";
|
||||
/**
|
||||
@name List/Pairs.vue
|
||||
@description This component is used to display key value information in a list.
|
||||
@example
|
||||
{
|
||||
pairs : [{key: "Total Users", value: "100"}],
|
||||
columns: {pc: 12, tablet: 12, mobile: 12}
|
||||
}
|
||||
@param {array} pairs - The list of key value information. The key and value can be a translation key or a raw text.
|
||||
@param {object} [columns={pc: 12, tablet: 12, mobile: 12}] - Determine the position of the items in the grid system.
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
pairs: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
columns: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: { pc: 12, tablet: 12, mobile: 12 },
|
||||
},
|
||||
});
|
||||
|
||||
const gridClass = computed(() => {
|
||||
return `col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<ul v-if="props.pairs" :class="['list-pairs-container']">
|
||||
<li v-for="item in props.pairs" :class="['list-pairs-item', gridClass]">
|
||||
<span class="list-pairs-title">
|
||||
{{ $t(item["key"], item["key"]) }}
|
||||
</span>
|
||||
<span class="list-pairs-subtitle">
|
||||
{{ $t(item["value"], item["value"]) }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
|
@ -25,6 +25,8 @@ import Funnel from "@components/Icons/Funnel.vue";
|
|||
import Disks from "@components/Icons/Disks.vue";
|
||||
import Globe from "@components/Icons/Globe.vue";
|
||||
import Info from "@components/Icons/Info.vue";
|
||||
import Redirect from "@components/Icons/Redirect.vue";
|
||||
import External from "@components/Icons/External.vue";
|
||||
|
||||
import { computed } from "vue";
|
||||
|
||||
|
|
@ -104,6 +106,11 @@ const iconClass = computed(() => {
|
|||
:iconClass="iconClass"
|
||||
:iconColor="iconColor"
|
||||
/>
|
||||
<External
|
||||
v-if="props.iconName === 'external'"
|
||||
:iconClass="iconClass"
|
||||
:iconColor="iconColor"
|
||||
/>
|
||||
<Crown
|
||||
v-if="props.iconName === 'crown'"
|
||||
:iconClass="iconClass"
|
||||
|
|
@ -219,5 +226,10 @@ const iconClass = computed(() => {
|
|||
:iconClass="iconClass"
|
||||
:iconColor="iconColor"
|
||||
/>
|
||||
<Redirect
|
||||
v-if="props.iconName === 'redirect'"
|
||||
:iconClass="iconClass"
|
||||
:iconColor="iconColor"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
<script setup>
|
||||
import { reactive, ref, watch, defineProps, onMounted } from "vue";
|
||||
import { contentIndex } from "@utils/tabindex.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
import Icons from "@components/Widget/Icons.vue";
|
||||
import { set } from "@vueuse/core";
|
||||
|
||||
/**
|
||||
@name Widget/Popover.vue
|
||||
|
|
@ -11,14 +10,17 @@ import { set } from "@vueuse/core";
|
|||
@example
|
||||
{
|
||||
text: "This is a popover text",
|
||||
href: "#",
|
||||
iconName: "info",
|
||||
iconColor: "info",
|
||||
}
|
||||
@param {string} text - Content of the button. Can be a translation key or by default raw text.
|
||||
@param {string} text - Content of the popover. Can be a translation key or by default raw text.
|
||||
@param {string} [href="#"] - Link of the anchor. By default it is a # link.
|
||||
@param {string} iconName - Name in lowercase of icons store on /Icons. If falsy value, no icon displayed.
|
||||
@param {string} iconColor - Color of the icon between tailwind colors
|
||||
@param {string} [tag="button"] - By default it is a button tag, but we can use other tag like div in case of popover on another button
|
||||
@param {string} [tag="a"] - By default it is a anchor tag, but we can use other tag like div in case of popover on another anchor
|
||||
@param {string} [popoverClass=""] - Additional class for the popover container
|
||||
@param {string} [svgClass="default"] - Additional class for the svg icon. "default" or "setting" will change the icon size.
|
||||
@param {string|number} [tabId=contentIndex] - The tabindex of the field, by default it is the contentIndex
|
||||
*/
|
||||
|
||||
|
|
@ -27,6 +29,11 @@ const props = defineProps({
|
|||
type: String,
|
||||
required: false,
|
||||
},
|
||||
href: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "#",
|
||||
},
|
||||
iconName: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -35,17 +42,22 @@ const props = defineProps({
|
|||
type: String,
|
||||
required: false,
|
||||
},
|
||||
// Sometimes we can't have a button tag (like popover on another btn)
|
||||
// Sometimes we can't have an anchor tag
|
||||
tag: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "div",
|
||||
default: "a",
|
||||
},
|
||||
popoverClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
svgClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "default",
|
||||
},
|
||||
tabId: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -55,9 +67,9 @@ const props = defineProps({
|
|||
|
||||
// Determine popover need to be display
|
||||
const popover = reactive({
|
||||
id: "",
|
||||
isOpen: false,
|
||||
isHover: false,
|
||||
id: uuidv4(),
|
||||
});
|
||||
|
||||
const popoverContainer = ref();
|
||||
|
|
@ -68,10 +80,9 @@ function showPopover() {
|
|||
|
||||
// Position popover relative to btn
|
||||
const popoverBtnRect = popoverBtn.value.getBoundingClientRect();
|
||||
const popoverContainerRect = popoverContainer.value.getBoundingClientRect();
|
||||
|
||||
popoverContainer.value.style.right = `${
|
||||
window.innerWidth - popoverBtnRect.left - popoverBtnRect.width
|
||||
window.innerWidth - popoverBtnRect.left - popoverBtnRect.width / 1.5
|
||||
}px`;
|
||||
|
||||
// We need to take care of parent padding and margin that will affect dropdown position but aren't calculate in rect
|
||||
|
|
@ -103,7 +114,7 @@ function showPopover() {
|
|||
window.scrollY +
|
||||
popoverBtnRect.top -
|
||||
noRectParentHeight -
|
||||
popoverBtnRect.height * 2 -
|
||||
popoverBtnRect.height * 1.5 -
|
||||
80
|
||||
}px`;
|
||||
|
||||
|
|
@ -128,6 +139,12 @@ watch(popover, () => {
|
|||
});
|
||||
|
||||
onMounted(() => {
|
||||
popover.id = useUUID(popover.id);
|
||||
// Remove href if tag is not an anchor
|
||||
if (props.tag !== "a") {
|
||||
popoverBtn.value.removeAttribute("href");
|
||||
}
|
||||
|
||||
// Close select dropdown when clicked outside element
|
||||
window.addEventListener("click", (e) => {
|
||||
if (
|
||||
|
|
@ -139,14 +156,6 @@ onMounted(() => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// random num between 0 and 100 with floats
|
||||
const randomNum = Math.random() * 10;
|
||||
setTimeout(() => {
|
||||
popover.id = uuidv4();
|
||||
}, randomNum);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -157,7 +166,7 @@ onMounted(() => {
|
|||
:aria-expanded="popover.isOpen ? 'true' : 'false'"
|
||||
:aria-labelledby="`${popover.id}-popover-text`"
|
||||
:is="props.tag"
|
||||
role="button"
|
||||
href="#"
|
||||
@click.prevent
|
||||
@focusin="showPopover()"
|
||||
@focusout="hidePopover()"
|
||||
|
|
@ -166,7 +175,7 @@ onMounted(() => {
|
|||
:class="['popover-btn', props.popoverClass]"
|
||||
>
|
||||
<Icons
|
||||
:iconClass="'popover-svg'"
|
||||
:iconClass="`popover-svg ${props.svgClass}`"
|
||||
:iconName="props.iconName"
|
||||
:iconColor="props.iconColor"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||
We need a list of popovers to display.
|
||||
@example
|
||||
{
|
||||
id: "group-popover",
|
||||
popoverClass : "justify-center",
|
||||
flexClass : "justify-center",
|
||||
popovers: [
|
||||
{
|
||||
text: "This is a popover text",
|
||||
|
|
@ -25,7 +24,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||
},
|
||||
],
|
||||
}
|
||||
@param {string} [flexClass="justify-center align-center"] - Additional class for the flex container
|
||||
@param {string} [flexClass="justify-center items-start"] - Additional class for the flex container
|
||||
@param {array} popovers - List of popovers to display. Popover component is used.
|
||||
*/
|
||||
|
||||
|
|
@ -38,7 +37,7 @@ const props = defineProps({
|
|||
flexClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "justify-center align-center",
|
||||
default: "justify-center items-start",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import Container from "@components/Widget/Container.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
import Subtitle from "@components/Widget/Subtitle.vue";
|
||||
import ContentStat from "@components/Content/Stat.vue";
|
||||
import Text from "@components/Widget/Text.vue";
|
||||
import Icons from "@components/Widget/Icons.vue";
|
||||
|
||||
/**
|
||||
|
|
@ -75,7 +75,7 @@ const props = defineProps({
|
|||
]"
|
||||
>
|
||||
<Title :tag="'h3'" type="stat" :title="props.title" />
|
||||
<ContentStat :stat="props.stat" />
|
||||
<Text :text="props.stat" :textClass="'text-stat'" />
|
||||
<Subtitle
|
||||
type="stat"
|
||||
v-if="props.subtitle"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
textClass: "text-3xl"
|
||||
}
|
||||
@param {string} text - The text value. Can be a translation key or by default raw text.
|
||||
@param {string} [textClass=""] - Additional class if needed
|
||||
@param {string} [textClass="text-content"] - Style of text. Can be replace by any class starting by 'text-' like 'text-stat'.
|
||||
@param {string} [tag="p"] - The tag of the text. Can be p, span, div, h1, h2, h3, h4, h5, h6
|
||||
*/
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ const props = defineProps({
|
|||
textClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
default: "text-content",
|
||||
},
|
||||
tag: {
|
||||
type: String,
|
||||
|
|
@ -31,7 +31,7 @@ const props = defineProps({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="props.tag" :class="['text-content', props.textClass]">
|
||||
<component :is="props.tag" :class="[props.textClass]">
|
||||
{{ $t(props.text, props.text) }}
|
||||
</component>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@
|
|||
"icons_cross_desc": "Cross icon representing a close, delete, error or cancel state.",
|
||||
"icons_check_desc": "Check icon representing a success, valid or active state.",
|
||||
"icons_core_desc": "Core icon representing a core setting or plugin.",
|
||||
"icons_external_desc": "External icon representing an external setting or plugin.",
|
||||
"icons_crown_desc": "Crown icon representing premium feature like a pro plugin.",
|
||||
"icons_plus_desc": "Plus icon representing an add, create or new state.",
|
||||
"icons_trespass_desc": "Trespass icon representing a ban, disable, lock or negative state.",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,6 @@
|
|||
<script setup>
|
||||
import { reactive, onBeforeMount, onMounted } from "vue";
|
||||
import { useGlobal } from "@utils/global.js";
|
||||
import { useForm } from "@utils/form.js";
|
||||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import BuilderHome from "@components/Builder/Home.vue";
|
||||
|
||||
|
|
@ -28,7 +27,6 @@ onBeforeMount(() => {
|
|||
|
||||
onMounted(() => {
|
||||
useGlobal();
|
||||
useForm();
|
||||
});
|
||||
|
||||
// const data = [
|
||||
|
|
|
|||
95
vuejs/client/src/pages/plugins/Plugins.vue
Normal file
95
vuejs/client/src/pages/plugins/Plugins.vue
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<script setup>
|
||||
import { reactive, onBeforeMount, onMounted } from "vue";
|
||||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import BuilderPlugins from "@components/Builder/Plugins.vue";
|
||||
import { useGlobal } from "@utils/global.js";
|
||||
|
||||
/**
|
||||
@name Page/PLugins.vue
|
||||
@description This component is the plugin page.
|
||||
This page displays global information about plugins, and allow to delete or upload some plugins.
|
||||
*/
|
||||
|
||||
const plugins = reactive({
|
||||
builder: "",
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
// Get builder data
|
||||
const dataAtt = "data-server-builder";
|
||||
const dataEl = document.querySelector(`[${dataAtt}]`);
|
||||
const data =
|
||||
dataEl && !dataEl.getAttribute(dataAtt).includes(dataAtt)
|
||||
? JSON.parse(dataEl.getAttribute(dataAtt))
|
||||
: {};
|
||||
plugins.builder = data;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
useGlobal();
|
||||
});
|
||||
|
||||
const builder = [
|
||||
{
|
||||
type: "card",
|
||||
containerColumns: { pc: 12, tablet: 12, mobile: 12 },
|
||||
widgets: [
|
||||
{
|
||||
type: "Title",
|
||||
data: {
|
||||
title: "dashboard_plugins",
|
||||
type: "card",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "PluginBox",
|
||||
data: {
|
||||
name: "Plugin name",
|
||||
columns: { pc: 4, tablet: 6, mobile: 12 },
|
||||
popovers: [
|
||||
{
|
||||
text: "External plugin",
|
||||
iconName: "external",
|
||||
iconColor: "info",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "PluginBox",
|
||||
data: {
|
||||
name: "Plugin name",
|
||||
columns: { pc: 4, tablet: 6, mobile: 12 },
|
||||
popovers: [
|
||||
{
|
||||
text: "Pro plugin",
|
||||
iconName: "crown",
|
||||
iconColor: "amber",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "PluginBox",
|
||||
data: {
|
||||
name: "Plugin name",
|
||||
columns: { pc: 4, tablet: 6, mobile: 12 },
|
||||
popovers: [
|
||||
{
|
||||
text: "Core plugin",
|
||||
iconName: "core",
|
||||
iconColor: "red",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardLayout>
|
||||
<BuilderPlugins v-if="builder" :builder="builder" />
|
||||
</DashboardLayout>
|
||||
</template>
|
||||
22
vuejs/client/src/pages/plugins/index.html
Normal file
22
vuejs/client/src/pages/plugins/index.html
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
|
||||
<link rel="stylesheet" href="/css/style.css" />
|
||||
<link rel="stylesheet" href="/css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | Plugins</title>
|
||||
</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","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"bunkerweb"},{"key":"instances_type","value":"manual"},{"key":"instances_status","value":"instances_active"}],"status":"success","title":"bunkerweb","buttons":[{"attrs":{"data-form-INSTANCE_ID":"bunkerweb","data-form-operation":"reload","data-submit-form":"true"},"text":"action_reload","color":"warning","size":"normal"},{"attrs":{"data-form-INSTANCE_ID":"bunkerweb","data-form-operation":"stop","data-submit-form":"true"},"text":"action_stop","color":"error","size":"normal"}]}}]}]'>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="plugins.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
11
vuejs/client/src/pages/plugins/plugins.js
Normal file
11
vuejs/client/src/pages/plugins/plugins.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import { getI18n } from "@utils/lang.js";
|
||||
import Plugins from "./Plugins.vue";
|
||||
|
||||
const pinia = createPinia();
|
||||
|
||||
createApp(Plugins)
|
||||
.use(pinia)
|
||||
.use(getI18n(["dashboard", "action", "inp", "icons", "plugins"]))
|
||||
.mount("#app");
|
||||
|
|
@ -67,7 +67,10 @@ function isElHidden(el) {
|
|||
|
||||
/**
|
||||
@name useUUID
|
||||
@description This function return a unique identifier uuidv4 after waiting some random time.
|
||||
@description This function return a unique identifier using uuidv4 and a random number.
|
||||
Adding random number to avoid duplicate uuids when some components are rendered at the same time.
|
||||
We can pass a possible existing id, the function will only generate one if the id is empty.
|
||||
@param {String} [id=""] - Possible existing id, check if it's empty to generate a new one.
|
||||
*/
|
||||
function useUUID(id = "") {
|
||||
if (id) return id;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
3216
vuejs/test.py
3216
vuejs/test.py
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue