first commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="sm:col-span-2 md:grow">
|
||||
<!--Фильтр-->
|
||||
<div class="flex justify-end gap-x-2">
|
||||
<div class="hs-dropdown relative inline-block [--placement:bottom-right]"
|
||||
data-hs-dropdown-auto-close="inside">
|
||||
<button id="hs-as-table-table-filter-dropdown" type="button"
|
||||
class="py-2 px-3 inline-flex justify-center items-center gap-2 rounded-md border font-medium bg-white text-gray-700 shadow-sm align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white focus:ring-blue-600 transition-all text-sm dark:bg-slate-900 dark:hover:bg-slate-800 dark:border-gray-700 dark:text-gray-400 dark:hover:text-white dark:focus:ring-offset-gray-800">
|
||||
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" width="16"
|
||||
height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
Фильтр
|
||||
<span
|
||||
class="pl-2 text-xs font-semibold text-blue-600 border-l border-gray-200 dark:border-gray-700 dark:text-blue-500">
|
||||
1
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 hidden mt-2 divide-y divide-gray-200 min-w-[12rem] z-10 bg-white shadow-md rounded-lg mt-2 dark:divide-gray-700 dark:bg-gray-800 dark:border dark:border-gray-700"
|
||||
aria-labelledby="hs-as-table-table-filter-dropdown">
|
||||
<div class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<label for="hs-radio-group-1" class="flex py-2.5 px-3">
|
||||
<input type="radio" name="hs-radio-group"
|
||||
class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800"
|
||||
id="hs-radio-group-1" checked>
|
||||
<span class="ml-3 text-sm text-gray-800 dark:text-gray-200">Все</span>
|
||||
</label>
|
||||
<label for="hs-radio-group-2" class="flex py-2.5 px-3">
|
||||
<input type="radio" name="hs-radio-group"
|
||||
class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800"
|
||||
id="hs-radio-group-2">
|
||||
<span class="ml-3 text-sm text-gray-800 dark:text-gray-200">Опубликованы</span>
|
||||
</label>
|
||||
<label for="hs-radio-group-3" class="flex py-2.5 px-3">
|
||||
<input disabled type="radio" name="hs-radio-group"
|
||||
class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800"
|
||||
id="hs-radio-group-3">
|
||||
<span class="ml-3 text-sm text-gray-800 dark:text-gray-200">На рассмотрении</span>
|
||||
</label>
|
||||
<label for="hs-radio-group-4" class="flex py-2.5 px-3">
|
||||
<input type="radio" name="hs-radio-group"
|
||||
class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800"
|
||||
id="hs-radio-group-4">
|
||||
<span class="ml-3 text-sm text-gray-800 dark:text-gray-200">Не активны</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Конец Фильтра-->
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "AdminIndexFilter",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
props: [
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="px-6 py-4 grid gap-3 md:flex md:justify-between md:items-center border-t border-gray-200 dark:border-gray-700">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AdminIndexFooter",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="px-4 py-4 gap-3 flex justify-center md:items-center border-gray-200">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AdminIndexHeader"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200">
|
||||
{{ title }}
|
||||
</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AdminIndexHeaderTitle",
|
||||
props: [
|
||||
'title'
|
||||
]
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="sm:col-span-1 w-full">
|
||||
<label for="hs-as-table-product-review-search" class="sr-only">Поиск</label>
|
||||
<div class="relative">
|
||||
<input autocomplete="off" @input="search" v-model="searchInput" type="text"
|
||||
id="hs-as-table-product-review-search"
|
||||
name="hs-as-table-product-review-search"
|
||||
class="py-2 px-3 pl-11 block flex-1 w-full border-gray-200 shadow-sm rounded-md text-sm focus:z-10 focus:border-blue-500 focus:ring-blue-500 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400"
|
||||
placeholder="Поиск">
|
||||
<div
|
||||
class="absolute inset-y-0 left-0 flex items-center pointer-events-none pl-4">
|
||||
<svg class="h-4 w-4 text-gray-400" xmlns="http://www.w3.org/2000/svg"
|
||||
width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { debounce } from "lodash/function.js";
|
||||
|
||||
export default {
|
||||
name: "AdminIndexSearch",
|
||||
data() {
|
||||
return {
|
||||
searchInput: this.$page.props.filters.search,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
search: debounce(function () {
|
||||
if(this.searchInput == "") {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('search');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl,{
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
replace: true,
|
||||
});
|
||||
} else {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl,{
|
||||
method: 'get',
|
||||
data: {
|
||||
search: this.searchInput,
|
||||
},
|
||||
preserveState: true,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
}, 500),
|
||||
|
||||
},
|
||||
props: [
|
||||
'filters'
|
||||
],
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
:fill="icon.fill || 'none'"
|
||||
:viewBox="icon.viewBox || this.viewBox || '0 0 24 24'"
|
||||
:stroke-width="icon.stroke_width || this.stroke_width || 1.5"
|
||||
:stroke="icon.stroke || 'currentColor'"
|
||||
:class="$attrs.class || 'w-6 h-6'"
|
||||
v-html="icon.path"
|
||||
>
|
||||
</svg>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import icons from "@/Components/other/icons.js";
|
||||
|
||||
export default {
|
||||
name: "BaseIcon",
|
||||
data() {
|
||||
return {
|
||||
icon: icons[this.name] || icons['file']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
viewBox: {
|
||||
type: String,
|
||||
},
|
||||
stroke_width: {
|
||||
type: Number,
|
||||
},
|
||||
fill: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<template v-for="file in block.data.file">
|
||||
<div class="">
|
||||
<a class="" :href="'/storage/'+ file.path" download type="button">
|
||||
|
||||
<div class="flex border rounded-lg px-4 py-2 items-center justify-between duration-300 hover:bg-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="w-[30px] h-[30px] bg-black flex justify-center items-center rounded-md mr-2">
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.9375 12.2188H12.75C12.4682 12.2188 12.198 12.1068 11.9987 11.9076C11.7994 11.7083 11.6875 11.438 11.6875 11.1562V5.84375C11.6875 5.56196 11.7994 5.29171 11.9987 5.09245C12.198 4.89319 12.4682 4.78125 12.75 4.78125H15.9375V5.84375H12.75V11.1562H15.9375V12.2188ZM9.5625 12.2188H7.4375C7.15571 12.2188 6.88546 12.1068 6.6862 11.9076C6.48694 11.7083 6.375 11.438 6.375 11.1562V5.84375C6.375 5.56196 6.48694 5.29171 6.6862 5.09245C6.88546 4.89319 7.15571 4.78125 7.4375 4.78125H9.5625C9.84429 4.78125 10.1145 4.89319 10.3138 5.09245C10.5131 5.29171 10.625 5.56196 10.625 5.84375V11.1562C10.625 11.438 10.5131 11.7083 10.3138 11.9076C10.1145 12.1068 9.84429 12.2188 9.5625 12.2188ZM7.4375 5.84375V11.1562H9.5625V5.84375H7.4375ZM3.1875 12.2188H1.0625V4.78125H3.1875C3.75087 4.78195 4.29096 5.00606 4.68933 5.40442C5.08769 5.80279 5.3118 6.34288 5.3125 6.90625V10.0938C5.3118 10.6571 5.08769 11.1972 4.68933 11.5956C4.29096 11.9939 3.75087 12.218 3.1875 12.2188ZM2.125 11.1562H3.1875C3.46929 11.1562 3.73954 11.0443 3.9388 10.8451C4.13806 10.6458 4.25 10.3755 4.25 10.0938V6.90625C4.25 6.62446 4.13806 6.35421 3.9388 6.15495C3.73954 5.95569 3.46929 5.84375 3.1875 5.84375H2.125V11.1562Z" fill="#F8F8F8"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>{{ file.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "FileBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="md:font-bold md:text-xl text-lg font-medium text-gray-800">{{ block.data.content }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "HeadingBlock",
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<img @click="toggler = !toggler" loading="lazy" class="mx-auto object-cover rounded-md hover:opacity-95 hover:duration-200 transition" :src="'/storage/' + block.data.url" alt="">
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.url]"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "ImageBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="ms-4 mt-2 w-full border px-4 py-4 rounded-xl shadow-sm">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="w-full px-2 sm:px-3 lg:px-4mx-auto">
|
||||
<!-- Grid -->
|
||||
<a class="group flex flex-col bg-white border shadow-sm rounded-xl hover:shadow-md focus:outline-none focus:shadow-md transition"
|
||||
:href="(page.is_url) ? page.path : route('page.view', page.path) + '/'">
|
||||
<div class="p-4 md:p-5">
|
||||
<div class="flex items-center gap-x-5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mt-1 shrink-0 size-7 text-gray-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
|
||||
</svg>
|
||||
<div class="grow">
|
||||
<ol v-if="breadcrumbs" class="flex items-center whitespace-nowrap">
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.page }}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
<h3 class="mt-1 group-hover:text-blue-600 font-semibold text-gray-700">
|
||||
{{ page.title }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<!-- End Grid -->
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PageItemBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
page: null,
|
||||
breadcrumbs: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPage(id) {
|
||||
axios.get(route('client.widget.page.single', id))
|
||||
.then(response => {
|
||||
this.page = response.data.data.page;
|
||||
this.breadcrumbs = response.data.data.breadcrumbs;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPage(this.block.data.page)
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="text-sm text-gray-600 leading-6 md:text-[16px] md:text-[#374151] md:leading-8 md:font-light paragraph-container" v-html="wrapTables(block).data.content" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "ParagraphBlock",
|
||||
methods: {
|
||||
wrapTables(data) {
|
||||
if (data.type === 'paragraph' && data.data && data.data.content) {
|
||||
// Используем регулярное выражение для поиска всех таблиц
|
||||
const wrappedContent = data.data.content.replace(/<table([^>]*)>([\s\S]*?)<\/table>/g, (match, attrs, content) => {
|
||||
return `<div class="div-table"><table${attrs}>${content}</table></div>`;
|
||||
});
|
||||
|
||||
// Возвращаем новый объект с обновленным контентом
|
||||
return {
|
||||
...data,
|
||||
data: {
|
||||
...data.data,
|
||||
content: wrappedContent
|
||||
}
|
||||
};
|
||||
}
|
||||
return data; // Если тип не 'paragraph', возвращаем объект без изменений
|
||||
}
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.paragraph-container a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container p {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.paragraph-container ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.paragraph-container li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
|
||||
.paragraph-container ul {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container hr {
|
||||
@apply my-4
|
||||
}
|
||||
|
||||
.paragraph-container strong {
|
||||
@apply text-xl
|
||||
}
|
||||
|
||||
.div-table {
|
||||
@apply overflow-x-auto
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container table {
|
||||
@apply w-full border-collapse mt-4 mb-4 overflow-hidden; /* Ширина 100%, стыковка границ, отступы, закругленные края */
|
||||
}
|
||||
|
||||
.paragraph-container th, .paragraph-container td {
|
||||
@apply border border-gray-300 p-3 text-left; /* Границы, отступы, выравнивание текста */
|
||||
}
|
||||
|
||||
.paragraph-container th {
|
||||
@apply bg-gray-100 text-gray-800 font-semibold; /* Фон заголовка, цвет текста, жирный шрифт */
|
||||
}
|
||||
|
||||
.paragraph-container tr {
|
||||
@apply transition-colors duration-200; /* Плавный переход цветов */
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-200; /* Фон строки при наведении */
|
||||
}
|
||||
|
||||
.paragraph-container td {
|
||||
@apply text-gray-600; /* Цвет текста ячеек */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="w-full rounded-xl mb-4 p-4 md:p-6 bg-white border border-gray-200 dark:bg-slate-900 dark:border-gray-700">
|
||||
<div class="flex items-center gap-x-4">
|
||||
<img @click="toggler = !toggler" loading="lazy" class="rounded-xl w-[150px]" :src="'/storage/' + block.data.photo" alt="Image Description">
|
||||
<div class="grow">
|
||||
<p class="font-medium text-gray-800 hover:text-gray-500">
|
||||
{{ block.data.name }}
|
||||
</p>
|
||||
<template v-for="item in block.data.info">
|
||||
<p class="text-xs text-gray-500 mt-2">
|
||||
{{ item.column }}: {{ item.content }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Social Brands -->
|
||||
<!-- End Social Brands -->
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.photo]"/>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "PersonBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.data.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.data.preview ? 'storage/images/' + post.data.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.data.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
post: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPost(id) {
|
||||
axios.get(route('client.widget.post.single', id), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.post = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPost(this.block.data.post);
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<template v-for="post in posts.data" :key="post.id">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.preview ? 'storage/images/' + post.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</template>
|
||||
<div class="flex justify-center">
|
||||
<a :href="route('client.post.index', { category: block.data.category })" class="group inline-flex items-center gap-x-1 text-sm font-semibold text-[#1A5AAF]">
|
||||
Все новости
|
||||
<svg class="flex-shrink-0 size-4 transition ease-in-out group-hover:translate-x-1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
posts: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPosts() {
|
||||
axios.get(route('client.widget.post.index'), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.posts = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPosts();
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(step, index) in block.data.steps">
|
||||
<div class="flex gap-x-3">
|
||||
<div class="w-16 text-end min-w-[4rem]">
|
||||
<span class="text-xs text-gray-500">{{ block.data.step_name }} {{ index + 1 }}</span>
|
||||
</div>
|
||||
<div class="relative last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200">
|
||||
<div class="relative z-10 size-7 flex justify-center items-center">
|
||||
<div class="size-2 rounded-full bg-primaryBlue"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grow max-w-[70%] pt-0.5 pb-8 overflow-wrap break-words">
|
||||
<h3 class="flex gap-x-1.5 font-semibold text-gray-800">
|
||||
{{ step.title }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-600 step-content" v-html="step.content" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "StepperBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.step-content a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.step-content a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
|
||||
.step-content ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.step-content ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.step-content li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
|
||||
<div class="">
|
||||
<nav class="-mb-0.5 flex justify-center gap-x-2" aria-label="Tabs" role="tablist" aria-orientation="horizontal">
|
||||
<button
|
||||
v-for="(tab, index) in block.data.tab" type="button"
|
||||
class="hs-tab-active:bg-gray-100 rounded-md hs-tab-active:text-gray-700 py-1.5 px-3 inline-flex items-center gap-x-2 border-b-2 border-transparent text-sm whitespace-nowrap text-gray-500 focus:outline-none disabled:opacity-50 disabled:pointer-events-none"
|
||||
:class="(activeTab === index) ? 'active' : ''"
|
||||
@click="activeTab = index"
|
||||
:id="generateSlug(tab.title) + '-item'"
|
||||
:data-hs-tab="'#' + generateSlug(tab.title)"
|
||||
aria-selected="false"
|
||||
:aria-controls="generateSlug(tab.title)" role="tab">
|
||||
{{ tab.title }}
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<template v-for="(tab, index) in block.data.tab">
|
||||
|
||||
<div :id="generateSlug(tab.title)" :class="(activeTab === index) ? '' : 'hidden'"
|
||||
role="tabpanel" :aria-labelledby="generateSlug(tab.title) + '-item'">
|
||||
<PageTabBuilder :blocks="tab.content" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import PageTabBuilder from "@/Components/BuilderUi/Pages/PageTabBuilder.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "TabBlock",
|
||||
components: {
|
||||
PageTabBuilder
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<video class="h-full w-full rounded-lg" controls>
|
||||
<source
|
||||
:src="domainPath + '/storage/' + block.data.path"
|
||||
:type="block.data.mime"
|
||||
/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<figcaption class="mt-3 text-sm text-center text-gray-500 dark:text-neutral-500">
|
||||
{{ block.data.title }}
|
||||
</figcaption>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "VideoBlock",
|
||||
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
|
||||
<a href="#" @click.prevent="this.back" class="inline-flex items-center gap-x-1.5 text-sm text-gray-600 decoration-2 hover:underline dark:text-blue-500">
|
||||
<svg class="flex-shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
|
||||
{{ title }}
|
||||
</a>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "DepartmentBackButton",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
back() {
|
||||
if (this.$page.props.urlPrev !== 'empty') {
|
||||
this.$inertia.visit(this.$page.props.urlPrev);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HeadingBlock from "@/Components/BuilderUi/Departments/Blocks/HeadingBlock.vue";
|
||||
import ParagraphBlock from "@/Components/BuilderUi/Departments/Blocks/ParagraphBlock.vue";
|
||||
import ClientImageSlider from "@/Components/ClientImageSlider.vue";
|
||||
import ImageBlock from "@/Components/BuilderUi/Departments/Blocks/ImageBlock.vue";
|
||||
import FileBlock from "@/Components/BuilderUi/Departments/Blocks/FileBlock.vue";
|
||||
import PersonBlock from "@/Components/BuilderUi/Departments/Blocks/PersonBlock.vue";
|
||||
import StepperBlock from "@/Components/BuilderUi/Departments/Blocks/StepperBlock.vue";
|
||||
import VideoBlock from "@/Components/BuilderUi/Departments/Blocks/VideoBlock.vue";
|
||||
import TabBlock from "@/Components/BuilderUi/Departments/Blocks/TabBlock.vue";
|
||||
import PostListBlock from "@/Components/BuilderUi/Departments/Blocks/PostListBlock.vue";
|
||||
import PageItemBlock from "@/Components/BuilderUi/Departments/Blocks/PageItemBlock.vue";
|
||||
import PostItemBlock from "@/Components/BuilderUi/Departments/Blocks/PostItemBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "DepartmentBuilder",
|
||||
components: {
|
||||
FileBlock,
|
||||
ImageBlock,
|
||||
ClientImageSlider,
|
||||
ParagraphBlock,
|
||||
HeadingBlock,
|
||||
PersonBlock,
|
||||
StepperBlock,
|
||||
VideoBlock,
|
||||
TabBlock,
|
||||
PostListBlock,
|
||||
PageItemBlock,
|
||||
PostItemBlock},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
heading: 'HeadingBlock',
|
||||
paragraph: 'ParagraphBlock',
|
||||
images: 'ClientImageSlider',
|
||||
image: 'ImageBlock',
|
||||
files: 'FileBlock',
|
||||
person: 'PersonBlock',
|
||||
stepper: 'StepperBlock',
|
||||
video: 'VideoBlock',
|
||||
tabs: 'TabBlock',
|
||||
postsList: 'PostListBlock',
|
||||
postItem: 'PostItemBlock',
|
||||
pageItem: 'PageItemBlock',
|
||||
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import HeadingBlock from "@/Components/BuilderUi/Departments/Blocks/HeadingBlock.vue";
|
||||
import ParagraphBlock from "@/Components/BuilderUi/Departments/Blocks/ParagraphBlock.vue";
|
||||
import ClientImageSlider from "@/Components/ClientImageSlider.vue";
|
||||
import ImageBlock from "@/Components/BuilderUi/Departments/Blocks/ImageBlock.vue";
|
||||
import FileBlock from "@/Components/BuilderUi/Departments/Blocks/FileBlock.vue";
|
||||
import PersonBlock from "@/Components/BuilderUi/Departments/Blocks/PersonBlock.vue";
|
||||
import StepperBlock from "@/Components/BuilderUi/Departments/Blocks/StepperBlock.vue";
|
||||
import VideoBlock from "@/Components/BuilderUi/Departments/Blocks/VideoBlock.vue";
|
||||
import PostListBlock from "@/Components/BuilderUi/Departments/Blocks/PostListBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "DepartmentTabBuilder",
|
||||
components: {FileBlock, ImageBlock, ClientImageSlider, ParagraphBlock, HeadingBlock, Link, PersonBlock, StepperBlock, VideoBlock, PostListBlock},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
heading: 'HeadingBlock',
|
||||
paragraph: 'ParagraphBlock',
|
||||
images: 'ClientImageSlider',
|
||||
image: 'ImageBlock',
|
||||
files: 'FileBlock',
|
||||
person: 'PersonBlock',
|
||||
stepper: 'StepperBlock',
|
||||
video: 'VideoBlock',
|
||||
postsList: 'PostListBlock',
|
||||
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
|
||||
<h1 class="text-brand-primary text-2xl mb-3 mt-2 md:text-3xl font-semibold tracking-tight dark:text-white lg:text-[40px] lg:leading-tight">
|
||||
{{ header }}
|
||||
</h1>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "DepartmentTitle",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
header: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<span v-if="this.filter.value !== null" class="inline-flex items-center gap-x-1.5 py-1.5 ps-3 pe-2 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
||||
{{ (filter.value.length > 1) ? filter.value.length + ' категории' : filter.content[filter.value[0]].data.title }}
|
||||
<button @click.prevent="clearFilter" type="button" class="shrink-0 size-4 inline-flex items-center justify-center rounded-full hover:bg-blue-200 focus:outline-none focus:bg-blue-200 focus:text-blue-500">
|
||||
<span class="sr-only">Remove badge</span>
|
||||
<svg class="shrink-0 size-3" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Link } from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "CategoryBadge",
|
||||
components: { Link },
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
|
||||
// Создаем массив для хранения всех ключей, которые нужно удалить
|
||||
const keysToDelete = [];
|
||||
|
||||
// Перебираем все параметры и добавляем ключи, начинающиеся с 'category', в массив
|
||||
for (const [key] of url.searchParams) {
|
||||
if (key.startsWith(this.filter.param)) {
|
||||
keysToDelete.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем все ключи из массива
|
||||
keysToDelete.forEach(key => url.searchParams.delete(key));
|
||||
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<span v-if="this.filter.value !== null" class="inline-flex items-center gap-x-1.5 py-1.5 ps-3 pe-2 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
||||
Поиск: {{ filter.value }}
|
||||
<button @click.prevent="clearFilter" type="button" class="shrink-0 size-4 inline-flex items-center justify-center rounded-full hover:bg-blue-200 focus:outline-none focus:bg-blue-200 focus:text-blue-500">
|
||||
<span class="sr-only">Remove badge</span>
|
||||
<svg class="shrink-0 size-3" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
export default {
|
||||
name: "SearchBadge",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete(this.filter.param);
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl,{
|
||||
method: 'get',
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<span v-if="this.filter.value !== null" class="inline-flex items-center gap-x-1.5 py-1.5 ps-3 pe-2 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
||||
{{ (filter.value.length > 1) ? 'Тэг: ' + filter.value.length + ' значений' : '#' + JSON.parse(filter.content[filter.value].data.name).ru }}
|
||||
<button @click.prevent="clearFilter" type="button" class="shrink-0 size-4 inline-flex items-center justify-center rounded-full hover:bg-blue-200 focus:outline-none focus:bg-blue-200 focus:text-blue-500">
|
||||
<span class="sr-only">Remove badge</span>
|
||||
<svg class="shrink-0 size-3" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
export default {
|
||||
name: "TagBadge",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
|
||||
// Создаем массив для хранения всех ключей, которые нужно удалить
|
||||
const keysToDelete = [];
|
||||
|
||||
// Перебираем все параметры и добавляем ключи, начинающиеся с 'category', в массив
|
||||
for (const [key] of url.searchParams) {
|
||||
if (key.startsWith(this.filter.param)) {
|
||||
keysToDelete.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем все ключи из массива
|
||||
keysToDelete.forEach(key => url.searchParams.delete(key));
|
||||
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<template v-for="file in block.data.file">
|
||||
<div class="">
|
||||
<a class="" :href="'/storage/'+ file.path" download type="button">
|
||||
|
||||
<div class="flex border rounded-lg px-4 py-2 items-center justify-between duration-300 hover:bg-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="w-[30px] h-[30px] bg-black flex justify-center items-center rounded-md mr-2">
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.9375 12.2188H12.75C12.4682 12.2188 12.198 12.1068 11.9987 11.9076C11.7994 11.7083 11.6875 11.438 11.6875 11.1562V5.84375C11.6875 5.56196 11.7994 5.29171 11.9987 5.09245C12.198 4.89319 12.4682 4.78125 12.75 4.78125H15.9375V5.84375H12.75V11.1562H15.9375V12.2188ZM9.5625 12.2188H7.4375C7.15571 12.2188 6.88546 12.1068 6.6862 11.9076C6.48694 11.7083 6.375 11.438 6.375 11.1562V5.84375C6.375 5.56196 6.48694 5.29171 6.6862 5.09245C6.88546 4.89319 7.15571 4.78125 7.4375 4.78125H9.5625C9.84429 4.78125 10.1145 4.89319 10.3138 5.09245C10.5131 5.29171 10.625 5.56196 10.625 5.84375V11.1562C10.625 11.438 10.5131 11.7083 10.3138 11.9076C10.1145 12.1068 9.84429 12.2188 9.5625 12.2188ZM7.4375 5.84375V11.1562H9.5625V5.84375H7.4375ZM3.1875 12.2188H1.0625V4.78125H3.1875C3.75087 4.78195 4.29096 5.00606 4.68933 5.40442C5.08769 5.80279 5.3118 6.34288 5.3125 6.90625V10.0938C5.3118 10.6571 5.08769 11.1972 4.68933 11.5956C4.29096 11.9939 3.75087 12.218 3.1875 12.2188ZM2.125 11.1562H3.1875C3.46929 11.1562 3.73954 11.0443 3.9388 10.8451C4.13806 10.6458 4.25 10.3755 4.25 10.0938V6.90625C4.25 6.62446 4.13806 6.35421 3.9388 6.15495C3.73954 5.95569 3.46929 5.84375 3.1875 5.84375H2.125V11.1562Z" fill="#F8F8F8"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>{{ file.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "FileBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="md:font-bold md:text-xl text-lg font-medium text-gray-800">{{ block.data.content }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "HeadingBlock",
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<img @click="toggler = !toggler" loading="lazy" class="mx-auto object-cover rounded-md hover:opacity-95 hover:duration-200 transition" :src="'/storage/' + block.data.url" alt="">
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.url]"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "ImageBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="ms-4 mt-2 w-full border px-4 py-4 rounded-xl shadow-sm">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="w-full px-2 sm:px-3 lg:px-4mx-auto">
|
||||
<!-- Grid -->
|
||||
<a class="group flex flex-col bg-white border shadow-sm rounded-xl hover:shadow-md focus:outline-none focus:shadow-md transition"
|
||||
:href="(page.is_url) ? page.path : route('page.view', page.path) + '/'">
|
||||
<div class="p-4 md:p-5">
|
||||
<div class="flex items-center gap-x-5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mt-1 shrink-0 size-7 text-gray-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
|
||||
</svg>
|
||||
<div class="grow">
|
||||
<ol v-if="breadcrumbs" class="flex items-center whitespace-nowrap">
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.page }}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
<h3 class="mt-1 group-hover:text-blue-600 font-semibold text-gray-700">
|
||||
{{ page.title }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<!-- End Grid -->
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PageItemBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
page: null,
|
||||
breadcrumbs: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPage(id) {
|
||||
axios.get(route('client.widget.page.single', id))
|
||||
.then(response => {
|
||||
this.page = response.data.data.page;
|
||||
this.breadcrumbs = response.data.data.breadcrumbs;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPage(this.block.data.page)
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="text-sm text-gray-600 leading-6 md:text-[16px] md:text-[#374151] md:leading-8 md:font-light paragraph-container" v-html="wrapTables(block).data.content" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "ParagraphBlock",
|
||||
methods: {
|
||||
wrapTables(data) {
|
||||
if (data.type === 'paragraph' && data.data && data.data.content) {
|
||||
// Используем регулярное выражение для поиска всех таблиц
|
||||
const wrappedContent = data.data.content.replace(/<table([^>]*)>([\s\S]*?)<\/table>/g, (match, attrs, content) => {
|
||||
return `<div class="div-table"><table${attrs}>${content}</table></div>`;
|
||||
});
|
||||
|
||||
// Возвращаем новый объект с обновленным контентом
|
||||
return {
|
||||
...data,
|
||||
data: {
|
||||
...data.data,
|
||||
content: wrappedContent
|
||||
}
|
||||
};
|
||||
}
|
||||
return data; // Если тип не 'paragraph', возвращаем объект без изменений
|
||||
}
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.paragraph-container a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container p {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.paragraph-container ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.paragraph-container li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
|
||||
.paragraph-container ul {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container hr {
|
||||
@apply my-4
|
||||
}
|
||||
|
||||
.paragraph-container strong {
|
||||
@apply text-xl
|
||||
}
|
||||
|
||||
.div-table {
|
||||
@apply overflow-x-auto
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container table {
|
||||
@apply w-full border-collapse mt-4 mb-4 overflow-hidden; /* Ширина 100%, стыковка границ, отступы, закругленные края */
|
||||
}
|
||||
|
||||
.paragraph-container th, .paragraph-container td {
|
||||
@apply border border-gray-300 p-3 text-left; /* Границы, отступы, выравнивание текста */
|
||||
}
|
||||
|
||||
.paragraph-container th {
|
||||
@apply bg-gray-100 text-gray-800 font-semibold; /* Фон заголовка, цвет текста, жирный шрифт */
|
||||
}
|
||||
|
||||
.paragraph-container tr {
|
||||
@apply transition-colors duration-200; /* Плавный переход цветов */
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-200; /* Фон строки при наведении */
|
||||
}
|
||||
|
||||
.paragraph-container td {
|
||||
@apply text-gray-600; /* Цвет текста ячеек */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="w-full rounded-xl mb-4 p-4 md:p-6 bg-white border border-gray-200 dark:bg-slate-900 dark:border-gray-700">
|
||||
<div class="flex items-center gap-x-4">
|
||||
<img @click="toggler = !toggler" loading="lazy" class="rounded-xl w-[150px]" :src="'/storage/' + block.data.photo" alt="Image Description">
|
||||
<div class="grow">
|
||||
<p class="font-medium text-gray-800 hover:text-gray-500">
|
||||
{{ block.data.name }}
|
||||
</p>
|
||||
<template v-for="item in block.data.info">
|
||||
<p class="text-xs text-gray-500 mt-2">
|
||||
{{ item.column }}: {{ item.content }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Social Brands -->
|
||||
<!-- End Social Brands -->
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.photo]"/>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "PersonBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.data.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.data.preview ? 'storage/images/' + post.data.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.data.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
post: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPost(id) {
|
||||
axios.get(route('client.widget.post.single', id), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.post = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPost(this.block.data.post);
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<template v-for="post in posts.data" :key="post.id">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.preview ? 'storage/images/' + post.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</template>
|
||||
<div class="flex justify-center">
|
||||
<a :href="route('client.post.index', { category: block.data.category })" class="group inline-flex items-center gap-x-1 text-sm font-semibold text-[#1A5AAF]">
|
||||
Все новости
|
||||
<svg class="flex-shrink-0 size-4 transition ease-in-out group-hover:translate-x-1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
posts: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPosts() {
|
||||
axios.get(route('client.widget.post.index'), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.posts = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPosts();
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(step, index) in block.data.steps">
|
||||
<div class="flex gap-x-3">
|
||||
<div class="w-16 text-end min-w-[4rem]">
|
||||
<span class="text-xs text-gray-500">{{ block.data.step_name }} {{ index + 1 }}</span>
|
||||
</div>
|
||||
<div class="relative last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200">
|
||||
<div class="relative z-10 size-7 flex justify-center items-center">
|
||||
<div class="size-2 rounded-full bg-primaryBlue"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grow max-w-[70%] pt-0.5 pb-8 overflow-wrap break-words">
|
||||
<h3 class="flex gap-x-1.5 font-semibold text-gray-800">
|
||||
{{ step.title }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-600 step-content" v-html="step.content" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "StepperBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.step-content a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.step-content a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
|
||||
.step-content ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.step-content ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.step-content li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
|
||||
<div class="">
|
||||
<nav class="-mb-0.5 flex justify-center gap-x-2" aria-label="Tabs" role="tablist" aria-orientation="horizontal">
|
||||
<button
|
||||
v-for="(tab, index) in block.data.tab" type="button"
|
||||
class="hs-tab-active:bg-gray-100 rounded-md hs-tab-active:text-gray-700 py-1.5 px-3 inline-flex items-center gap-x-2 border-b-2 border-transparent text-sm whitespace-nowrap text-gray-500 focus:outline-none disabled:opacity-50 disabled:pointer-events-none"
|
||||
:class="(activeTab === index) ? 'active' : ''"
|
||||
@click="activeTab = index"
|
||||
:id="generateSlug(tab.title) + '-item'"
|
||||
:data-hs-tab="'#' + generateSlug(tab.title)"
|
||||
aria-selected="false"
|
||||
:aria-controls="generateSlug(tab.title)" role="tab">
|
||||
{{ tab.title }}
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<template v-for="(tab, index) in block.data.tab">
|
||||
|
||||
<div :id="generateSlug(tab.title)" :class="(activeTab === index) ? '' : 'hidden'"
|
||||
role="tabpanel" :aria-labelledby="generateSlug(tab.title) + '-item'">
|
||||
<PageTabBuilder :blocks="tab.content" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import PageTabBuilder from "@/Components/BuilderUi/Pages/PageTabBuilder.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "TabBlock",
|
||||
components: {
|
||||
PageTabBuilder
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<video class="h-full w-full rounded-lg" controls>
|
||||
<source
|
||||
:src="domainPath + '/storage/' + block.data.path"
|
||||
:type="block.data.mime"
|
||||
/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<figcaption class="mt-3 text-sm text-center text-gray-500 dark:text-neutral-500">
|
||||
{{ block.data.title }}
|
||||
</figcaption>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "VideoBlock",
|
||||
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
|
||||
<a @click.prevent="this.back" class="inline-flex items-center gap-x-1.5 text-sm text-gray-600 decoration-2 hover:underline dark:text-blue-500" href="#">
|
||||
<svg class="flex-shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
|
||||
{{ title }}
|
||||
</a>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "EventBackButton",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
back() {
|
||||
if (this.$page.props.urlPrev !== 'empty') {
|
||||
this.$inertia.visit(this.$page.props.urlPrev);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(filter, index) in filters"
|
||||
:key="index"
|
||||
:is="getComponent(filter.type)"
|
||||
:filter="filter"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import SearchBadge from "@/Components/BuilderUi/Events/Badges/SearchBadge.vue";
|
||||
import CategoryBadge from "@/Components/BuilderUi/Events/Badges/CategoryBadge.vue";
|
||||
import TagBadge from "@/Components/BuilderUi/Events/Badges/TagBadge.vue";
|
||||
export default {
|
||||
name: "EventBadgeBuilder",
|
||||
components: {
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge,
|
||||
TagBadge,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
search: 'SearchBadge',
|
||||
category: 'CategoryBadge',
|
||||
tag: 'TagBadge',
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
props: {
|
||||
filters: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HeadingBlock from "@/Components/BuilderUi/Events/Blocks/HeadingBlock.vue";
|
||||
import ParagraphBlock from "@/Components/BuilderUi/Events/Blocks/ParagraphBlock.vue";
|
||||
import ClientImageSlider from "@/Components/ClientImageSlider.vue";
|
||||
import ImageBlock from "@/Components/BuilderUi/Events/Blocks/ImageBlock.vue";
|
||||
import FileBlock from "@/Components/BuilderUi/Events/Blocks/FileBlock.vue";
|
||||
import PersonBlock from "@/Components/BuilderUi/Events/Blocks/PersonBlock.vue";
|
||||
import StepperBlock from "@/Components/BuilderUi/Events/Blocks/StepperBlock.vue";
|
||||
import VideoBlock from "@/Components/BuilderUi/Events/Blocks/VideoBlock.vue";
|
||||
import TabBlock from "@/Components/BuilderUi/Events/Blocks/TabBlock.vue";
|
||||
import PostListBlock from "@/Components/BuilderUi/Events/Blocks/PostListBlock.vue";
|
||||
import PageItemBlock from "@/Components/BuilderUi/Events/Blocks/PageItemBlock.vue";
|
||||
import PostItemBlock from "@/Components/BuilderUi/Events/Blocks/PostItemBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "EventBuilder",
|
||||
components: {
|
||||
FileBlock,
|
||||
ImageBlock,
|
||||
ClientImageSlider,
|
||||
ParagraphBlock,
|
||||
HeadingBlock,
|
||||
PersonBlock,
|
||||
StepperBlock,
|
||||
VideoBlock,
|
||||
TabBlock,
|
||||
PostListBlock,
|
||||
PageItemBlock,
|
||||
PostItemBlock},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
heading: 'HeadingBlock',
|
||||
paragraph: 'ParagraphBlock',
|
||||
images: 'ClientImageSlider',
|
||||
image: 'ImageBlock',
|
||||
files: 'FileBlock',
|
||||
person: 'PersonBlock',
|
||||
stepper: 'StepperBlock',
|
||||
video: 'VideoBlock',
|
||||
tabs: 'TabBlock',
|
||||
postsList: 'PostListBlock',
|
||||
postItem: 'PostItemBlock',
|
||||
pageItem: 'PageItemBlock',
|
||||
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import HeadingBlock from "@/Components/BuilderUi/Pages/Blocks/HeadingBlock.vue";
|
||||
import ParagraphBlock from "@/Components/BuilderUi/Pages/Blocks/ParagraphBlock.vue";
|
||||
import ClientImageSlider from "@/Components/ClientImageSlider.vue";
|
||||
import ImageBlock from "@/Components/BuilderUi/Pages/Blocks/ImageBlock.vue";
|
||||
import FileBlock from "@/Components/BuilderUi/Pages/Blocks/FileBlock.vue";
|
||||
import PersonBlock from "@/Components/BuilderUi/Pages/Blocks/PersonBlock.vue";
|
||||
import StepperBlock from "@/Components/BuilderUi/Pages/Blocks/StepperBlock.vue";
|
||||
import VideoBlock from "@/Components/BuilderUi/Pages/Blocks/VideoBlock.vue";
|
||||
import PostListBlock from "@/Components/BuilderUi/Pages/Blocks/PostListBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "EventTabBuilder",
|
||||
components: {FileBlock, ImageBlock, ClientImageSlider, ParagraphBlock, HeadingBlock, Link, PersonBlock, StepperBlock, VideoBlock, PostListBlock},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
heading: 'HeadingBlock',
|
||||
paragraph: 'ParagraphBlock',
|
||||
images: 'ClientImageSlider',
|
||||
image: 'ImageBlock',
|
||||
files: 'FileBlock',
|
||||
person: 'PersonBlock',
|
||||
stepper: 'StepperBlock',
|
||||
video: 'VideoBlock',
|
||||
postsList: 'PostListBlock',
|
||||
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<input type="text" v-model="searchTerm" class="py-2 px-3 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500" placeholder="Поиск по категориям">
|
||||
<button @click.prevent="clearFilter" class="text-gray-500 text-sm flex gap-x-1 items-center hover:text-gray-700">
|
||||
<BaseIcon class="w-4 h-4" name="delete" />
|
||||
<span>Очистить</span>
|
||||
</button>
|
||||
|
||||
<div class="divide-y divide-gray-200 dark:divide-gray-700 max-h-[30vh] overflow-y-auto">
|
||||
<div>
|
||||
<div class="flex flex-col ml-3">
|
||||
<div v-for="item in filteredItems" :key="item.id">
|
||||
<input v-model="category_slug" :value="item.slug" @change="filter"
|
||||
type="checkbox" class="shrink-0 mt-0.5 border-gray-200 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
|
||||
:id="'inp' + item.id">
|
||||
<label :for="'inp' + item.id" class="text-sm text-gray-500 ms-3 dark:text-neutral-400">{{ item.title }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import SearchBadge from "@/Components/BuilderUi/Events/Badges/SearchBadge.vue";
|
||||
import CategoryBadge from "@/Components/BuilderUi/Events/Badges/CategoryBadge.vue";
|
||||
import BaseIcon from "@/Components/BaseComponents/BaseIcon.vue";
|
||||
import {debounce} from "lodash/function.js";
|
||||
export default {
|
||||
name: "CategoryFilter",
|
||||
components: {
|
||||
BaseIcon,
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
category_slug: this.category_filter.value || [],
|
||||
tag_slug: [],
|
||||
searchTerm: '',
|
||||
filteredItems: this.categories.data, // Изначально все элементы
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('category[]');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
category: this.category_slug,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('category[]');
|
||||
this.category_slug = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
filteredItems() {
|
||||
if (!this.searchTerm) {
|
||||
return this.categories.data; // Возвращаем все элементы, если ничего не введено
|
||||
}
|
||||
const term = this.searchTerm.toLowerCase(); // Приводим к нижнему регистру для нечувствительности к регистру
|
||||
return this.categories.data.filter(item =>
|
||||
item.title.toLowerCase().includes(term) // Фильтруем по заголовку
|
||||
);
|
||||
}
|
||||
},
|
||||
props: {
|
||||
categories: {
|
||||
type: Object,
|
||||
},
|
||||
category_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<h3 class="font-medium text-gray-900 text-sm mb-2">Сортировать по онлайн статусу</h3>
|
||||
<div>
|
||||
<select @change="filter" v-model="is_online" class="py-2 px-3 pe-9 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:placeholder-neutral-500 dark:focus:ring-neutral-600">
|
||||
<option value="all">Все</option>
|
||||
<option value="online">Только онлайн</option>
|
||||
<option value="offline">Только офлайн</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import SearchBadge from "@/Components/BuilderUi/Events/Badges/SearchBadge.vue";
|
||||
import CategoryBadge from "@/Components/BuilderUi/Events/Badges/CategoryBadge.vue";
|
||||
import BaseIcon from "@/Components/BaseComponents/BaseIcon.vue";
|
||||
import {debounce} from "lodash/function.js";
|
||||
export default {
|
||||
name: "IsOnlineFilter",
|
||||
components: {
|
||||
BaseIcon,
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
is_online: this.is_online_filter.value || 'all',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('is_online');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
is_online: this.is_online,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('sort');
|
||||
this.tag_slug = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
is_online_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<h3 class="font-medium text-gray-900 text-sm mb-2">Сортировать по</h3>
|
||||
<div>
|
||||
<select @change="filter" v-model="sortOrder" class="py-2 px-3 pe-9 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:placeholder-neutral-500 dark:focus:ring-neutral-600">
|
||||
<option value="desc">Дате (Сначала новые)</option>
|
||||
<option value="asc">Дате (Сначала старые)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import SearchBadge from "@/Components/BuilderUi/Events/Badges/SearchBadge.vue";
|
||||
import CategoryBadge from "@/Components/BuilderUi/Events/Badges/CategoryBadge.vue";
|
||||
import BaseIcon from "@/Components/BaseComponents/BaseIcon.vue";
|
||||
import {debounce} from "lodash/function.js";
|
||||
export default {
|
||||
name: "SortingByFilter",
|
||||
components: {
|
||||
BaseIcon,
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sortOrder: this.sortingBy_filter.value || 'desc',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('sort');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
sort: this.sortOrder,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('sort');
|
||||
this.tag_slug = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
sortingBy_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<input type="text" v-model="searchTerm" class="py-2 px-3 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500" placeholder="Поиск по тэгам">
|
||||
<button @click.prevent="clearFilter" class="text-gray-500 text-sm flex gap-x-1 items-center hover:text-gray-700">
|
||||
<BaseIcon class="w-4 h-4" name="delete" />
|
||||
<span>Очистить</span>
|
||||
</button>
|
||||
|
||||
<div class="divide-y divide-gray-200 dark:divide-gray-700 max-h-[30vh] overflow-y-auto">
|
||||
<div>
|
||||
<div class="flex flex-col ml-3">
|
||||
<div v-for="item in filteredItems" :key="item.id">
|
||||
<input v-model="tag_slug" :value="item.slug['ru']" @change="filter"
|
||||
type="checkbox" class="shrink-0 mt-0.5 border-gray-200 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
|
||||
:id="'inp-tag' + item.id">
|
||||
<label :for="'inp-tag' + item.id" class="text-sm text-gray-500 ms-3 dark:text-neutral-400">#{{ item.name.ru }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import SearchBadge from "@/Components/BuilderUi/Events/Badges/SearchBadge.vue";
|
||||
import CategoryBadge from "@/Components/BuilderUi/Events/Badges/CategoryBadge.vue";
|
||||
import BaseIcon from "@/Components/BaseComponents/BaseIcon.vue";
|
||||
import {debounce} from "lodash/function.js";
|
||||
export default {
|
||||
name: "TagFilter",
|
||||
components: {
|
||||
BaseIcon,
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tag_slug: this.tag_filter.value || [],
|
||||
searchTerm: '',
|
||||
filteredItems: this.tags, // Изначально все элементы
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('tag[]');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
tag: this.tag_slug,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('tag[]');
|
||||
this.tag_slug = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
filteredItems() {
|
||||
if (!this.searchTerm) {
|
||||
return this.tags; // Возвращаем все элементы, если ничего не введено
|
||||
}
|
||||
const term = this.searchTerm.toLowerCase(); // Приводим к нижнему регистру для нечувствительности к регистру
|
||||
return this.tags.filter(item =>
|
||||
item.name.ru.toLowerCase().includes(term) // Фильтруем по заголовку
|
||||
);
|
||||
}
|
||||
},
|
||||
props: {
|
||||
tags: {
|
||||
type: Object,
|
||||
},
|
||||
tag_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
|
||||
<h1 class="text-brand-primary text-2xl mb-3 mt-2 md:text-3xl font-semibold tracking-tight dark:text-white lg:text-[40px] lg:leading-tight">
|
||||
{{ header }}
|
||||
</h1>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "TitleEvent",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
header: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<template v-for="file in block.data.file">
|
||||
<div class="">
|
||||
<a class="" :href="'/storage/'+ file.path" download type="button">
|
||||
|
||||
<div class="flex border rounded-lg px-4 py-2 items-center justify-between duration-300 hover:bg-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="w-[30px] h-[30px] bg-black flex justify-center items-center rounded-md mr-2">
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.9375 12.2188H12.75C12.4682 12.2188 12.198 12.1068 11.9987 11.9076C11.7994 11.7083 11.6875 11.438 11.6875 11.1562V5.84375C11.6875 5.56196 11.7994 5.29171 11.9987 5.09245C12.198 4.89319 12.4682 4.78125 12.75 4.78125H15.9375V5.84375H12.75V11.1562H15.9375V12.2188ZM9.5625 12.2188H7.4375C7.15571 12.2188 6.88546 12.1068 6.6862 11.9076C6.48694 11.7083 6.375 11.438 6.375 11.1562V5.84375C6.375 5.56196 6.48694 5.29171 6.6862 5.09245C6.88546 4.89319 7.15571 4.78125 7.4375 4.78125H9.5625C9.84429 4.78125 10.1145 4.89319 10.3138 5.09245C10.5131 5.29171 10.625 5.56196 10.625 5.84375V11.1562C10.625 11.438 10.5131 11.7083 10.3138 11.9076C10.1145 12.1068 9.84429 12.2188 9.5625 12.2188ZM7.4375 5.84375V11.1562H9.5625V5.84375H7.4375ZM3.1875 12.2188H1.0625V4.78125H3.1875C3.75087 4.78195 4.29096 5.00606 4.68933 5.40442C5.08769 5.80279 5.3118 6.34288 5.3125 6.90625V10.0938C5.3118 10.6571 5.08769 11.1972 4.68933 11.5956C4.29096 11.9939 3.75087 12.218 3.1875 12.2188ZM2.125 11.1562H3.1875C3.46929 11.1562 3.73954 11.0443 3.9388 10.8451C4.13806 10.6458 4.25 10.3755 4.25 10.0938V6.90625C4.25 6.62446 4.13806 6.35421 3.9388 6.15495C3.73954 5.95569 3.46929 5.84375 3.1875 5.84375H2.125V11.1562Z" fill="#F8F8F8"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>{{ file.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "FileBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="md:font-bold md:text-xl text-lg font-medium text-gray-800">{{ block.data.content }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "HeadingBlock",
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<img @click="toggler = !toggler" loading="lazy" class="mx-auto object-cover rounded-md hover:opacity-95 hover:duration-200 transition" :src="'/storage/' + block.data.url" alt="">
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.url]"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "ImageBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="ms-4 mt-2 w-full border px-4 py-4 rounded-xl shadow-sm">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="w-full px-2 sm:px-3 lg:px-4mx-auto">
|
||||
<!-- Grid -->
|
||||
<a class="group flex flex-col bg-white border shadow-sm rounded-xl hover:shadow-md focus:outline-none focus:shadow-md transition"
|
||||
:href="(page.is_url) ? page.path : route('page.view', page.path) + '/'">
|
||||
<div class="p-4 md:p-5">
|
||||
<div class="flex items-center gap-x-5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mt-1 shrink-0 size-7 text-gray-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
|
||||
</svg>
|
||||
<div class="grow">
|
||||
<ol v-if="breadcrumbs" class="flex items-center whitespace-nowrap">
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.page }}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
<h3 class="mt-1 group-hover:text-blue-600 font-semibold text-gray-700">
|
||||
{{ page.title }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<!-- End Grid -->
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PageItemBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
page: null,
|
||||
breadcrumbs: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPage(id) {
|
||||
axios.get(route('client.widget.page.single', id))
|
||||
.then(response => {
|
||||
this.page = response.data.data.page;
|
||||
this.breadcrumbs = response.data.data.breadcrumbs;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPage(this.block.data.page)
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="text-sm text-gray-600 leading-6 md:text-[16px] md:text-[#374151] md:leading-8 md:font-light paragraph-container" v-html="wrapTables(block).data.content" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "ParagraphBlock",
|
||||
methods: {
|
||||
wrapTables(data) {
|
||||
if (data.type === 'paragraph' && data.data && data.data.content) {
|
||||
// Используем регулярное выражение для поиска всех таблиц
|
||||
const wrappedContent = data.data.content.replace(/<table([^>]*)>([\s\S]*?)<\/table>/g, (match, attrs, content) => {
|
||||
return `<div class="div-table"><table${attrs}>${content}</table></div>`;
|
||||
});
|
||||
|
||||
// Возвращаем новый объект с обновленным контентом
|
||||
return {
|
||||
...data,
|
||||
data: {
|
||||
...data.data,
|
||||
content: wrappedContent
|
||||
}
|
||||
};
|
||||
}
|
||||
return data; // Если тип не 'paragraph', возвращаем объект без изменений
|
||||
}
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.paragraph-container a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container p {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.paragraph-container ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.paragraph-container li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
|
||||
.paragraph-container ul {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container hr {
|
||||
@apply my-4
|
||||
}
|
||||
|
||||
.paragraph-container strong {
|
||||
@apply text-xl
|
||||
}
|
||||
|
||||
.div-table {
|
||||
@apply overflow-x-auto
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container table {
|
||||
@apply w-full border-collapse mt-4 mb-4 overflow-hidden; /* Ширина 100%, стыковка границ, отступы, закругленные края */
|
||||
}
|
||||
|
||||
.paragraph-container th, .paragraph-container td {
|
||||
@apply border border-gray-300 p-3 text-left; /* Границы, отступы, выравнивание текста */
|
||||
}
|
||||
|
||||
.paragraph-container th {
|
||||
@apply bg-gray-100 text-gray-800 font-semibold; /* Фон заголовка, цвет текста, жирный шрифт */
|
||||
}
|
||||
|
||||
.paragraph-container tr {
|
||||
@apply transition-colors duration-200; /* Плавный переход цветов */
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-200; /* Фон строки при наведении */
|
||||
}
|
||||
|
||||
.paragraph-container td {
|
||||
@apply text-gray-600; /* Цвет текста ячеек */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="w-full rounded-xl mb-4 p-4 md:p-6 bg-white border border-gray-200 dark:bg-slate-900 dark:border-gray-700">
|
||||
<div class="flex items-center gap-x-4">
|
||||
<img @click="toggler = !toggler" loading="lazy" class="rounded-xl w-[150px]" :src="'/storage/' + block.data.photo" alt="Image Description">
|
||||
<div class="grow">
|
||||
<p class="font-medium text-gray-800 hover:text-gray-500">
|
||||
{{ block.data.name }}
|
||||
</p>
|
||||
<template v-for="item in block.data.info">
|
||||
<p class="text-xs text-gray-500 mt-2">
|
||||
{{ item.column }}: {{ item.content }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Social Brands -->
|
||||
<!-- End Social Brands -->
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.photo]"/>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "PersonBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.data.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.data.preview ? 'storage/images/' + post.data.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.data.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
post: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPost(id) {
|
||||
axios.get(route('client.widget.post.single', id), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.post = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPost(this.block.data.post);
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<template v-for="post in posts.data" :key="post.id">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.preview ? 'storage/images/' + post.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</template>
|
||||
<div class="flex justify-center">
|
||||
<a :href="route('client.post.index', { category: block.data.category })" class="group inline-flex items-center gap-x-1 text-sm font-semibold text-[#1A5AAF]">
|
||||
Все новости
|
||||
<svg class="flex-shrink-0 size-4 transition ease-in-out group-hover:translate-x-1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
posts: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPosts() {
|
||||
axios.get(route('client.widget.post.index'), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.posts = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPosts();
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(step, index) in block.data.steps">
|
||||
<div class="flex gap-x-3">
|
||||
<div class="w-16 text-end min-w-[4rem]">
|
||||
<span class="text-xs text-gray-500">{{ block.data.step_name }} {{ index + 1 }}</span>
|
||||
</div>
|
||||
<div class="relative last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200">
|
||||
<div class="relative z-10 size-7 flex justify-center items-center">
|
||||
<div class="size-2 rounded-full bg-primaryBlue"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grow max-w-[70%] pt-0.5 pb-8 overflow-wrap break-words">
|
||||
<h3 class="flex gap-x-1.5 font-semibold text-gray-800">
|
||||
{{ step.title }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-600 step-content" v-html="step.content" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "StepperBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.step-content a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.step-content a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
|
||||
.step-content ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.step-content ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.step-content li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
|
||||
<div class="">
|
||||
<nav class="-mb-0.5 flex justify-center gap-x-2" aria-label="Tabs" role="tablist" aria-orientation="horizontal">
|
||||
<button
|
||||
v-for="(tab, index) in block.data.tab" type="button"
|
||||
class="hs-tab-active:bg-gray-100 rounded-md hs-tab-active:text-gray-700 py-1.5 px-3 inline-flex items-center gap-x-2 border-b-2 border-transparent text-sm whitespace-nowrap text-gray-500 focus:outline-none disabled:opacity-50 disabled:pointer-events-none"
|
||||
:class="(activeTab === index) ? 'active' : ''"
|
||||
@click="activeTab = index"
|
||||
:id="generateSlug(tab.title) + '-item'"
|
||||
:data-hs-tab="'#' + generateSlug(tab.title)"
|
||||
aria-selected="false"
|
||||
:aria-controls="generateSlug(tab.title)" role="tab">
|
||||
{{ tab.title }}
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<template v-for="(tab, index) in block.data.tab">
|
||||
|
||||
<div :id="generateSlug(tab.title)" :class="(activeTab === index) ? '' : 'hidden'"
|
||||
role="tabpanel" :aria-labelledby="generateSlug(tab.title) + '-item'">
|
||||
<PageTabBuilder :blocks="tab.content" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import PageTabBuilder from "@/Components/BuilderUi/Pages/PageTabBuilder.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "TabBlock",
|
||||
components: {
|
||||
PageTabBuilder
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<video class="h-full w-full rounded-lg" controls>
|
||||
<source
|
||||
:src="domainPath + '/storage/' + block.data.path"
|
||||
:type="block.data.mime"
|
||||
/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<figcaption class="mt-3 text-sm text-center text-gray-500 dark:text-neutral-500">
|
||||
{{ block.data.title }}
|
||||
</figcaption>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "VideoBlock",
|
||||
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
|
||||
<a href="#" @click.prevent="this.back" class="inline-flex items-center gap-x-1.5 text-sm text-gray-600 decoration-2 hover:underline dark:text-blue-500">
|
||||
<svg class="flex-shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
|
||||
{{ title }}
|
||||
</a>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "FacultyBackButton",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
back() {
|
||||
if (this.$page.props.urlPrev !== 'empty') {
|
||||
this.$inertia.visit(this.$page.props.urlPrev);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HeadingBlock from "@/Components/BuilderUi/Faculties/Blocks/HeadingBlock.vue";
|
||||
import ParagraphBlock from "@/Components/BuilderUi/Faculties/Blocks/ParagraphBlock.vue";
|
||||
import ClientImageSlider from "@/Components/ClientImageSlider.vue";
|
||||
import ImageBlock from "@/Components/BuilderUi/Faculties/Blocks/ImageBlock.vue";
|
||||
import FileBlock from "@/Components/BuilderUi/Faculties/Blocks/FileBlock.vue";
|
||||
import PersonBlock from "@/Components/BuilderUi/Faculties/Blocks/PersonBlock.vue";
|
||||
import StepperBlock from "@/Components/BuilderUi/Faculties/Blocks/StepperBlock.vue";
|
||||
import VideoBlock from "@/Components/BuilderUi/Faculties/Blocks/VideoBlock.vue";
|
||||
import TabBlock from "@/Components/BuilderUi/Faculties/Blocks/TabBlock.vue";
|
||||
import PostListBlock from "@/Components/BuilderUi/Faculties/Blocks/PostListBlock.vue";
|
||||
import PageItemBlock from "@/Components/BuilderUi/Faculties/Blocks/PageItemBlock.vue";
|
||||
import PostItemBlock from "@/Components/BuilderUi/Faculties/Blocks/PostItemBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "FacultyBuilder",
|
||||
components: {
|
||||
FileBlock,
|
||||
ImageBlock,
|
||||
ClientImageSlider,
|
||||
ParagraphBlock,
|
||||
HeadingBlock,
|
||||
PersonBlock,
|
||||
StepperBlock,
|
||||
VideoBlock,
|
||||
TabBlock,
|
||||
PostListBlock,
|
||||
PageItemBlock,
|
||||
PostItemBlock},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
heading: 'HeadingBlock',
|
||||
paragraph: 'ParagraphBlock',
|
||||
images: 'ClientImageSlider',
|
||||
image: 'ImageBlock',
|
||||
files: 'FileBlock',
|
||||
person: 'PersonBlock',
|
||||
stepper: 'StepperBlock',
|
||||
video: 'VideoBlock',
|
||||
tabs: 'TabBlock',
|
||||
postsList: 'PostListBlock',
|
||||
postItem: 'PostItemBlock',
|
||||
pageItem: 'PageItemBlock',
|
||||
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import HeadingBlock from "@/Components/BuilderUi/Faculties/Blocks/HeadingBlock.vue";
|
||||
import ParagraphBlock from "@/Components/BuilderUi/Faculties/Blocks/ParagraphBlock.vue";
|
||||
import ClientImageSlider from "@/Components/ClientImageSlider.vue";
|
||||
import ImageBlock from "@/Components/BuilderUi/Faculties/Blocks/ImageBlock.vue";
|
||||
import FileBlock from "@/Components/BuilderUi/Faculties/Blocks/FileBlock.vue";
|
||||
import PersonBlock from "@/Components/BuilderUi/Faculties/Blocks/PersonBlock.vue";
|
||||
import StepperBlock from "@/Components/BuilderUi/Faculties/Blocks/StepperBlock.vue";
|
||||
import VideoBlock from "@/Components/BuilderUi/Faculties/Blocks/VideoBlock.vue";
|
||||
import PostListBlock from "@/Components/BuilderUi/Faculties/Blocks/PostListBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "FacultyTabBuilder",
|
||||
components: {FileBlock, ImageBlock, ClientImageSlider, ParagraphBlock, HeadingBlock, Link, PersonBlock, StepperBlock, VideoBlock, PostListBlock},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
heading: 'HeadingBlock',
|
||||
paragraph: 'ParagraphBlock',
|
||||
images: 'ClientImageSlider',
|
||||
image: 'ImageBlock',
|
||||
files: 'FileBlock',
|
||||
person: 'PersonBlock',
|
||||
stepper: 'StepperBlock',
|
||||
video: 'VideoBlock',
|
||||
postsList: 'PostListBlock',
|
||||
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
|
||||
<h1 class="text-brand-primary text-2xl mb-3 mt-2 md:text-3xl font-semibold tracking-tight dark:text-white lg:text-[40px] lg:leading-tight">
|
||||
{{ header }}
|
||||
</h1>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "FacultyTitle",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
header: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<template v-for="file in block.data.file">
|
||||
<div class="mb-4">
|
||||
<a class="" :href="'/storage/'+ file.path" download type="button">
|
||||
<div class="flex border rounded-lg px-4 py-2 items-center justify-between duration-300 hover:bg-gray-100">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="min-w-[30px] min-h-[30px] bg-[#303030] flex justify-center items-center rounded-md mr-2">
|
||||
<BaseIcon :name="file.expansion" class="w-5 h-5 flex-shrink-0" />
|
||||
</div>
|
||||
<div>{{ textLimit(file.title, 70) }}</div>
|
||||
</div>
|
||||
<span class="text-sm text-gray-400">{{ file.size }}</span>
|
||||
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
import BaseIcon from "@/Components/BaseComponents/BaseIcon.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "FileBlock",
|
||||
components: {BaseIcon, FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText;
|
||||
LimitedText = text.substring(0, symbols);
|
||||
return LimitedText + "...";
|
||||
}
|
||||
return text;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<FormBuilder :blocks="form" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
import BaseIcon from "@/Components/BaseComponents/BaseIcon.vue";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import FormBuilder from "@/Components/BuilderUi/Pages/FormBuilder.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "FormBlock",
|
||||
components: {FormBuilder, axios, Link },
|
||||
data() {
|
||||
return {
|
||||
form: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getForm(id) {
|
||||
axios.get(route('client.widget.form.single', id))
|
||||
.then(response => {
|
||||
this.form = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getForm(this.block.data.form);
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div >
|
||||
<h2 :id="generateSlug(block.data.content)" class="font-bold text-xl">{{ block.data.content }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "HeadingBlock",
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<img @click="toggler = !toggler" loading="lazy" class="mx-auto object-cover rounded-md hover:opacity-95 hover:duration-200 transition" :src="'/storage/' + block.data.url" alt="">
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.url]"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "ImageBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="ms-4 mt-2 w-full border px-4 py-4 rounded-xl shadow-sm">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="w-full px-2 py-5 sm:px-3 lg:px-4 lg:py-7 mx-auto">
|
||||
<!-- Grid -->
|
||||
<a class="group flex flex-col bg-white border shadow-sm rounded-xl hover:shadow-md focus:outline-none focus:shadow-md transition"
|
||||
:href="(page.is_url) ? page.path : route('page.view', page.path) + '/'">
|
||||
<div class="p-4 md:p-5">
|
||||
<div class="flex items-center gap-x-5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mt-1 shrink-0 size-7 text-gray-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
|
||||
</svg>
|
||||
<div class="grow">
|
||||
<ol class="flex items-center whitespace-nowrap">
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.page }}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
<h3 class="mt-1 group-hover:text-blue-600 font-semibold text-gray-700">
|
||||
{{ page.title }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<!-- End Grid -->
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PageItemBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
page: null,
|
||||
breadcrumbs: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPage(id) {
|
||||
axios.get(route('client.widget.page.single', id))
|
||||
.then(response => {
|
||||
this.page = response.data.data.page;
|
||||
this.breadcrumbs = response.data.data.breadcrumbs;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPage(this.block.data.page)
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
|
||||
<div class="text-sm text-gray-600 leading-6 md:text-[16px] md:text-[#374151] md:leading-8 md:font-light paragraph-container" v-html="wrapTables(block).data.content" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "ParagraphBlock",
|
||||
methods: {
|
||||
wrapTables(data) {
|
||||
if (data.type === 'paragraph' && data.data && data.data.content) {
|
||||
// Используем регулярное выражение для поиска всех таблиц
|
||||
const wrappedContent = data.data.content.replace(/<table([^>]*)>([\s\S]*?)<\/table>/g, (match, attrs, content) => {
|
||||
return `<div class="div-table"><table${attrs}>${content}</table></div>`;
|
||||
});
|
||||
|
||||
// Возвращаем новый объект с обновленным контентом
|
||||
return {
|
||||
...data,
|
||||
data: {
|
||||
...data.data,
|
||||
content: wrappedContent
|
||||
}
|
||||
};
|
||||
}
|
||||
return data; // Если тип не 'paragraph', возвращаем объект без изменений
|
||||
}
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.paragraph-container a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container p {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.paragraph-container ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.paragraph-container li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
|
||||
.paragraph-container ul {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container hr {
|
||||
@apply my-4
|
||||
}
|
||||
|
||||
.paragraph-container strong {
|
||||
@apply text-xl
|
||||
}
|
||||
|
||||
.div-table {
|
||||
@apply overflow-x-auto
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container table {
|
||||
@apply w-full border-collapse mt-4 mb-4 overflow-hidden; /* Ширина 100%, стыковка границ, отступы, закругленные края */
|
||||
}
|
||||
|
||||
.paragraph-container th, .paragraph-container td {
|
||||
@apply border border-gray-300 p-3 text-left; /* Границы, отступы, выравнивание текста */
|
||||
}
|
||||
|
||||
.paragraph-container th {
|
||||
@apply bg-gray-100 text-gray-800 font-semibold; /* Фон заголовка, цвет текста, жирный шрифт */
|
||||
}
|
||||
|
||||
.paragraph-container tr {
|
||||
@apply transition-colors duration-200; /* Плавный переход цветов */
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-200; /* Фон строки при наведении */
|
||||
}
|
||||
|
||||
.paragraph-container td {
|
||||
@apply text-gray-600; /* Цвет текста ячеек */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="w-full rounded-xl mb-4 p-4 md:p-6 bg-white border border-gray-200 ">
|
||||
<div class="flex items-center gap-x-4 text-nowrap">
|
||||
<img @click="toggler = !toggler" loading="lazy" class="rounded-xl w-[150px]" :src="'/storage/' + block.data.photo" alt="Image Description">
|
||||
<div class="grow overflow-x-auto">
|
||||
<p class="font-medium text-gray-800 hover:text-gray-500">
|
||||
{{ block.data.name }}
|
||||
</p>
|
||||
<template v-for="item in block.data.info">
|
||||
<p class="text-xs text-gray-500 mt-2">
|
||||
{{ item.column }}: {{ item.content }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Social Brands -->
|
||||
<!-- End Social Brands -->
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.photo]"/>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "PersonBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.data.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.data.preview ? 'storage/images/' + post.data.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.data.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
post: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPost(id) {
|
||||
axios.get(route('client.widget.post.single', id), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.post = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPost(this.block.data.post);
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<template v-for="post in posts.data" :key="post.id">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.preview ? 'storage/images/' + post.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</template>
|
||||
<div class="flex justify-center">
|
||||
<a :href="route('client.post.index', { category: block.data.category })" class="group inline-flex items-center gap-x-1 text-sm font-semibold text-[#1A5AAF]">
|
||||
Все новости
|
||||
<svg class="flex-shrink-0 size-4 transition ease-in-out group-hover:translate-x-1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
posts: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPosts() {
|
||||
axios.get(route('client.widget.post.index'), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.posts = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPosts();
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(step, index) in block.data.steps">
|
||||
<div class="flex gap-x-3">
|
||||
<div class="w-16 text-end min-w-[4rem]">
|
||||
<span class="text-xs text-gray-500">{{ block.data.step_name }} {{ index + 1 }}</span>
|
||||
</div>
|
||||
<div class="relative last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200">
|
||||
<div class="relative z-10 size-7 flex justify-center items-center">
|
||||
<div class="size-2 rounded-full bg-primaryBlue"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grow max-w-[70%] pt-0.5 pb-8 overflow-wrap break-words">
|
||||
<h3 class="flex gap-x-1.5 font-semibold text-gray-800">
|
||||
{{ step.title }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-600 step-content" v-html="step.content" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "StepperBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.step-content a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.step-content a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
|
||||
.step-content ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.step-content ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.step-content li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
|
||||
<div class="">
|
||||
<nav class="-mb-0.5 flex justify-center gap-2 flex-wrap" aria-label="Tabs" role="tablist" aria-orientation="horizontal">
|
||||
<button
|
||||
v-for="(tab, index) in block.data.tab" type="button"
|
||||
class="hs-tab-active:bg-gray-100 rounded-md hs-tab-active:text-gray-700 py-1.5 px-3 inline-flex items-center gap-x-2 border-b-2 border-transparent text-sm whitespace-nowrap text-gray-500 focus:outline-none disabled:opacity-50 disabled:pointer-events-none"
|
||||
:class="(activeTab === index) ? 'active' : ''"
|
||||
@click="activeTab = index"
|
||||
:id="generateSlug(tab.title) + '-item'"
|
||||
:data-hs-tab="'#' + generateSlug(tab.title)"
|
||||
aria-selected="false"
|
||||
:aria-controls="generateSlug(tab.title)" role="tab">
|
||||
{{ tab.title }}
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<template v-for="(tab, index) in block.data.tab">
|
||||
|
||||
<div :id="generateSlug(tab.title)" :class="(activeTab === index) ? '' : 'hidden'"
|
||||
role="tabpanel" :aria-labelledby="generateSlug(tab.title) + '-item'">
|
||||
<PageTabBuilder :blocks="tab.content" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import PageTabBuilder from "@/Components/BuilderUi/Pages/PageTabBuilder.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "TabBlock",
|
||||
components: {
|
||||
PageTabBuilder
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<video class="h-full w-full rounded-lg" controls>
|
||||
<source
|
||||
:src="domainPath + '/storage/' + block.data.path"
|
||||
:type="block.data.mime"
|
||||
/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<figcaption class="mt-3 text-sm text-center text-gray-500 dark:text-neutral-500">
|
||||
{{ block.data.title }}
|
||||
</figcaption>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "VideoBlock",
|
||||
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="mb-4 sm:mb-8">
|
||||
<label :for="block.data.name_field + '-id'" class="block mb-2 text-sm font-medium">{{ block.data.title_field }}</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
:required="block.data.rules.required"
|
||||
:name="block.data.name_field"
|
||||
:min="block.data.rules.min"
|
||||
:max="block.data.rules.max"
|
||||
type="email"
|
||||
:id="block.data.name_field + '-id'"
|
||||
:class="(error) ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : 'focus:border-blue-500 focus:ring-blue-500'"
|
||||
class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm disabled:opacity-50 disabled:pointer-events-none"
|
||||
:placeholder="block.data.title_field"
|
||||
>
|
||||
<div v-if="error" class="absolute inset-y-0 end-0 flex items-center pointer-events-none pe-3">
|
||||
<svg class="shrink-0 size-4 text-red-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" x2="12" y1="8" y2="12"></line>
|
||||
<line x1="12" x2="12.01" y1="16" y2="16"></line>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!error" class="mt-2 text-sm text-gray-500" id="hs-input-helper-text">{{ block.data.description }}</p>
|
||||
<p v-for="item in error" class="text-sm text-red-600 mt-2" id="hs-validation-name-error-helper">{{ item }}</p>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "EmailBlock",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="mb-4 sm:mb-8">
|
||||
<label class="block mb-3 text-sm font-medium">{{ block.data.title_field }}</label>
|
||||
<div class="space-y-2">
|
||||
<div v-for="column in block.data.columns" class="flex">
|
||||
<input
|
||||
type="checkbox"
|
||||
:name="block.data.name_field + '[]'"
|
||||
:value="column.name_field"
|
||||
class="shrink-0 mt-0.5 border-gray-200 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
|
||||
:id="column.name_field + '-id'">
|
||||
<label :for="column.name_field + '-id'" class="text-sm text-gray-500 ms-3">{{ column.title_field }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!error" class="mt-2 text-sm text-gray-500" id="hs-input-helper-text">{{ block.data.description }}</p>
|
||||
<p v-for="item in error" class="text-sm text-red-600 mt-2" id="hs-validation-name-error-helper">{{ item }}</p>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MultipleChoiceBlock",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="mb-4 sm:mb-8">
|
||||
<label :for="block.data.name_field + '-id'" class="block mb-2 text-sm font-medium">{{ block.data.title_field }}</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
:required="block.data.rules.required"
|
||||
:name="block.data.name_field"
|
||||
:min="block.data.rules.min"
|
||||
:max="block.data.rules.max"
|
||||
type="tel"
|
||||
:id="block.data.name_field + '-id'"
|
||||
:class="(error) ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : 'focus:border-blue-500 focus:ring-blue-500'"
|
||||
class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm disabled:opacity-50 disabled:pointer-events-none"
|
||||
:placeholder="block.data.title_field"
|
||||
>
|
||||
<div v-if="error" class="absolute inset-y-0 end-0 flex items-center pointer-events-none pe-3">
|
||||
<svg class="shrink-0 size-4 text-red-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" x2="12" y1="8" y2="12"></line>
|
||||
<line x1="12" x2="12.01" y1="16" y2="16"></line>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!error" class="mt-2 text-sm text-gray-500" id="hs-input-helper-text">{{ block.data.description }}</p>
|
||||
<p v-for="item in error" class="text-sm text-red-600 mt-2" id="hs-validation-name-error-helper">{{ item }}</p>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "PhoneBlock",
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="mb-4 sm:mb-8">
|
||||
<label class="block mb-3 text-sm font-medium">{{ block.data.title_field }}</label>
|
||||
<div class="space-y-2">
|
||||
<div v-for="column in block.data.columns" class="flex">
|
||||
<input
|
||||
type="radio"
|
||||
:name="block.data.name_field"
|
||||
:value="column.name_field"
|
||||
name="hs-default-radio"
|
||||
class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
|
||||
:id="column.name_field + '-id'">
|
||||
<label :for="column.name_field + '-id'" class="text-sm text-gray-500 ms-2">{{ column.title_field }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!error" class="mt-2 text-sm text-gray-500" id="hs-input-helper-text">{{ block.data.description }}</p>
|
||||
<p v-for="item in error" class="text-sm text-red-600 mt-2" id="hs-validation-name-error-helper">{{ item }}</p>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SingleChoiceBlock",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
|
||||
<div class="mt-6 grid">
|
||||
<button type="submit" class="w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none">{{ block }}</button>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "SubmitBlock",
|
||||
methods: {
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="mb-4 sm:mb-8">
|
||||
<label :for="block.data.name_field + '-id'" class="block mb-2 text-sm font-medium">{{ block.data.title_field }}</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
:required="block.data.rules.required"
|
||||
:name="block.data.name_field"
|
||||
:min="block.data.rules.min"
|
||||
:max="block.data.rules.max"
|
||||
type="text"
|
||||
:id="block.data.name_field + '-id'"
|
||||
:class="(error) ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : 'focus:border-blue-500 focus:ring-blue-500'"
|
||||
class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm disabled:opacity-50 disabled:pointer-events-none"
|
||||
:placeholder="block.data.title_field"
|
||||
>
|
||||
<div v-if="error" class="absolute inset-y-0 end-0 flex items-center pointer-events-none pe-3">
|
||||
<svg class="shrink-0 size-4 text-red-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" x2="12" y1="8" y2="12"></line>
|
||||
<line x1="12" x2="12.01" y1="16" y2="16"></line>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!error" class="mt-2 text-sm text-gray-500" id="hs-input-helper-text">{{ block.data.description }}</p>
|
||||
<p v-for="item in error" class="text-sm text-red-600 mt-2" id="hs-validation-name-error-helper">{{ item }}</p>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TextAreaBlock",
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="mb-4 sm:mb-8">
|
||||
<label :for="block.data.name_field + '-id'" class="block mb-2 text-sm font-medium">{{ block.data.title_field }}</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
:required="block.data.rules.required"
|
||||
:name="block.data.name_field"
|
||||
:min="block.data.rules.min"
|
||||
:max="block.data.rules.max"
|
||||
type="text"
|
||||
:id="block.data.name_field + '-id'"
|
||||
:class="(error) ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : 'focus:border-blue-500 focus:ring-blue-500'"
|
||||
class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm disabled:opacity-50 disabled:pointer-events-none"
|
||||
:placeholder="block.data.title_field"
|
||||
>
|
||||
<div v-if="error" class="absolute inset-y-0 end-0 flex items-center pointer-events-none pe-3">
|
||||
<svg class="shrink-0 size-4 text-red-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" x2="12" y1="8" y2="12"></line>
|
||||
<line x1="12" x2="12.01" y1="16" y2="16"></line>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!error" class="mt-2 text-sm text-gray-500" id="hs-input-helper-text">{{ block.data.description }}</p>
|
||||
<p v-for="item in error" class="text-sm text-red-600 mt-2" id="hs-validation-name-error-helper">{{ item }}</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TextBlock",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
|
||||
|
||||
<div v-if="success" class="bg-teal-50 border-t-2 border-teal-500 rounded-lg p-4 dark:bg-teal-800/30" role="alert" tabindex="-1" aria-labelledby="hs-bordered-success-style-label">
|
||||
<div class="flex">
|
||||
<div class="shrink-0">
|
||||
<!-- Icon -->
|
||||
<span class="inline-flex justify-center items-center size-8 rounded-full border-4 border-teal-100 bg-teal-200 text-teal-800 dark:border-teal-900 dark:bg-teal-800 dark:text-teal-400">
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"></path>
|
||||
<path d="m9 12 2 2 4-4"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<!-- End Icon -->
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
<h3 id="hs-bordered-success-style-label" class="text-gray-800 font-semibold dark:text-white">
|
||||
Успешно отправлено!
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700 dark:text-neutral-400">
|
||||
{{ message }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="max-w-[85rem] py-10 sm:px-6 lg:px-8 lg:py-14 mx-auto">
|
||||
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<div class="text-center">
|
||||
<h2 class="text-xl text-gray-800 font-bold sm:text-3xl">
|
||||
{{ blocks.data.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div class="mt-5 p-4 relative z-10 bg-white border rounded-xl sm:mt-10 md:p-10">
|
||||
<form @submit="submitForm">
|
||||
<component
|
||||
v-for="(block, index) in blocks.data.columns"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
:error="errors && errors[block.data.name_field] ? errors[block.data.name_field] : null"
|
||||
/>
|
||||
<SubmitBlock :block="blocks.data.button" />
|
||||
</form>
|
||||
</div>
|
||||
<!-- End Card -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TextBlock from "@/Components/BuilderUi/Pages/FormBlocks/TextBlock.vue";
|
||||
import PhoneBlock from "@/Components/BuilderUi/Pages/FormBlocks/PhoneBlock.vue";
|
||||
import EmailBlock from "@/Components/BuilderUi/Pages/FormBlocks/EmailBlock.vue";
|
||||
import SubmitBlock from "@/Components/BuilderUi/Pages/FormBlocks/SubmitBlock.vue";
|
||||
import axios from "axios";
|
||||
import TextAreaBlock from "@/Components/BuilderUi/Pages/FormBlocks/TextAreaBlock.vue";
|
||||
import MultipleChoiceBlock from "@/Components/BuilderUi/Pages/FormBlocks/MultipleChoiceBlock.vue";
|
||||
import SingleChoiceBlock from "@/Components/BuilderUi/Pages/FormBlocks/SingleChoiceBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "FormBuilder",
|
||||
components: {
|
||||
SubmitBlock,
|
||||
TextBlock,
|
||||
PhoneBlock,
|
||||
EmailBlock,
|
||||
TextAreaBlock,
|
||||
MultipleChoiceBlock,
|
||||
SingleChoiceBlock
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formData: {},
|
||||
errors: null,
|
||||
success: false,
|
||||
message: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
text: 'TextBlock',
|
||||
phone: 'PhoneBlock',
|
||||
email: 'EmailBlock',
|
||||
textarea: 'TextAreaBlock',
|
||||
multiple_choice: 'MultipleChoiceBlock',
|
||||
single_choice: 'SingleChoiceBlock',
|
||||
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
submitForm(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const formElements = event.target.elements;
|
||||
this.formData = {};
|
||||
for (let i = 0; i < formElements.length; i++) {
|
||||
const element = formElements[i];
|
||||
if (element.tagName === 'INPUT') {
|
||||
// Убираем квадратные скобки из имени поля
|
||||
const fieldName = element.name.endsWith('[]') ? element.name.slice(0, -2) : element.name;
|
||||
|
||||
if (element.type === 'checkbox') {
|
||||
if (!this.formData[fieldName]) {
|
||||
this.formData[fieldName] = [];
|
||||
}
|
||||
if (element.checked) {
|
||||
this.formData[fieldName].push(element.value);
|
||||
}
|
||||
} else if (fieldName && fieldName !== 'choices') {
|
||||
this.formData[fieldName] = element.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Отправляем данные на сервер
|
||||
axios.post(route('client.widget.form.submit', this.blocks.data.id), this.formData)
|
||||
.then(response => {
|
||||
if (response.data.status === 'ok') {
|
||||
this.success = true;
|
||||
this.message = response.data.message;
|
||||
this.errors = null; // Сбрасываем ошибки при успешной отправке
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.errors = error.response.data || ['Неизвестная ошибка']; // Обработка ошибок
|
||||
this.success = false; // Сбрасываем успешное состояние
|
||||
});
|
||||
} },
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="flex justify-between pb-4 items-center">
|
||||
<div class="flex w-full sm:items-center gap-x-5 sm:gap-x-3">
|
||||
<div class="grow">
|
||||
<div class="grid sm:flex sm:justify-between sm:items-center gap-2">
|
||||
<ol v-if="breadcrumbs" class="flex items-center whitespace-normal min-w-0 flex-wrap gap-y-2"
|
||||
aria-label="Breadcrumb">
|
||||
<li class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue">
|
||||
Главная
|
||||
<svg class="flex-shrink-0 mx-3 overflow-visible h-2.5 w-2.5 text-gray-400"
|
||||
width="16" height="16"
|
||||
viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 1L10.6869 7.16086C10.8637 7.35239 10.8637 7.64761 10.6869 7.83914L5 14"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue" @click.prevent>
|
||||
{{ textLimit(this.breadcrumbs.mainSection, 25) }}
|
||||
<svg class="flex-shrink-0 mx-3 overflow-visible h-2.5 w-2.5 text-gray-400"
|
||||
width="16" height="16"
|
||||
viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 1L10.6869 7.16086C10.8637 7.35239 10.8637 7.64761 10.6869 7.83914L5 14"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue" @click.prevent>
|
||||
{{ textLimit(this.breadcrumbs.subSection, 25) }}
|
||||
<svg class="flex-shrink-0 mx-3 overflow-visible h-2.5 w-2.5 text-gray-400"
|
||||
width="16" height="16"
|
||||
viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 1L10.6869 7.16086C10.8637 7.35239 10.8637 7.64761 10.6869 7.83914L5 14"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue" @click.prevent>
|
||||
{{ textLimit(this.breadcrumbs.page, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
<ol v-if="!breadcrumbs" class="flex items-center whitespace-nowrap min-w-0 flex-wrap"
|
||||
aria-label="Breadcrumb">
|
||||
<li class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
Главная
|
||||
<svg class="flex-shrink-0 mx-3 overflow-visible h-2.5 w-2.5 text-gray-400"
|
||||
width="16" height="16"
|
||||
viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 1L10.6869 7.16086C10.8637 7.35239 10.8637 7.64761 10.6869 7.83914L5 14"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-blue-600" @click.prevent>
|
||||
{{ textLimit(pageTitle, 30) }}
|
||||
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "PageBreadcrumbs",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
pageTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HeadingBlock from "@/Components/BuilderUi/Pages/Blocks/HeadingBlock.vue";
|
||||
import ParagraphBlock from "@/Components/BuilderUi/Pages/Blocks/ParagraphBlock.vue";
|
||||
import ClientImageSlider from "@/Components/ClientImageSlider.vue";
|
||||
import ImageBlock from "@/Components/BuilderUi/Pages/Blocks/ImageBlock.vue";
|
||||
import FileBlock from "@/Components/BuilderUi/Pages/Blocks/FileBlock.vue";
|
||||
import PersonBlock from "@/Components/BuilderUi/Pages/Blocks/PersonBlock.vue";
|
||||
import StepperBlock from "@/Components/BuilderUi/Pages/Blocks/StepperBlock.vue";
|
||||
import VideoBlock from "@/Components/BuilderUi/Pages/Blocks/VideoBlock.vue";
|
||||
import TabBlock from "@/Components/BuilderUi/Pages/Blocks/TabBlock.vue";
|
||||
import PostListBlock from "@/Components/BuilderUi/Pages/Blocks/PostListBlock.vue";
|
||||
import PageItemBlock from "@/Components/BuilderUi/Pages/Blocks/PageItemBlock.vue";
|
||||
import PostItemBlock from "@/Components/BuilderUi/Pages/Blocks/PostItemBlock.vue";
|
||||
import FormBlock from "@/Components/BuilderUi/Pages/Blocks/FormBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "PageBuilder",
|
||||
components: {
|
||||
FileBlock,
|
||||
ImageBlock,
|
||||
ClientImageSlider,
|
||||
ParagraphBlock,
|
||||
HeadingBlock,
|
||||
PersonBlock,
|
||||
StepperBlock,
|
||||
VideoBlock,
|
||||
TabBlock,
|
||||
PostListBlock,
|
||||
PageItemBlock,
|
||||
PostItemBlock,
|
||||
FormBlock
|
||||
},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
heading: 'HeadingBlock',
|
||||
paragraph: 'ParagraphBlock',
|
||||
images: 'ClientImageSlider',
|
||||
image: 'ImageBlock',
|
||||
files: 'FileBlock',
|
||||
person: 'PersonBlock',
|
||||
stepper: 'StepperBlock',
|
||||
video: 'VideoBlock',
|
||||
tabs: 'TabBlock',
|
||||
postsList: 'PostListBlock',
|
||||
postItem: 'PostItemBlock',
|
||||
pageItem: 'PageItemBlock',
|
||||
customForm: 'FormBlock'
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="w-full h-[67px] fixed" id="visor"></div>
|
||||
|
||||
<nav class="order-last hidden w-56 shrink-0 lg:block">
|
||||
<div v-if="headerNavs.length > 0" class="sticky top-[100px] h-[calc(100vh-121px)]">
|
||||
<div class="text-gray-1000 mb-2 text-md font-medium">На этой странице</div>
|
||||
<ul class="styled-scrollbar max-h-[70vh] space-y-1.5 overflow-y-auto py-2 text-sm">
|
||||
<li class="anchor-li" v-for="pageNav in headerNavs" :key="pageNav.id">
|
||||
<a :class="{ 'translate-x-2 text-primaryBlue' : currentNavSection === generateSlug(pageNav.text), 'bg-transperant text-gray-600 hover:text-gray-900' : currentNavSection !== generateSlug(pageNav.text) }"
|
||||
class="duration-150 block py-1 px-2 leading-[1.6] rounded-md"
|
||||
:href="'#' + generateSlug(pageNav.text)">{{ pageNav.text }}</a>
|
||||
</li>
|
||||
<transition name="fade">
|
||||
<li class="anchor-li flex items-center py-2 border-t" v-if="scrollTop" @click.prevent="scrollToTop">
|
||||
<button class="bg-transperant text-gray-600 cursor-pointer hover:text-gray-900 duration-300 block px-2 leading-[1.6] rounded-md">К началу</button>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-[17px] text-gray-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 11.25l-3-3m0 0l-3 3m3-3v7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</li>
|
||||
</transition>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Link } from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "PageNavigateLinks",
|
||||
components: { Link },
|
||||
data() {
|
||||
return {
|
||||
currentNavSection: null,
|
||||
scrollTop: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText;
|
||||
LimitedText = text.substring(0, symbols);
|
||||
return LimitedText + "...";
|
||||
}
|
||||
return text;
|
||||
},
|
||||
generateSlug(text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: "ru",
|
||||
});
|
||||
},
|
||||
onScroll(e) {
|
||||
const windowTop = window.scrollY;
|
||||
this.scrollTop = windowTop > 100;
|
||||
|
||||
const headings = document.querySelectorAll("h2");
|
||||
const visor = document.querySelector("#visor");
|
||||
let lastVisibleHeading = null;
|
||||
|
||||
const visorRect = visor.getBoundingClientRect();
|
||||
|
||||
// Проверяем, находится ли визор в пределах видимости
|
||||
if (visorRect.top > window.scrollY) {
|
||||
this.currentNavSection = null;
|
||||
lastVisibleHeading = null; // Сбрасываем заголовок, если визор не виден
|
||||
return; // Выходим из функции, если визор не виден
|
||||
}
|
||||
|
||||
for (let i = 0; i < headings.length; i++) {
|
||||
const heading = headings[i];
|
||||
const rect = heading.getBoundingClientRect();
|
||||
|
||||
// Проверяем, находится ли заголовок в видимой области и касается ли он элемента visor
|
||||
if (
|
||||
rect.top >= 0 &&
|
||||
rect.bottom <= window.innerHeight &&
|
||||
rect.bottom >= visorRect.top &&
|
||||
rect.top <= visorRect.bottom
|
||||
) {
|
||||
// Проверяем, изменился ли заголовок
|
||||
if (heading !== lastVisibleHeading) {
|
||||
this.currentNavSection = heading.id;
|
||||
lastVisibleHeading = heading;
|
||||
}
|
||||
break; // Выходим из цикла, если нашли видимый заголовок
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollToTop() {
|
||||
window.scrollTo(0, 0);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("scroll", this.onScroll);
|
||||
},
|
||||
unmounted() {
|
||||
window.removeEventListener("scroll", this.onScroll);
|
||||
},
|
||||
props: {
|
||||
headerNavs: {
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
|
||||
<div class="sticky top-[100px] hidden h-[calc(100vh-121px)] max-w-[20%] min-w-[20%] md:flex md:shrink-0 md:flex-col md:justify-between">
|
||||
<nav v-if="this.subSectionPages"
|
||||
class="styled-scrollbar flex h-[calc(100vh-200px)] flex-col overflow-y-scroll pr-2 pb-4">
|
||||
<div class="text-gray-1000 mb-2 text-md font-medium">{{ currentSection }}</div>
|
||||
<div class="flex gap-x-1">
|
||||
<ul class="px-0.5 last-of-type:mb-0 mb-8">
|
||||
<li v-for="page in this.subSectionPages.data" :key="page.id" class="my-1.5 flex">
|
||||
<a :class="{'text-white font-semibold bg-primaryBlue': isSameRoute(page.path), 'text-gray-600 hover:text-[#2C6288]': !isSameRoute(page.path) }"
|
||||
:href="(page.is_url) ? page.path : route('page.view', page.path) + '/'"
|
||||
class="relative duration-300 flex gap-x-1 w-full rounded-md cursor-pointer items-center px-2 py-1 text-left text-sm">
|
||||
{{
|
||||
page.title
|
||||
}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "PageSubSectionLinks",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
currentNavSection: null,
|
||||
scrollTop: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isSameRoute(route) {
|
||||
if (this.$page.props.ziggy.location === this.$page.props.ziggy.url + '/' + route) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
props: {
|
||||
subSectionPages: {
|
||||
type: Object,
|
||||
},
|
||||
currentSection: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import HeadingBlock from "@/Components/BuilderUi/Pages/Blocks/HeadingBlock.vue";
|
||||
import ParagraphBlock from "@/Components/BuilderUi/Pages/Blocks/ParagraphBlock.vue";
|
||||
import ClientImageSlider from "@/Components/ClientImageSlider.vue";
|
||||
import ImageBlock from "@/Components/BuilderUi/Pages/Blocks/ImageBlock.vue";
|
||||
import FileBlock from "@/Components/BuilderUi/Pages/Blocks/FileBlock.vue";
|
||||
import PersonBlock from "@/Components/BuilderUi/Pages/Blocks/PersonBlock.vue";
|
||||
import StepperBlock from "@/Components/BuilderUi/Pages/Blocks/StepperBlock.vue";
|
||||
import VideoBlock from "@/Components/BuilderUi/Pages/Blocks/VideoBlock.vue";
|
||||
import PostListBlock from "@/Components/BuilderUi/Pages/Blocks/PostListBlock.vue";
|
||||
|
||||
export default {
|
||||
name: "PageTabBuilder",
|
||||
components: {FileBlock, ImageBlock, ClientImageSlider, ParagraphBlock, HeadingBlock, Link, PersonBlock, StepperBlock, VideoBlock, PostListBlock},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
heading: 'HeadingBlock',
|
||||
paragraph: 'ParagraphBlock',
|
||||
images: 'ClientImageSlider',
|
||||
image: 'ImageBlock',
|
||||
files: 'FileBlock',
|
||||
person: 'PersonBlock',
|
||||
stepper: 'StepperBlock',
|
||||
video: 'VideoBlock',
|
||||
postsList: 'PostListBlock',
|
||||
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
|
||||
<div class="space-y-3">
|
||||
<h1 class="text-2xl mb-10 font-bold md:text-3xl">{{ header }}</h1>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "PageTitle",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
header: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<span v-if="this.filter.value !== null" class="inline-flex items-center gap-x-1.5 py-1.5 ps-3 pe-2 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
||||
{{ (filter.value.length > 1) ? filter.value.length + ' категории' : filter.content[filter.value[0]].data.title }}
|
||||
<button @click.prevent="clearFilter" type="button" class="shrink-0 size-4 inline-flex items-center justify-center rounded-full hover:bg-blue-200 focus:outline-none focus:bg-blue-200 focus:text-blue-500">
|
||||
<span class="sr-only">Remove badge</span>
|
||||
<svg class="shrink-0 size-3" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Link } from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "CategoryBadge",
|
||||
components: { Link },
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
|
||||
// Создаем массив для хранения всех ключей, которые нужно удалить
|
||||
const keysToDelete = [];
|
||||
|
||||
// Перебираем все параметры и добавляем ключи, начинающиеся с 'category', в массив
|
||||
for (const [key] of url.searchParams) {
|
||||
if (key.startsWith(this.filter.param)) {
|
||||
keysToDelete.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем все ключи из массива
|
||||
keysToDelete.forEach(key => url.searchParams.delete(key));
|
||||
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<span v-if="this.filter.value !== null" class="inline-flex items-center gap-x-1.5 py-1.5 ps-3 pe-2 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
||||
Поиск: {{ filter.value }}
|
||||
<button @click.prevent="clearFilter" type="button" class="shrink-0 size-4 inline-flex items-center justify-center rounded-full hover:bg-blue-200 focus:outline-none focus:bg-blue-200 focus:text-blue-500">
|
||||
<span class="sr-only">Remove badge</span>
|
||||
<svg class="shrink-0 size-3" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
export default {
|
||||
name: "SearchBadge",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete(this.filter.param);
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl,{
|
||||
method: 'get',
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<span v-if="this.filter.value !== null" class="inline-flex items-center gap-x-1.5 py-1.5 ps-3 pe-2 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
||||
{{ (filter.value.length > 1) ? 'Тэг: ' + filter.value.length + ' значений' : '#' + JSON.parse(filter.content[filter.value].data.name).ru }}
|
||||
<button @click.prevent="clearFilter" type="button" class="shrink-0 size-4 inline-flex items-center justify-center rounded-full hover:bg-blue-200 focus:outline-none focus:bg-blue-200 focus:text-blue-500">
|
||||
<span class="sr-only">Remove badge</span>
|
||||
<svg class="shrink-0 size-3" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
export default {
|
||||
name: "TagBadge",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
|
||||
// Создаем массив для хранения всех ключей, которые нужно удалить
|
||||
const keysToDelete = [];
|
||||
|
||||
// Перебираем все параметры и добавляем ключи, начинающиеся с 'category', в массив
|
||||
for (const [key] of url.searchParams) {
|
||||
if (key.startsWith(this.filter.param)) {
|
||||
keysToDelete.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем все ключи из массива
|
||||
keysToDelete.forEach(key => url.searchParams.delete(key));
|
||||
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<template v-for="file in block.data.file">
|
||||
<div class="">
|
||||
<a class="" :href="'/storage/'+ file.path" download type="button">
|
||||
|
||||
<div class="flex border rounded-lg px-4 py-2 items-center justify-between duration-300 hover:bg-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="w-[30px] h-[30px] bg-black flex justify-center items-center rounded-md mr-2">
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.9375 12.2188H12.75C12.4682 12.2188 12.198 12.1068 11.9987 11.9076C11.7994 11.7083 11.6875 11.438 11.6875 11.1562V5.84375C11.6875 5.56196 11.7994 5.29171 11.9987 5.09245C12.198 4.89319 12.4682 4.78125 12.75 4.78125H15.9375V5.84375H12.75V11.1562H15.9375V12.2188ZM9.5625 12.2188H7.4375C7.15571 12.2188 6.88546 12.1068 6.6862 11.9076C6.48694 11.7083 6.375 11.438 6.375 11.1562V5.84375C6.375 5.56196 6.48694 5.29171 6.6862 5.09245C6.88546 4.89319 7.15571 4.78125 7.4375 4.78125H9.5625C9.84429 4.78125 10.1145 4.89319 10.3138 5.09245C10.5131 5.29171 10.625 5.56196 10.625 5.84375V11.1562C10.625 11.438 10.5131 11.7083 10.3138 11.9076C10.1145 12.1068 9.84429 12.2188 9.5625 12.2188ZM7.4375 5.84375V11.1562H9.5625V5.84375H7.4375ZM3.1875 12.2188H1.0625V4.78125H3.1875C3.75087 4.78195 4.29096 5.00606 4.68933 5.40442C5.08769 5.80279 5.3118 6.34288 5.3125 6.90625V10.0938C5.3118 10.6571 5.08769 11.1972 4.68933 11.5956C4.29096 11.9939 3.75087 12.218 3.1875 12.2188ZM2.125 11.1562H3.1875C3.46929 11.1562 3.73954 11.0443 3.9388 10.8451C4.13806 10.6458 4.25 10.3755 4.25 10.0938V6.90625C4.25 6.62446 4.13806 6.35421 3.9388 6.15495C3.73954 5.95569 3.46929 5.84375 3.1875 5.84375H2.125V11.1562Z" fill="#F8F8F8"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>{{ file.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "FileBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="md:font-bold md:text-xl text-lg font-medium text-gray-800">{{ block.data.content }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "HeadingBlock",
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<img @click="toggler = !toggler" loading="lazy" class="mx-auto object-cover rounded-md hover:opacity-95 hover:duration-200 transition" :src="'/storage/' + block.data.url" alt="">
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.url]"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "ImageBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="ms-4 mt-2 w-full border px-4 py-4 rounded-xl shadow-sm">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="w-full px-2 sm:px-3 lg:px-4mx-auto">
|
||||
<!-- Grid -->
|
||||
<a class="group flex flex-col bg-white border shadow-sm rounded-xl hover:shadow-md focus:outline-none focus:shadow-md transition"
|
||||
:href="(page.is_url) ? page.path : route('page.view', page.path) + '/'">
|
||||
<div class="p-4 md:p-5">
|
||||
<div class="flex items-center gap-x-5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mt-1 shrink-0 size-7 text-gray-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
|
||||
</svg>
|
||||
<div class="grow">
|
||||
<ol v-if="breadcrumbs" class="flex items-center whitespace-nowrap">
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.mainSection }}
|
||||
</span>
|
||||
<svg class="shrink-0 mx-2 size-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="inline-flex items-center">
|
||||
<span class="flex items-center text-sm text-gray-500 hover:text-blue-600 focus:outline-none focus:text-blue-600" href="#">
|
||||
{{ breadcrumbs.page }}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
<h3 class="mt-1 group-hover:text-blue-600 font-semibold text-gray-700">
|
||||
{{ page.title }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<!-- End Grid -->
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PageItemBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
page: null,
|
||||
breadcrumbs: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPage(id) {
|
||||
axios.get(route('client.widget.page.single', id))
|
||||
.then(response => {
|
||||
this.page = response.data.data.page;
|
||||
this.breadcrumbs = response.data.data.breadcrumbs;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPage(this.block.data.page)
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="text-sm text-gray-600 leading-6 md:text-[16px] md:text-[#374151] md:leading-8 md:font-light paragraph-container" v-html="wrapTables(block).data.content" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "ParagraphBlock",
|
||||
methods: {
|
||||
wrapTables(data) {
|
||||
if (data.type === 'paragraph' && data.data && data.data.content) {
|
||||
// Используем регулярное выражение для поиска всех таблиц
|
||||
const wrappedContent = data.data.content.replace(/<table([^>]*)>([\s\S]*?)<\/table>/g, (match, attrs, content) => {
|
||||
return `<div class="div-table"><table${attrs}>${content}</table></div>`;
|
||||
});
|
||||
|
||||
// Возвращаем новый объект с обновленным контентом
|
||||
return {
|
||||
...data,
|
||||
data: {
|
||||
...data.data,
|
||||
content: wrappedContent
|
||||
}
|
||||
};
|
||||
}
|
||||
return data; // Если тип не 'paragraph', возвращаем объект без изменений
|
||||
}
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.paragraph-container a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.paragraph-container p {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.paragraph-container ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.paragraph-container li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
|
||||
.paragraph-container ul {
|
||||
@apply mb-4
|
||||
}
|
||||
|
||||
.paragraph-container hr {
|
||||
@apply my-4
|
||||
}
|
||||
|
||||
.paragraph-container strong {
|
||||
@apply text-xl
|
||||
}
|
||||
|
||||
.div-table {
|
||||
@apply overflow-x-auto
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container table {
|
||||
@apply w-full border-collapse mt-4 mb-4 overflow-hidden; /* Ширина 100%, стыковка границ, отступы, закругленные края */
|
||||
}
|
||||
|
||||
.paragraph-container th, .paragraph-container td {
|
||||
@apply border border-gray-300 p-3 text-left; /* Границы, отступы, выравнивание текста */
|
||||
}
|
||||
|
||||
.paragraph-container th {
|
||||
@apply bg-gray-100 text-gray-800 font-semibold; /* Фон заголовка, цвет текста, жирный шрифт */
|
||||
}
|
||||
|
||||
.paragraph-container tr {
|
||||
@apply transition-colors duration-200; /* Плавный переход цветов */
|
||||
}
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-200; /* Фон строки при наведении */
|
||||
}
|
||||
|
||||
.paragraph-container td {
|
||||
@apply text-gray-600; /* Цвет текста ячеек */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.paragraph-container tr:hover {
|
||||
@apply bg-gray-100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="w-full rounded-xl mb-4 p-4 md:p-6 bg-white border border-gray-200 dark:bg-slate-900 dark:border-gray-700">
|
||||
<div class="flex items-center gap-x-4">
|
||||
<img @click="toggler = !toggler" loading="lazy" class="rounded-xl w-[150px]" :src="'/storage/' + block.data.photo" alt="Image Description">
|
||||
<div class="grow">
|
||||
<p class="font-medium text-gray-800 hover:text-gray-500">
|
||||
{{ block.data.name }}
|
||||
</p>
|
||||
<template v-for="item in block.data.info">
|
||||
<p class="text-xs text-gray-500 mt-2">
|
||||
{{ item.column }}: {{ item.content }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Social Brands -->
|
||||
<!-- End Social Brands -->
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.photo]"/>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "PersonBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.data.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.data.preview ? 'storage/images/' + post.data.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.data.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
post: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPost(id) {
|
||||
axios.get(route('client.widget.post.single', id), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.post = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPost(this.block.data.post);
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="w-full px-4 py-5 sm:px-6 lg:px-8 lg:py-7 mx-auto">
|
||||
<!-- Проверка на загрузку данных -->
|
||||
|
||||
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex animate-pulse">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<div class="bg-gray-200 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ms-4 mt-2 w-full">
|
||||
<p class="h-4 bg-gray-200 rounded-full" style="width: 40%;"></p>
|
||||
<ul class="mt-5 space-y-3 flex flex-col">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-20 h-4 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-else class="grid lg:grid-cols-1 lg:gap-y-16 gap-10">
|
||||
<template v-for="post in posts.data" :key="post.id">
|
||||
<Link class="group block rounded-xl overflow-hidden focus:outline-none" :href="route('client.post.show', post.slug)">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-5">
|
||||
<div class="shrink-0 relative rounded-xl overflow-hidden w-full sm:w-56 h-44">
|
||||
<img class="group-hover:scale-105 group-focus:scale-105 transition-transform duration-500 ease-in-out size-full absolute top-0 start-0 object-cover rounded-xl"
|
||||
:src="post.preview ? 'storage/images/' + post.preview : '/img/thumbnail-1.png'" />
|
||||
</div>
|
||||
|
||||
<div class="grow">
|
||||
<h3 class="text-xl font-semibold text-gray-800 group-hover:text-gray-600">
|
||||
{{ post.title }}
|
||||
</h3>
|
||||
<p class="mt-3 text-gray-600">
|
||||
Produce professional, reliable streams easily leveraging Preline's innovative broadcast studio
|
||||
</p>
|
||||
<p class="mt-4 inline-flex items-center gap-x-1 text-sm text-primaryBlue decoration-2 group-hover:underline group-focus:underline font-medium">
|
||||
Читать далее
|
||||
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m9 18 6-6-6-6"/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</template>
|
||||
<div class="flex justify-center">
|
||||
<a :href="route('client.post.index', { category: block.data.category })" class="group inline-flex items-center gap-x-1 text-sm font-semibold text-[#1A5AAF]">
|
||||
Все новости
|
||||
<svg class="flex-shrink-0 size-4 transition ease-in-out group-hover:translate-x-1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PostListBlock",
|
||||
components: { axios, Link },
|
||||
data() {
|
||||
return {
|
||||
posts: null,
|
||||
loading: true, // Состояние загрузки
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPosts() {
|
||||
axios.get(route('client.widget.post.index'), {
|
||||
params: {
|
||||
count: this.block.data.count,
|
||||
category: this.block.data.category // Исправлено с count.category на category
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.posts = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPosts();
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(step, index) in block.data.steps">
|
||||
<div class="flex gap-x-3">
|
||||
<div class="w-16 text-end min-w-[4rem]">
|
||||
<span class="text-xs text-gray-500">{{ block.data.step_name }} {{ index + 1 }}</span>
|
||||
</div>
|
||||
<div class="relative last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200">
|
||||
<div class="relative z-10 size-7 flex justify-center items-center">
|
||||
<div class="size-2 rounded-full bg-primaryBlue"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grow max-w-[70%] pt-0.5 pb-8 overflow-wrap break-words">
|
||||
<h3 class="flex gap-x-1.5 font-semibold text-gray-800">
|
||||
{{ step.title }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-600 step-content" v-html="step.content" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "StepperBlock",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.step-content a {
|
||||
@apply text-secondAzure;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
.step-content a:hover {
|
||||
@apply text-secondDarkBlue;
|
||||
@apply underline;
|
||||
}
|
||||
|
||||
|
||||
.step-content ol li {
|
||||
@apply list-decimal list-inside
|
||||
}
|
||||
|
||||
.step-content ul li {
|
||||
@apply list-disc list-inside
|
||||
}
|
||||
|
||||
.step-content li ol {
|
||||
@apply ml-10
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
|
||||
<div class="">
|
||||
<nav class="-mb-0.5 flex justify-center gap-x-2" aria-label="Tabs" role="tablist" aria-orientation="horizontal">
|
||||
<button
|
||||
v-for="(tab, index) in block.data.tab" type="button"
|
||||
class="hs-tab-active:bg-gray-100 rounded-md hs-tab-active:text-gray-700 py-1.5 px-3 inline-flex items-center gap-x-2 border-b-2 border-transparent text-sm whitespace-nowrap text-gray-500 focus:outline-none disabled:opacity-50 disabled:pointer-events-none"
|
||||
:class="(activeTab === index) ? 'active' : ''"
|
||||
@click="activeTab = index"
|
||||
:id="generateSlug(tab.title) + '-item'"
|
||||
:data-hs-tab="'#' + generateSlug(tab.title)"
|
||||
aria-selected="false"
|
||||
:aria-controls="generateSlug(tab.title)" role="tab">
|
||||
{{ tab.title }}
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<template v-for="(tab, index) in block.data.tab">
|
||||
|
||||
<div :id="generateSlug(tab.title)" :class="(activeTab === index) ? '' : 'hidden'"
|
||||
role="tabpanel" :aria-labelledby="generateSlug(tab.title) + '-item'">
|
||||
<PageTabBuilder :blocks="tab.content" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import PageTabBuilder from "@/Components/BuilderUi/Pages/PageTabBuilder.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "TabBlock",
|
||||
components: {
|
||||
PageTabBuilder
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug: function (text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: 'ru'
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<video class="h-full w-full rounded-lg" controls>
|
||||
<source
|
||||
:src="domainPath + '/storage/' + block.data.path"
|
||||
:type="block.data.mime"
|
||||
/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<figcaption class="mt-3 text-sm text-center text-gray-500 dark:text-neutral-500">
|
||||
{{ block.data.title }}
|
||||
</figcaption>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue/v3";
|
||||
|
||||
|
||||
export default {
|
||||
name: "VideoBlock",
|
||||
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<input type="text" v-model="searchTerm" class="py-2 px-3 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500" placeholder="Поиск по категориям">
|
||||
<button @click.prevent="clearFilter" class="text-gray-500 text-sm flex gap-x-1 items-center hover:text-gray-700">
|
||||
<BaseIcon class="w-4 h-4" name="delete" />
|
||||
<span>Очистить</span>
|
||||
</button>
|
||||
|
||||
<div class="divide-y divide-gray-200 dark:divide-gray-700 max-h-[30vh] overflow-y-auto">
|
||||
<div>
|
||||
<div class="flex flex-col ml-3">
|
||||
<div v-for="item in filteredItems" :key="item.id">
|
||||
<input v-model="category_slug" :value="item.slug" @change="filter"
|
||||
type="checkbox" class="shrink-0 mt-0.5 border-gray-200 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
|
||||
:id="'inp' + item.id">
|
||||
<label :for="'inp' + item.id" class="text-sm text-gray-500 ms-3 dark:text-neutral-400">{{ item.title }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import SearchBadge from "@/Components/BuilderUi/Events/Badges/SearchBadge.vue";
|
||||
import CategoryBadge from "@/Components/BuilderUi/Events/Badges/CategoryBadge.vue";
|
||||
import BaseIcon from "@/Components/BaseComponents/BaseIcon.vue";
|
||||
import {debounce} from "lodash/function.js";
|
||||
export default {
|
||||
name: "CategoryFilter",
|
||||
components: {
|
||||
BaseIcon,
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
category_slug: this.category_filter.value || [],
|
||||
tag_slug: [],
|
||||
searchTerm: '',
|
||||
filteredItems: this.categories.data, // Изначально все элементы
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('category[]');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
category: this.category_slug,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('category[]');
|
||||
this.category_slug = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
filteredItems() {
|
||||
if (!this.searchTerm) {
|
||||
return this.categories.data; // Возвращаем все элементы, если ничего не введено
|
||||
}
|
||||
const term = this.searchTerm.toLowerCase(); // Приводим к нижнему регистру для нечувствительности к регистру
|
||||
return this.categories.data.filter(item =>
|
||||
item.title.toLowerCase().includes(term) // Фильтруем по заголовку
|
||||
);
|
||||
}
|
||||
},
|
||||
props: {
|
||||
categories: {
|
||||
type: Object,
|
||||
},
|
||||
category_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user