Rework frontend
This commit is contained in:
+43
@@ -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: "AcademicJournalsTitle",
|
||||
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>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<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 CategoryBadge from "@/componentss/features/additionalEducationPrograms/components/badges/CategoryBadge.vue";
|
||||
import FormBadge from "@/componentss/features/additionalEducationPrograms/components/badges/FormBadge.vue";
|
||||
export default {
|
||||
name: "AdditionalProgramBadge",
|
||||
components: {
|
||||
Link,
|
||||
CategoryBadge,
|
||||
FormBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
category: 'CategoryBadge',
|
||||
form: 'FormBadge',
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
props: {
|
||||
filters: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="flex justify-between 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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.additionalEducation.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Дополнительное образование', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ additionalEducationTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.additionalEducation.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Дополнительное образование', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ additionalEducationTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
|
||||
export default {
|
||||
name: "AdditionalProgramItemBreadcrumbs",
|
||||
components: {BasicIcon, Link, BreadcrumbsWrapper},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
additionalEducationTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.additionalEducation.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Дополнительное образование', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.additionalEducation.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Дополнительное образование', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "AdditionalProgramListBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
eventTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+43
@@ -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: "AdditionalProgramTitle",
|
||||
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>
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<select @change="handleChange" v-model="directionEdu" class="py-3 px-4 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">
|
||||
<option selected value="">Все</option>
|
||||
<option v-for="direction in directions.data" :value="direction.slug">{{ direction.title }}</option>
|
||||
</select>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import {debounce} from "lodash";
|
||||
|
||||
export default {
|
||||
name: "LevelEduFilter",
|
||||
components: {
|
||||
BasicIcon,
|
||||
Link,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
directionEdu: this.direction_filter?.value || '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange() {
|
||||
if (this.directionEdu === "") {
|
||||
this.clearFilter();
|
||||
} else {
|
||||
this.filter();
|
||||
}
|
||||
},
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('direction');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
direction: this.directionEdu,
|
||||
},
|
||||
});
|
||||
}, 200),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('direction');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
directions: {
|
||||
type: Object,
|
||||
},
|
||||
direction_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<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>
|
||||
+59
@@ -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 }}
|
||||
<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: "FormBadge",
|
||||
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>
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<button @click.prevent="clearFilter" class="text-gray-500 text-sm flex gap-x-1 items-center hover:text-gray-700">
|
||||
<BasicIcon 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="(budget, index) in budgets">
|
||||
<input v-model="budgetEdu" :value="index" @change="filter"
|
||||
type="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="'inp-tag' + index">
|
||||
<label :for="'inp-tag' + index" class="text-sm text-gray-500 ms-3 dark:text-neutral-400">{{ budget }}</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 BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import {debounce} from "lodash";
|
||||
export default {
|
||||
name: "BudgetFilter",
|
||||
components: {
|
||||
BasicIcon,
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
budgetEdu: this.budget_filter.value || [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('budget');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
budget: this.budgetEdu,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('budget');
|
||||
this.budgetEdu = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
props: {
|
||||
budgets: {
|
||||
type: Object,
|
||||
},
|
||||
budget_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<button @click.prevent="clearFilter" class="text-gray-500 text-sm flex gap-x-1 items-center hover:text-gray-700">
|
||||
<BasicIcon 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 categories" :key="item.id">
|
||||
<input v-model="category" :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.name }}</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 BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import {debounce} from "lodash";
|
||||
export default {
|
||||
name: "CategoryFilter",
|
||||
components: {
|
||||
BasicIcon,
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
category: this.category_filter.value || [],
|
||||
searchTerm: '',
|
||||
}
|
||||
},
|
||||
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,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('category[]');
|
||||
this.category = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
props: {
|
||||
categories: {
|
||||
type: Object,
|
||||
},
|
||||
category_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<button @click.prevent="clearFilter" class="text-gray-500 text-sm flex gap-x-1 items-center hover:text-gray-700">
|
||||
<BasicIcon 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="(form, index) in forms">
|
||||
<input v-model="formEdu" :value="index" @change="filter"
|
||||
type="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="'inp-tag' + index">
|
||||
<label :for="'inp-tag' + index" class="text-sm text-gray-500 ms-3 dark:text-neutral-400">{{ form }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import {debounce} from "lodash";
|
||||
export default {
|
||||
name: "FormEducationalFilter",
|
||||
components: {
|
||||
BasicIcon,
|
||||
Link
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formEdu: this.formEdu_filter.value || [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('form');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
form: this.formEdu,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('form');
|
||||
this.formEdu = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
props: {
|
||||
forms: {
|
||||
type: Object,
|
||||
},
|
||||
formEdu_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+72
@@ -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 BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import {debounce} from "lodash";
|
||||
export default {
|
||||
name: "SortingByFilter",
|
||||
components: {
|
||||
BasicIcon,
|
||||
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>
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.faculty.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Факультеты', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.faculty.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Факультет', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ departmentTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.faculty.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Факультеты', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.faculty.show', faculty.slug)" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ faculty.title }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ departmentTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "DepartmentItemBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
departmentTitle: {
|
||||
type: String,
|
||||
},
|
||||
faculty: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
|
||||
<div class="flex flex-col rounded-xl 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-y-4 gap-x-4 flex-wrap md:flex-nowrap">
|
||||
<img v-if="teacher.photo" @click="toggler = !toggler" loading="lazy" class="rounded-xl md:w-[150px]" :src="'/storage/' + teacher.photo" alt="Image Description">
|
||||
<div class="grow">
|
||||
<Link :href="route('client.person.show', teacher.slug)" class="font-medium text-gray-800 hover:text-gray-500 underline">
|
||||
{{ teacher.position }}: {{ teacher.name }}
|
||||
</Link>
|
||||
<p v-if="teacher.academicTitle !== null" class="text-xs text-gray-500 mt-2">
|
||||
Ученая степень: {{ teacher.academicTitle }}
|
||||
</p>
|
||||
<p v-if="teacher.contactPhone" class="text-xs text-gray-500 mt-2">
|
||||
Контактный телефон: {{ teacher.contactPhone }}
|
||||
</p>
|
||||
<p v-if="teacher.contactEmail" class="text-xs text-gray-500 mt-2">
|
||||
Электронная почта: {{ teacher.contactEmail }}
|
||||
</p>
|
||||
<p v-if="teacher.cabinet" class="text-xs text-gray-500 mt-2">
|
||||
Кабинет: {{ teacher.cabinet }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="['/storage/' + teacher.photo]"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
import {Head, Link} from "@inertiajs/vue3";
|
||||
import MainNavbar from "@/Navbars/MainNavbar.vue";
|
||||
import FsLightbox from "fslightbox-vue";
|
||||
export default {
|
||||
name: "DepartmentTeacherCard",
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
teacher: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
MainNavbar,
|
||||
Link,
|
||||
FsLightbox,
|
||||
Head
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
|
||||
<div class="flex flex-col rounded-xl p-4 md:p-6 bg-white border border-gray-200">
|
||||
<div class="flex items-center gap-y-4 gap-x-4 flex-wrap md:flex-nowrap">
|
||||
<img v-if="worker.photo" @click="toggler = !toggler" loading="lazy" class="rounded-xl md:w-[150px]" :src="'/storage/' + worker.photo" alt="Image Description">
|
||||
<div class="grow">
|
||||
<Link :href="route('client.person.show', worker.slug)" class="font-medium text-gray-800 hover:text-gray-500 underline">
|
||||
{{ worker.position }}: {{ worker.name }}
|
||||
</Link>
|
||||
<p v-if="worker.academicTitle !== null" class="text-xs text-gray-500 mt-2">
|
||||
Ученая степень: {{ worker.academicTitle }}
|
||||
</p>
|
||||
<p v-if="worker.contactPhone" class="text-xs text-gray-500 mt-2">
|
||||
Контактный телефон: {{ worker.contactPhone }}
|
||||
</p>
|
||||
<p v-if="worker.contactEmail" class="text-xs text-gray-500 mt-2">
|
||||
Электронная почта: {{ worker.contactEmail }}
|
||||
</p>
|
||||
<p v-if="worker.cabinet" class="text-xs text-gray-500 mt-2">
|
||||
Кабинет: {{ worker.cabinet }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="['/storage/' + worker.photo]"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
import {Head, Link} from "@inertiajs/vue3";
|
||||
import MainNavbar from "@/Navbars/MainNavbar.vue";
|
||||
import FsLightbox from "fslightbox-vue";
|
||||
export default {
|
||||
name: "DepartmentWorkerCard",
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
worker: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
MainNavbar,
|
||||
Link,
|
||||
FsLightbox,
|
||||
Head
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,208 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.division.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Подразделения института', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ divisionTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.division.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Подразделения института', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ divisionTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "DivisionItemBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
divisionTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,186 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.program.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Образовательные программы', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.division.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Подразделения института', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
|
||||
export default {
|
||||
name: "DivisionListBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
eventTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="max-w-2xl text-center mx-auto mb-10 lg:mb-14">
|
||||
<h2 class="text-2xl font-bold md:text-4xl md:leading-tight dark:text-white">{{ header }}</h2>
|
||||
<p class="mt-1 text-gray-600 dark:text-neutral-400">{{ bottomText }}</p>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "DivisionListTitle",
|
||||
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,
|
||||
required: true
|
||||
},
|
||||
bottomText: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
|
||||
<div class="flex flex-col rounded-xl 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-y-4 gap-x-4 flex-wrap md:flex-nowrap">
|
||||
<img v-if="worker.photo" @click="toggler = !toggler" loading="lazy" class="rounded-xl md:w-[150px]" :src="'/storage/' + worker.photo" alt="Image Description">
|
||||
<div class="grow">
|
||||
<Link :href="route('client.person.show', worker.slug)" class="font-medium text-gray-800 hover:text-gray-500 underline">
|
||||
{{ worker.administrativePosition }}: {{ worker.name }}
|
||||
</Link>
|
||||
<p v-if="worker.academicTitle !== null" class="text-xs text-gray-500 mt-2">
|
||||
Ученая степень: {{ worker.academicTitle }}
|
||||
</p>
|
||||
<p v-if="worker.contactPhone" class="text-xs text-gray-500 mt-2">
|
||||
Контактный телефон: {{ worker.contactPhone }}
|
||||
</p>
|
||||
<p v-if="worker.contactEmail" class="text-xs text-gray-500 mt-2">
|
||||
Электронная почта: {{ worker.contactEmail }}
|
||||
</p>
|
||||
<p v-if="worker.cabinet" class="text-xs text-gray-500 mt-2">
|
||||
Кабинет: {{ worker.cabinet }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="['/storage/' + worker.photo]"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
import {Head, Link} from "@inertiajs/vue3";
|
||||
import MainNavbar from "@/Navbars/MainNavbar.vue";
|
||||
import FsLightbox from "fslightbox-vue";
|
||||
export default {
|
||||
name: "DivisionWorkerCard",
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
worker: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
MainNavbar,
|
||||
Link,
|
||||
FsLightbox,
|
||||
Head
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.program.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Образовательные программы', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ programTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.program.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Образовательные программы', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ programTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
|
||||
export default {
|
||||
name: "ProgramItemBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
programTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<select @change="filter" v-model="levelEdu" class="py-3 px-4 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">
|
||||
<option @click.prevent="clearFilter" selected value="">Все</option>
|
||||
<option v-for="(level, key) in levels" :value="key">{{ level }}</option>
|
||||
</select>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
import {debounce} from "lodash";
|
||||
|
||||
export default {
|
||||
name: "ProgramLevelEduFilter",
|
||||
components: {
|
||||
Link,
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
levelEdu: this.level_filter.value || '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('level');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
level: this.levelEdu,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('level');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
props: {
|
||||
levels: {
|
||||
type: Object,
|
||||
},
|
||||
level_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.program.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Образовательные программы', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.program.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Образовательные программы', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
|
||||
export default {
|
||||
name: "ProgramListBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
eventTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</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: "ProgramTitle",
|
||||
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,197 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.event.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
|
||||
{{ textLimit('Мероприятия', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.event.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Мероприятия', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.event.archive')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Архив', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "EventArchiveListBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
eventTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,207 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.event.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Мероприятия', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="$page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ eventTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.event.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Мероприятия', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="$page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ eventTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "EventItemBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
eventTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,186 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.event.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
|
||||
{{ textLimit('Мероприятия', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.event.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Мероприятия', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "EventListBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
eventTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
|
||||
<div class="">
|
||||
<!--Фильтр-->
|
||||
|
||||
<button type="button" class="py-3 px-4 inline-flex 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" aria-haspopup="dialog" aria-expanded="false" aria-controls="hs-offcanvas-example" data-hs-overlay="#hs-offcanvas-example">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
|
||||
<!--Конец Фильтра-->
|
||||
</div>
|
||||
<div id="hs-offcanvas-example" class="hs-overlay hs-overlay-open:translate-x-0 hidden -translate-x-full fixed top-0 start-0 transition-all duration-300 transform h-full max-w-xs w-full z-[80] bg-white border-e overflow-auto" role="dialog" tabindex="-1" aria-labelledby="hs-offcanvas-example-label">
|
||||
<div class="flex justify-between items-center py-3 px-4 border-b">
|
||||
<h3 id="hs-offcanvas-example-label" class="font-bold text-gray-800">
|
||||
Фильтры
|
||||
</h3>
|
||||
<button type="button" class="size-8 inline-flex justify-center items-center gap-x-2 rounded-full border border-transparent bg-gray-100 text-gray-800 hover:bg-gray-200 focus:outline-none focus:bg-gray-200 disabled:opacity-50 disabled:pointer-events-none" aria-label="Close" data-hs-overlay="#hs-offcanvas-example">
|
||||
<span class="sr-only">Close</span>
|
||||
<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="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<IsOnlineFilter :is_online_filter="is_online_filter" />
|
||||
<div class="hs-accordion-group">
|
||||
<div class="hs-accordion" :id="'id' + category_filter.type">
|
||||
<button class="hs-accordion-toggle hs-accordion-active:text-blue-600 py-3 inline-flex items-center gap-x-3 w-full font-semibold text-start text-gray-800 hover:text-gray-500 focus:outline-none focus:text-gray-500 rounded-lg disabled:opacity-50 disabled:pointer-events-none" aria-expanded="false" aria-controls="hs-basic-with-arrow-collapse-two">
|
||||
<svg class="hs-accordion-active:hidden block 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="m6 9 6 6 6-6"></path>
|
||||
</svg>
|
||||
<svg class="hs-accordion-active:block hidden 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="m18 15-6-6-6 6"></path>
|
||||
</svg>
|
||||
Категории
|
||||
<span v-if="category_filter.value" class="inline-flex items-center gap-x-1.5 py-1.5 px-3 rounded-md text-xs font-medium border border-gray-200 bg-white text-gray-800 shadow-sm dark:bg-neutral-900 dark:border-neutral-700 dark:text-white">{{ category_filter.value.length }}</span>
|
||||
|
||||
</button>
|
||||
<div id="hs-basic-with-arrow-collapse-two" class="hs-accordion-content hidden w-full overflow-hidden transition-[height] duration-300" role="region" :aria-labelledby="'id' + category_filter.type">
|
||||
<CategoryFilter :categories="categories" :category_filter="category_filter" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import CategoryFilter from "@/componentss/shared/filter/filters/CategoryFilter.vue";
|
||||
import IsOnlineFilter from "@/componentss/shared/filter/filters/IsOnlineFilter.vue";
|
||||
import SortingByFilter from "@/componentss/shared/filter/filters/SortingByFilter.vue";
|
||||
import TagFilter from "@/componentss/shared/filter/filters/TagFilter.vue";
|
||||
|
||||
export default {
|
||||
name: "EventListFilter",
|
||||
components: {IsOnlineFilter, SortingByFilter, TagFilter, CategoryFilter, BasicIcon},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
categories: {
|
||||
type: Object,
|
||||
},
|
||||
category_filter: {
|
||||
type: Object,
|
||||
},
|
||||
sortingBy_filter: {
|
||||
type: Object,
|
||||
},
|
||||
is_online_filter: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</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-3 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";
|
||||
|
||||
export default {
|
||||
name: "EventListSearch",
|
||||
data() {
|
||||
return {
|
||||
searchInput: this.search_filter.value,
|
||||
}
|
||||
},
|
||||
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: [
|
||||
'search_filter'
|
||||
],
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="relative rounded-xl overflow-auto">
|
||||
<div class="max-w-4xl mx-auto bg-white min-w-0">
|
||||
<div class="overflow-x-scroll flex no-scrollbar">
|
||||
<div class="flex items-center gap-x-3 whitespace-nowrap">
|
||||
<Link :href="route('client.event.archive')" class="text-[11px] text-center text-gray-500 hover:underline">Перейти <br> в архив</Link>
|
||||
<template v-for="eventMonth in dates">
|
||||
<div class="">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[12px] text-gray-500">{{ eventMonth.month }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="flex">
|
||||
<template v-for="eventDate in eventMonth.events">
|
||||
<div class="flex flex-col items-center">
|
||||
<button :class="date === eventDate.date ? 'active-button' : ''" @click="filter(eventDate.date)" type="button" class="min-h-[38px] duration-300 ease-linear min-w-[38px] flex justify-center items-center text-gray-800 hover:bg-gray-100 py-2 px-3 text-sm rounded-lg focus:outline-none focus:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none">{{ eventDate.day }}</button>
|
||||
<span class="text-[12px] text-gray-500">{{ eventDate.dayOfWeek }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "EventListSelectDate",
|
||||
components: {
|
||||
|
||||
Link,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
date: this.currentDate,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter(eventDate) {
|
||||
this.date = eventDate
|
||||
this.$inertia.reload( {
|
||||
data: {
|
||||
date: eventDate,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
dates: {
|
||||
type: Array,
|
||||
},
|
||||
currentDate: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.active-button {
|
||||
@apply bg-blue-600 text-white;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,207 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.faculty.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Факультеты', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ facultyTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.faculty.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Факультеты', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="this.$inertia.page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ facultyTitle }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "FacultyItemBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
facultyTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,185 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(this.breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(this.breadcrumbs.mainSection, this.breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(this.breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.faculty.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Факультеты', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.faculty.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Факультеты', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "FacultyListBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
eventTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="max-w-2xl text-center mx-auto mb-10 lg:mb-14">
|
||||
<h2 class="text-2xl font-bold md:text-4xl md:leading-tight dark:text-white">{{ header }}</h2>
|
||||
<p class="mt-1 text-gray-600 dark:text-neutral-400">{{ bottomText }}</p>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "FacultyListTitle",
|
||||
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,
|
||||
required: true
|
||||
},
|
||||
bottomText: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
|
||||
<div class="flex flex-col rounded-xl 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-y-4 gap-x-4 flex-wrap md:flex-nowrap">
|
||||
<img v-if="worker.photo" @click="toggler = !toggler" loading="lazy" class="rounded-xl md:w-[150px]" :src="'/storage/' + worker.photo" alt="Image Description">
|
||||
<div class="grow">
|
||||
<Link :href="route('client.person.show', worker.slug)" class="font-medium text-gray-800 hover:text-gray-500 underline">
|
||||
{{ worker.position }}: {{ worker.name }}
|
||||
</Link>
|
||||
<p v-if="worker.academicTitle !== null" class="text-xs text-gray-500 mt-2">
|
||||
Ученая степень: {{ worker.academicTitle }}
|
||||
</p>
|
||||
<p v-if="worker.contactPhone" class="text-xs text-gray-500 mt-2">
|
||||
Контактный телефон: {{ worker.contactPhone }}
|
||||
</p>
|
||||
<p v-if="worker.contactEmail" class="text-xs text-gray-500 mt-2">
|
||||
Электронная почта: {{ worker.contactEmail }}
|
||||
</p>
|
||||
<p v-if="worker.cabinet" class="text-xs text-gray-500 mt-2">
|
||||
Кабинет: {{ worker.cabinet }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="['/storage/' + worker.photo]"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
import {Head, Link} from "@inertiajs/vue3";
|
||||
import MainNavbar from "@/Navbars/MainNavbar.vue";
|
||||
import FsLightbox from "fslightbox-vue";
|
||||
export default {
|
||||
name: "FacultyWorkerCard",
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
worker: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
MainNavbar,
|
||||
Link,
|
||||
FsLightbox,
|
||||
Head
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,185 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(breadcrumbs.mainSection, breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('page.view', breadcrumbs.page.data.path)" class="flex items-center text-gray-500 hover:text-primaryBlue">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.page.data.title, 25) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(pageTitle, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "PageBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
pageTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="w-full h-[67px] fixed pointer-events-none bvi-no-styles" 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="max-h-[70vh] space-y-1.5 overflow-hidden 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,80 @@
|
||||
<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="subSectionPages"
|
||||
class="flex h-[calc(100vh-200px)] flex-col overflow-hidden pr-2 pb-4">
|
||||
<div class="text-gray-1000 mb-2 text-md font-medium">{{ currentSection }}</div>
|
||||
<div>
|
||||
<ul class="px-0.5 last-of-type:mb-0 mb-8">
|
||||
<li v-for="page in subSectionPages.data" :key="page.id" class="my-1.5 flex">
|
||||
<a :class="{'text-white font-normal bg-primaryBlue border': isSameRoute(page.path), 'text-gray-600 hover:text-[#2C6288]': !isSameRoute(page.path) }"
|
||||
:href="(page.is_url) ? page.path : route('page.view', page.path) + '/'"
|
||||
class="duration-300 flex gap-x-2 w-full rounded-md cursor-pointer items-center px-2 py-1.5 text-left text-sm">
|
||||
<BasicIcon class="w-4" :name="page.icon" />
|
||||
{{
|
||||
page.title
|
||||
}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
export default {
|
||||
name: "PageSubSectionLinks",
|
||||
components: {Link, BasicIcon},
|
||||
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
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// console.log(IconsMap)
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
props: {
|
||||
subSectionPages: {
|
||||
type: Object,
|
||||
},
|
||||
currentSection: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<img @click="toggler = !toggler" class="shrink-0 w-full md:w-[200px] md:h-[300px] rounded-xl object-cover" :src="'/storage/' + photo" alt="Avatar">
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + photo]"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue";
|
||||
|
||||
export default {
|
||||
name: "PersonAvatarBlock",
|
||||
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: {
|
||||
photo: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.fslightbox-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,192 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(breadcrumbs.mainSection, breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="$page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ personName }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="$page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ personName }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
|
||||
export default {
|
||||
name: "PersonBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
default: () => ({}), // Default to an empty object
|
||||
},
|
||||
personName: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!-- NavLink.vue -->
|
||||
<template>
|
||||
<a :class="linkClass"
|
||||
class="duration-150 block py-1 px-2 leading-[1.6] rounded-md"
|
||||
:href="href">
|
||||
{{ label }}
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PersonNavLink',
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
href: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
linkClass() {
|
||||
return {
|
||||
'translate-x-2 text-[#135aae]': this.isActive,
|
||||
'bg-transperant text-gray-600 hover:text-gray-900': !this.isActive
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Добавьте стили, если это необходимо */
|
||||
</style>
|
||||
@@ -0,0 +1,126 @@
|
||||
<!-- NavLink.vue -->
|
||||
<template>
|
||||
<div class="flex items-center gap-x-10 gap-y-4 flex-wrap">
|
||||
<PersonAvatarBlock v-if="person.data.details.photo" :photo="person.data.details.photo" />
|
||||
<div class="grow">
|
||||
<h1 class="text-2xl font-medium text-gray-800 dark:text-neutral-200">
|
||||
{{ person.data.name }}
|
||||
</h1>
|
||||
<div class="">
|
||||
<template v-for="division_work in person.data.divisions_works">
|
||||
<Link :href="route('client.division.show', division_work.slug)" class="text-sm text-blue-500 dark:text-neutral-400 mr-2">
|
||||
{{ division_work.position }}
|
||||
</Link>
|
||||
</template>
|
||||
<template v-for="faculty_work in person.data.faculties_works">
|
||||
<Link :href="route('client.faculty.show', faculty_work.slug)" class="text-sm text-blue-500 dark:text-neutral-400 mr-2">
|
||||
{{ faculty_work.position }}
|
||||
</Link>
|
||||
</template>
|
||||
<template v-for="division_work in person.data.departments_work">
|
||||
<Link :href="route('client.department.show', { facultySlug: division_work.faculty_slug, departmentSlug: division_work.slug })" class="text-sm text-blue-500 dark:text-neutral-400 mr-2">
|
||||
{{ division_work.position }}
|
||||
</Link>
|
||||
</template>
|
||||
<template v-for="division_tech in person.data.departments_teach">
|
||||
<Link :href="route('client.department.show', { facultySlug: division_tech.faculty_slug, departmentSlug: division_tech.slug })" class="text-sm text-blue-500 dark:text-neutral-400 mr-2">
|
||||
{{ division_tech.position }}
|
||||
</Link>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<ul class="mt-5 flex flex-col gap-y-3">
|
||||
|
||||
<li v-if="person.data.details.contactPhone" class="flex items-center gap-x-2.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="shrink-0 size-3.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z" />
|
||||
</svg>
|
||||
<a class="text-[13px] text-gray-500 underline hover:text-gray-800 hover:decoration-2 focus:outline-none focus:decoration-2 dark:text-neutral-500 dark:hover:text-neutral-400" href="#">
|
||||
{{ person.data.details.contactPhone }}
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="person.data.details.contactEmail" class="flex items-center gap-x-2.5">
|
||||
<svg class="shrink-0 size-3.5" 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">
|
||||
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||
</svg>
|
||||
<a class="text-[13px] text-gray-500 underline hover:text-gray-800 hover:decoration-2 focus:outline-none focus:decoration-2 dark:text-neutral-500 dark:hover:text-neutral-400" href="#">
|
||||
{{ person.data.details.contactEmail }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5 sm:mt-7">
|
||||
<EducationBlock v-if="person.data.details.education.length > 0" title="Направление подготовки" :educations="person.data.details.education" />
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<ListBlock v-if="person.data.details.awards.length > 0" name-list="Награды" :list="person.data.details.awards" />
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<ListBlock v-if="person.data.details.professionalRetraining.length > 0" name-list="Профессиональная переподготовка" :list="person.data.details.professionalRetraining" />
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<ListBlock v-if="person.data.details.professionalDevelopment.length > 0" name-list="Повышение квалификации" :list="person.data.details.professionalDevelopment" />
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<ListBlock v-if="person.data.details.professDisciplines.length > 0" name-list="Преподаваемые дисциплины" :list="person.data.details.professDisciplines" />
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<WorkExperienceBlock v-if="person.data.details.workExperience.total" title="Стаж" :work-exp-by-prof="person.data.details.workExperience.byProf" :work-exp-total="person.data.details.workExperience.total" />
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<ListBlock v-if="person.data.details.attendedConferences.length > 0" name-list="Участие в конференциях" :list="person.data.details.attendedConferences" />
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<PublicationBlock v-if="person.data.details.publications.length > 0" name-list="Публикации" :list="person.data.details.publications" />
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<OtherBlock v-if="person.data.details.other?.length > 0" name-list="Другое" :blocks="person.data.details.other" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import OtherBlock from "@/componentss/features/persons/components/sections/blocks/OtherBlock.vue";
|
||||
import EducationBlock from "@/componentss/features/persons/components/sections/blocks/EducationBlock.vue";
|
||||
import ListBlock from "@/componentss/features/persons/components/sections/blocks/ListBlock.vue";
|
||||
import PersonAvatarBlock from "@/componentss/features/persons/components/PersonAvatarBlock.vue";
|
||||
import WorkExperienceBlock from "@/componentss/features/persons/components/sections/blocks/WorkExperienceBlock.vue";
|
||||
import PublicationBlock from "@/componentss/features/persons/components/sections/blocks/PublicationBlock.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: 'InfoSection',
|
||||
components: {OtherBlock, EducationBlock, ListBlock, PersonAvatarBlock, Link, WorkExperienceBlock, PublicationBlock},
|
||||
props: {
|
||||
person: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
linkClass() {
|
||||
return {
|
||||
'translate-x-2 text-[#135aae]': this.isActive,
|
||||
'bg-transperant text-gray-600 hover:text-gray-900': !this.isActive
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Добавьте стили, если это необходимо */
|
||||
</style>
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<!-- NavLink.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<h2 :id="generateSlug(title)" class="mb-3 font-medium text-gray-800 dark:text-neutral-200">
|
||||
{{ title }}
|
||||
</h2>
|
||||
|
||||
<template v-for="education in educations">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-1 my-4">
|
||||
<div class="p-4 border border-gray-200 rounded-lg dark:border-neutral-700">
|
||||
<h3 class="mb-1 text-xs text-gray-600 dark:text-neutral-400">
|
||||
{{ education.year }}
|
||||
</h3>
|
||||
|
||||
<p class="font-semibold text-sm text-gray-800 dark:text-neutral-200">
|
||||
{{ education.content }}
|
||||
</p>
|
||||
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-neutral-400">
|
||||
{{ education.institution }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: 'EducationBlock',
|
||||
props: {
|
||||
educations: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSlug(text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: "ru",
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Добавьте стили, если это необходимо */
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<!-- NavLink.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<h2 :id="generateSlug(nameList)" class="mb-3 font-medium text-gray-800 dark:text-neutral-200">
|
||||
{{ nameList }}
|
||||
</h2>
|
||||
<ul class="list-disc ms-6 mt-3 space-y-1.5">
|
||||
<template v-for="el in list">
|
||||
<li class="ps-1 text-sm text-gray-600 dark:text-neutral-400">
|
||||
{{ el.item }}
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: 'ListBlock',
|
||||
props: {
|
||||
list: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
nameList: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
generateSlug(text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: "ru",
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Добавьте стили, если это необходимо */
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!-- NavLink.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<h2 :id="generateSlug(nameList)" class="mb-3 font-medium text-gray-800 dark:text-neutral-200">
|
||||
{{ nameList }}
|
||||
</h2>
|
||||
<Builder :blocks="blocks" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import Builder from "@/componentss/shared/builder/pageBuilder/Builder.vue";
|
||||
|
||||
export default {
|
||||
name: 'OtherBlock',
|
||||
components: {Builder},
|
||||
props: {
|
||||
blocks: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
nameList: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
generateSlug(text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: "ru",
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Добавьте стили, если это необходимо */
|
||||
</style>
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<!-- NavLink.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="el in list">
|
||||
<h2 :id="generateSlug(el.category_publication)" class="my-4 font-medium text-gray-800">{{ el.category_publication }}</h2>
|
||||
<ul class="list-disc ms-6 mt-3 space-y-1.5">
|
||||
<template v-for="el in el.publication">
|
||||
<li class="ps-1 text-sm text-gray-600 dark:text-neutral-400">
|
||||
{{ el.item }}
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: 'PublicationBlock',
|
||||
props: {
|
||||
list: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
nameList: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
generateSlug(text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: "ru",
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Добавьте стили, если это необходимо */
|
||||
</style>
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!-- NavLink.vue -->
|
||||
<template>
|
||||
<h2 :id="generateSlug(title)" class="mb-3 font-medium text-gray-800 dark:text-neutral-200">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-1 gap-3">
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-neutral-400">
|
||||
Стаж работы: {{ workExpTotal }}
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-neutral-400">
|
||||
Стаж по специальности: {{ workExpByProf }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: 'WorkExperienceBlock',
|
||||
props: {
|
||||
workExpTotal: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
workExpByProf: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
generateSlug(text) {
|
||||
return slugify(text, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
locale: "ru",
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Добавьте стили, если это необходимо */
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
|
||||
<div class="col-start-2">
|
||||
<div class="hs-tooltip inline-block">
|
||||
<button type="button" class="hs-tooltip-toggle underline hover:text-blue-400 duration-300">
|
||||
Над статьей работали
|
||||
<span class="hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute duration-300 invisible z-10 py-1 px-2 bg-gray-900 text-xs font-medium text-white rounded shadow-sm dark:bg-neutral-700" role="tooltip">
|
||||
<template v-for="author in authors">
|
||||
{{ author }} <br>
|
||||
</template>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "PostAuthorsList",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
authors: {
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
|
||||
<div v-if="images.length" class="grid gap-4 border-t border-gray-300 py-4">
|
||||
<div class="relative">
|
||||
<img loading="lazy"
|
||||
class="filter brightness-[0.8] w-full max-h-[500px] object-cover rounded-lg hover:opacity-95 hover:duration-200 transition"
|
||||
:src="'/storage/' + images[0]" data-hs-overlay="#modal-post-gallery" alt="">
|
||||
<div class="absolute inset-x-0 bottom-5 flex flex-col justify-center items-center">
|
||||
<p class="text-white text-sm lg:text-xl text-center">{{ title }}</p>
|
||||
<span class="text-gray-300 text-sm">{{ images.length }} фотографий</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-post-gallery" class="hs-overlay hidden size-full fixed top-0 start-0 z-[80] overflow-x-hidden overflow-y-auto pointer-events-none" role="dialog" tabindex="-1" aria-labelledby="modal-post-gallery-label">
|
||||
<div class="hs-overlay-open:mt-0 hs-overlay-open:opacity-100 hs-overlay-open:duration-500 mt-10 opacity-0 transition-all max-w-full max-h-full h-full">
|
||||
<div class="flex flex-col bg-white pointer-events-auto max-w-full max-h-full h-full dark:bg-neutral-800">
|
||||
<div class="flex justify-between items-center py-3 px-4 border-b dark:border-neutral-700">
|
||||
<h3 id="modal-post-gallery-label" class="font-bold text-gray-800 dark:text-white">
|
||||
Галлерея: {{ title }}
|
||||
</h3>
|
||||
<button type="button" class="size-8 inline-flex justify-center items-center gap-x-2 rounded-full border border-transparent bg-gray-100 text-gray-800 hover:bg-gray-200 focus:outline-none focus:bg-gray-200 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-700 dark:hover:bg-neutral-600 dark:text-neutral-400 dark:focus:bg-neutral-600" aria-label="Close" data-hs-overlay="#modal-post-gallery">
|
||||
<span class="sr-only">Close</span>
|
||||
<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="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 overflow-y-auto">
|
||||
<div class="grid grid-cols-2 sm:grid-cols-5 gap-3">
|
||||
<div v-for="(image, index) in images" class="group block relative overflow-hidden rounded-lg">
|
||||
<img loading="lazy" @click="openLightboxOnSlide(index + 1)" class="w-full size-40 object-cover bg-gray-100 rounded-lg" :src="'/storage/' + image">
|
||||
<div class="absolute bottom-1 end-1 opacity-0 group-hover:opacity-100 transition">
|
||||
<div class="flex items-center gap-x-1 py-1 px-2 bg-white border border-gray-200 text-gray-800 rounded-lg">
|
||||
<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"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<span class="text-xs">Открыть</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<FsLightbox class="" :slide="slide" :toggler="toggler" :sources="images.map(image => domainPath + '/storage/' + image)"/>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import FsLightbox from "fslightbox-vue";
|
||||
export default {
|
||||
name: "PostGallery",
|
||||
components: {Link, FsLightbox},
|
||||
data() {
|
||||
return {
|
||||
toggler: false,
|
||||
slide: 1,
|
||||
domainPath: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
openLightboxOnSlide: function(number) {
|
||||
this.slide = number;
|
||||
this.toggler = !this.toggler;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
|
||||
props: {
|
||||
images: {
|
||||
type: Array,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,209 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(breadcrumbs.mainSection, breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.post.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Новости', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="$page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(postTitle, 60) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.post.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
{{ textLimit('Новости', 30) }}
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="$page.props.ziggy.location" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
{{ textLimit(postTitle, 60) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "PostItemBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 1024; // Проверка на мобильные устройства
|
||||
}
|
||||
return false; // По умолчанию возвращаем false, когда не в браузере
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
default: () => ({}), // Default to an empty object
|
||||
},
|
||||
postTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(filter, index) in filters"
|
||||
:key="index"
|
||||
:is="getComponent(filter.type)"
|
||||
:filter="filter"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import SearchBadge from "@/componentss/shared/badge/badges/SearchBadge.vue";
|
||||
import CategoryBadge from "@/componentss/shared/badge/badges/CategoryBadge.vue";
|
||||
import TagBadge from "@/componentss/shared/badge/badges/TagBadge.vue";
|
||||
export default {
|
||||
name: "PostListBadge",
|
||||
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,180 @@
|
||||
<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">
|
||||
<BreadcrumbsWrapper v-if="breadcrumbs">
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.mainSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSectionClick(breadcrumbs.mainSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.mainSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="breadcrumbs.subSection" class="text-sm">
|
||||
<span class="flex items-center text-gray-500 hover:text-primaryBlue cursor-pointer" @click.prevent="handleSubSectionClick(breadcrumbs.mainSection, breadcrumbs.subSection)">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit(breadcrumbs.subSection.data.title, 25) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.post.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Новости', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
<BreadcrumbsWrapper v-else>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('index')" class="flex items-center text-gray-500 hover:text-blue-600" href="/">
|
||||
<BasicIcon class="size-5" name="home" />
|
||||
</Link>
|
||||
</li>
|
||||
<li class="text-sm">
|
||||
<Link :href="route('client.post.index')" class="flex items-center text-gray-500 hover:text-blue-600">
|
||||
<svg class="flex-shrink-0 mx-2 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>
|
||||
{{ textLimit('Новости', 30) }}
|
||||
</Link>
|
||||
</li>
|
||||
</BreadcrumbsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import BreadcrumbsWrapper from "@/componentss/ui/wrappers/BreadcrumbsWrapper.vue";
|
||||
export default {
|
||||
name: "PostListBreadcrumbs",
|
||||
components: {BreadcrumbsWrapper, BasicIcon, Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
isMobileDevice() {
|
||||
return window.innerWidth < 1024; // Задайте порог для мобильных устройств
|
||||
},
|
||||
handleSectionClick(breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSection(breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSection(breadcrumb)
|
||||
}
|
||||
},
|
||||
handleSubSectionClick(mainSectionBreadcrumb, breadcrumb) {
|
||||
if (this.isMobileDevice()) {
|
||||
this.toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
} else {
|
||||
this.toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb)
|
||||
}
|
||||
},
|
||||
highlightNavItem(item) {
|
||||
item.classList.add('animate-pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
item.classList.remove('animate-pulse');
|
||||
}, 4000);
|
||||
},
|
||||
openMobileNavMenu() {
|
||||
const openMobileNavBtn = document.getElementById('open-mobile-btn');
|
||||
openMobileNavBtn.click();
|
||||
},
|
||||
toggleMobileNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
this.openMobileNavMenu()
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + mainSectionBreadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
if (!sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
const subSectionNavBlock = mobileNavElement.querySelector('#nav-sub-section-accordion-' + breadcrumb.data.slug);
|
||||
const subSectionNavBlockBtn = mobileNavElement.querySelector('#nav-sub-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (subSectionNavBlock.classList.contains('active')) {
|
||||
subSectionNavBlockBtn.click()
|
||||
}
|
||||
|
||||
this.highlightNavItem(subSectionNavBlock)
|
||||
},
|
||||
toggleMobileNavSection(breadcrumb) {
|
||||
const mobileNavElement = document.getElementById('open-mobile-nav');
|
||||
const sectionNavBlock = mobileNavElement.querySelector('#nav-section-accordion-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = mobileNavElement.querySelector('#nav-section-accordion-btn-' + breadcrumb.data.slug);
|
||||
if (sectionNavBlock.classList.contains('active')) {
|
||||
sectionNavBlockBtn.click()
|
||||
}
|
||||
this.openMobileNavMenu()
|
||||
|
||||
this.highlightNavItem(sectionNavBlock)
|
||||
|
||||
},
|
||||
toggleDesktopNavSubSection(mainSectionBreadcrumb, breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavTitle = desktopNavElement.querySelector('#nav-sub-section-title-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + mainSectionBreadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
this.highlightNavItem(sectionNavTitle)
|
||||
},
|
||||
toggleDesktopNavSection(breadcrumb) {
|
||||
const desktopNavElement = document.getElementById('desktop-nav');
|
||||
const sectionNavBlock = desktopNavElement.querySelector('#nav-section-menu-' + breadcrumb.data.slug);
|
||||
const sectionNavBlockBtn = desktopNavElement.querySelector('#nav-section-btn-' + breadcrumb.data.slug);
|
||||
sectionNavBlockBtn.click()
|
||||
// this.highlightNavItem(sectionNavBlock)
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
breadcrumbs: {
|
||||
type: Object,
|
||||
},
|
||||
postTitle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
|
||||
<div class="sm:col-span-2 md:grow">
|
||||
<!--Фильтр-->
|
||||
|
||||
<button type="button" class="py-3 px-4 inline-flex 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" aria-haspopup="dialog" aria-expanded="false" aria-controls="hs-offcanvas-example" data-hs-overlay="#hs-offcanvas-example">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
|
||||
<!--Конец Фильтра-->
|
||||
</div>
|
||||
<div id="hs-offcanvas-example" class="hs-overlay hs-overlay-open:translate-x-0 hidden -translate-x-full fixed top-0 start-0 transition-all duration-300 transform h-full max-w-xs w-full z-[80] bg-white border-e overflow-auto" role="dialog" tabindex="-1" aria-labelledby="hs-offcanvas-example-label">
|
||||
<div class="flex justify-between items-center py-3 px-4 border-b">
|
||||
<h3 id="hs-offcanvas-example-label" class="font-bold text-gray-800">
|
||||
Фильтры
|
||||
</h3>
|
||||
<button type="button" class="size-8 inline-flex justify-center items-center gap-x-2 rounded-full border border-transparent bg-gray-100 text-gray-800 hover:bg-gray-200 focus:outline-none focus:bg-gray-200 disabled:opacity-50 disabled:pointer-events-none" aria-label="Close" data-hs-overlay="#hs-offcanvas-example">
|
||||
<span class="sr-only">Close</span>
|
||||
<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="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<SortingByFilter :sortingBy_filter="sortingBy_filter" />
|
||||
<div class="hs-accordion-group">
|
||||
<div class="hs-accordion" :id="'id' + category_filter.type">
|
||||
<button class="hs-accordion-toggle hs-accordion-active:text-blue-600 py-3 inline-flex items-center gap-x-3 w-full font-semibold text-start text-gray-800 hover:text-gray-500 focus:outline-none focus:text-gray-500 rounded-lg disabled:opacity-50 disabled:pointer-events-none" aria-expanded="false" aria-controls="hs-basic-with-arrow-collapse-two">
|
||||
<svg class="hs-accordion-active:hidden block 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="m6 9 6 6 6-6"></path>
|
||||
</svg>
|
||||
<svg class="hs-accordion-active:block hidden 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="m18 15-6-6-6 6"></path>
|
||||
</svg>
|
||||
Категории
|
||||
<span v-if="category_filter.value" class="inline-flex items-center gap-x-1.5 py-1.5 px-3 rounded-md text-xs font-medium border border-gray-200 bg-white text-gray-800 shadow-sm dark:bg-neutral-900 dark:border-neutral-700 dark:text-white">{{ category_filter.value.length }}</span>
|
||||
|
||||
</button>
|
||||
<div id="hs-basic-with-arrow-collapse-two" class="hs-accordion-content hidden w-full overflow-hidden transition-[height] duration-300" role="region" :aria-labelledby="'id' + tag_filter.type">
|
||||
<CategoryFilter :categories="items" :category_filter="category_filter" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
export default {
|
||||
name: "PostListFilter",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
items: {
|
||||
type: Object,
|
||||
},
|
||||
category_filter: {
|
||||
type: Object,
|
||||
},
|
||||
sortingBy_filter: {
|
||||
type: Object,
|
||||
},
|
||||
tag_filter: {
|
||||
type: Object,
|
||||
},
|
||||
tags: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="group cursor-pointer">
|
||||
<div
|
||||
class="overflow-hidden rounded-md max-h-[250px] bg-gray-100 transition-all hover:scale-105 dark:bg-gray-800"
|
||||
>
|
||||
<a
|
||||
class="relative block aspect-square"
|
||||
:href="route('client.post.show', post.slug)"
|
||||
><img
|
||||
alt="Thumbnail"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
data-nimg="fill"
|
||||
class="object-cover transition-all"
|
||||
style="
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
inset: 0px;
|
||||
color: transparent;
|
||||
"
|
||||
sizes="(max-width: 768px) 30vw, 33vw"
|
||||
:src="post.preview ? '/storage/' + post.preview : '/img/thumbnail-1.png'"
|
||||
/></a>
|
||||
</div>
|
||||
<div class="">
|
||||
<div>
|
||||
<div v-if="post.category" class="flex gap-3">
|
||||
<a :href="route('client.post.index', { 'category[]': post.category.slug })">
|
||||
<span class="inline-block text-xs font-medium tracking-wider uppercase mt-5 text-blue-600">
|
||||
{{ post.category ? post.category.title : "Новости" }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-else class="flex gap-3">
|
||||
<span>
|
||||
<span class="inline-block text-xs font-medium tracking-wider uppercase mt-5 text-blue-600">
|
||||
Новости
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<h2
|
||||
class="text-lg font-semibold leading-snug tracking-tight mt-2 dark:text-white"
|
||||
>
|
||||
<a :href="route('client.post.show', post.slug)"
|
||||
><span class="duration-200 group-hover:text-gray-500">{{
|
||||
post.title
|
||||
}}</span></a
|
||||
>
|
||||
</h2>
|
||||
<div class="">
|
||||
<p
|
||||
class="mt-2 line-clamp-3 text-sm text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
<a
|
||||
:href="route('client.post.show', post.slug)"
|
||||
>{{ post.preview_text }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="mt-3 flex items-center space-x-3 text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
<span>
|
||||
<div class="flex items-center gap-3">
|
||||
<span v-if="post && post.authors && post.authors.length > 0" class="truncate text-sm">
|
||||
{{ textLimit(post.authors[0], 20) }}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span class="text-xs text-gray-300 dark:text-gray-600"
|
||||
>•</span
|
||||
>
|
||||
<span class="truncate text-sm">{{ post.created_post }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "PostListItem",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText;
|
||||
LimitedText = text.substring(0, symbols);
|
||||
return LimitedText + "...";
|
||||
}
|
||||
return text;
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
post: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</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-3 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";
|
||||
|
||||
export default {
|
||||
name: "PostListSearch",
|
||||
data() {
|
||||
return {
|
||||
searchInput: this.search_filter.value,
|
||||
}
|
||||
},
|
||||
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: [
|
||||
'search_filter'
|
||||
],
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
|
||||
<span class="block">Чтение займет {{ time }}<!-- --> Минуты</span>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "PostTimeRead",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
time: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="min-w-[12rem] py-1 space-y-3">
|
||||
<button @click.prevent="clearFilter" class="text-gray-500 text-sm flex gap-x-1 items-center hover:text-gray-700">
|
||||
<BasicIcon 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="(form, index) in forms">
|
||||
<input v-model="formEdu" :value="index" @change="filter"
|
||||
type="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="'inp-tag' + index">
|
||||
<label :for="'inp-tag' + index" class="text-sm text-gray-500 ms-3 dark:text-neutral-400">{{ form }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
import {debounce} from "lodash";
|
||||
export default {
|
||||
name: "FormEducationalFilter",
|
||||
components: {
|
||||
BasicIcon,
|
||||
Link,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formEdu: this.formEdu_filter.value || [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filter: debounce(function () {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('page');
|
||||
url.searchParams.delete('form');
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
data: {
|
||||
form: this.formEdu,
|
||||
},
|
||||
});
|
||||
}, 500),
|
||||
clearFilter() {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.delete('form');
|
||||
this.formEdu = [];
|
||||
this.searchTerm = ''; // Сбросить поле поиска
|
||||
let newUrl = url.toString();
|
||||
this.$inertia.visit(newUrl, {
|
||||
method: 'get',
|
||||
preserveState: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
props: {
|
||||
forms: {
|
||||
type: Object,
|
||||
},
|
||||
formEdu_filter: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<!--Фильтр-->
|
||||
<button type="button"
|
||||
aria-haspopup="dialog" aria-expanded="false" aria-controls="hs-offcanvas-example" data-hs-overlay="#hs-offcanvas-example"
|
||||
class="flex w-full py-2 px-4 items-center gap-x-2 text-xs font-medium rounded-lg border border-gray-200 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-primaryBlue focus:text-white disabled:opacity-50 disabled:pointer-events-none">
|
||||
<BasicIcon class="shrink-0 size-4" name="settings" />
|
||||
<span>Фильтры</span>
|
||||
</button>
|
||||
|
||||
|
||||
<!--Конец Фильтра-->
|
||||
<div id="hs-offcanvas-example" class="hs-overlay hs-overlay-open:translate-x-0 hidden -translate-x-full fixed top-0 start-0 transition-all duration-300 transform h-full max-w-xs w-full z-[80] bg-white border-e overflow-auto" role="dialog" tabindex="-1" aria-labelledby="hs-offcanvas-example-label">
|
||||
<div class="flex justify-between items-center py-3 px-4 border-b">
|
||||
<h3 id="hs-offcanvas-example-label" class="font-bold text-gray-800">
|
||||
Фильтры
|
||||
</h3>
|
||||
<button type="button" class="size-8 inline-flex justify-center items-center gap-x-2 rounded-full border border-transparent bg-gray-100 text-gray-800 hover:bg-gray-200 focus:outline-none focus:bg-gray-200 disabled:opacity-50 disabled:pointer-events-none" aria-label="Close" data-hs-overlay="#hs-offcanvas-example">
|
||||
<span class="sr-only">Close</span>
|
||||
<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="M18 6 6 18"></path>
|
||||
<path d="m6 6 12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="hs-accordion-group">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
|
||||
export default {
|
||||
name: "ScheduleListFilter",
|
||||
components: {BasicIcon},
|
||||
|
||||
|
||||
props: {
|
||||
faculty_filter: {
|
||||
type: Object,
|
||||
},
|
||||
formEdu_filter: {
|
||||
type: Object
|
||||
},
|
||||
forms_educational: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<h1 class="text-2xl sm:text-4xl font-bold text-gray-800 text-center">
|
||||
{{ header }}
|
||||
</h1>
|
||||
<p class="mt-3 text-gray-600 text-center">
|
||||
{{ bottomText }}
|
||||
</p>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import slugify from "slugify";
|
||||
|
||||
export default {
|
||||
name: "ScheduleListTitle",
|
||||
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,
|
||||
required: true
|
||||
},
|
||||
bottomText: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="relative px-3 pt-3 flex justify-start items-center overflow-x-auto whitespace-nowrap hide-scrollbar">
|
||||
<button @click="categorySort(null)" :class="selectedCategory === null ? 'bg-blue-50 text-blue-500 border-blue-300' : ''" class="hover:bg-gray-50 duration-150 border border-[#EAEAEA] rounded font-light text-[13px] px-1 mr-2" type="button">Все</button>
|
||||
<template v-for="category in categories">
|
||||
<button @click="categorySort(category)"
|
||||
:class="{
|
||||
'bg-blue-50': selectedCategory === category,
|
||||
'text-blue-500': selectedCategory === category,
|
||||
'border-blue-300': selectedCategory === category,
|
||||
}"
|
||||
class="hover:bg-gray-50 duration-150 border border-[#EAEAEA] rounded font-light text-[13px] px-1 mr-2" type="button">{{ category }}</button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {debounce} from "lodash";
|
||||
|
||||
export default {
|
||||
name: "CategorySearchList",
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: null,
|
||||
categories: null,
|
||||
paginate: null,
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
categorySort: debounce(function (category) {
|
||||
this.loading = true
|
||||
this.selectedCategory = category; // Устанавливаем выбранную категорию
|
||||
this.$emit('update:selected-category', this.selectedCategory, this.categories, this.paginate); // Эмитируем событие
|
||||
axios({
|
||||
method: "get",
|
||||
url: route('client.search.index'),
|
||||
params: {
|
||||
search: this.search.toLowerCase(),
|
||||
category: category
|
||||
}
|
||||
}).then((response) => {
|
||||
this.paginate = response.data.paginate;
|
||||
this.result = response.data.searchRes;
|
||||
this.categories = response.data.result_type;
|
||||
this.loading = false;
|
||||
});
|
||||
}, 500),
|
||||
},
|
||||
props: {
|
||||
categories: {
|
||||
type: Array,
|
||||
},
|
||||
search: {
|
||||
type: String,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div class="border-b">
|
||||
<div v-if="result !== null && result.length !== 0" class="relative px-3 pt-3 flex justify-start items-center overflow-x-auto whitespace-nowrap hide-scrollbar">
|
||||
<button @click="categorySort(null)"
|
||||
:class="{
|
||||
'bg-blue-50': selectedCategory === null,
|
||||
'text-blue-500': selectedCategory === null,
|
||||
'border-blue-300': selectedCategory === null,
|
||||
'animate-pulse': loading === true
|
||||
}"
|
||||
class="hover:bg-gray-50 duration-150 border border-[#EAEAEA] rounded font-light text-[13px] px-1 mr-2" type="button">Все</button>
|
||||
<template v-for="category in categories">
|
||||
<button @click="categorySort(category)"
|
||||
:class="{
|
||||
'bg-blue-50': selectedCategory === category,
|
||||
'text-blue-500': selectedCategory === category,
|
||||
'border-blue-300': selectedCategory === category,
|
||||
'animate-pulse': loading === true
|
||||
}"
|
||||
class="hover:bg-gray-50 duration-150 border border-[#EAEAEA] rounded font-light text-[13px] px-1 mr-2" type="button">
|
||||
{{ruCategories()[category] }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<div class="relative px-1 py-2 flex justify-between items-center">
|
||||
<input autofocus="autofocus" 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-1 placeholder-[#8F8F8F] font-light px-2 block flex-1 w-full border-none text-lg focus:z-10 border-transparent focus:border-transparent focus:ring-0"
|
||||
placeholder="Поиск по сайту">
|
||||
<button :data-hs-overlay="'#' + open_id" class="hover:bg-gray-50 duration-150 border border-[#EAEAEA] rounded font-light text-[13px] px-1 mr-4" type="button">Esc</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto relative">
|
||||
<ResultList :result="result" :loading="loading" :selected-category="selectedCategory" />
|
||||
</div>
|
||||
<nav v-if="paginate" class="bg-white border border-gray-100 flex items-center gap-x-1 fixed bottom-4 w-[200px] justify-center left-0 right-0 mx-auto py-2 rounded-xl " aria-label="Pagination">
|
||||
<button @click="prev(paginate.prev_page)" :disabled="!paginate.prev_page" type="button" class="min-h-[38px] min-w-[38px] py-2 px-2.5 inline-flex justify-center items-center gap-x-2 text-sm rounded-lg text-gray-800 hover:bg-gray-100 focus:outline-none disabled:opacity-50 disabled:pointer-events-none" aria-label="Previous">
|
||||
<svg class="shrink-0 size-3.5" 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"></path>
|
||||
</svg>
|
||||
<span class="sr-only">Previous</span>
|
||||
</button>
|
||||
<div class="flex items-center gap-x-1">
|
||||
<span class="min-h-[38px] min-w-[38px] flex justify-center items-center border border-gray-200 text-gray-800 py-2 px-3 text-sm rounded-lg focus:outline-none focus:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none">{{ paginate.current_page }}</span>
|
||||
<span class="min-h-[38px] flex justify-center items-center text-gray-500 py-2 px-1.5 text-sm">из</span>
|
||||
<span class="min-h-[38px] flex justify-center items-center text-gray-500 py-2 px-1.5 text-sm">{{ paginate.last_page }}</span>
|
||||
</div>
|
||||
<button @click="next(paginate.next_page)" :disabled="!paginate.next_page" type="button" class="min-h-[38px] min-w-[38px] py-2 px-2.5 inline-flex justify-center items-center gap-x-2 text-sm rounded-lg text-gray-800 hover:bg-gray-100 focus:outline-none disabled:opacity-50 disabled:pointer-events-none" aria-label="Next">
|
||||
<span class="sr-only">Next</span>
|
||||
<svg class="shrink-0 size-3.5" 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>
|
||||
</button>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {debounce} from "lodash";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import {ruCategories} from "@/assets/data/ruCategories.js";
|
||||
import ResultList from "@/componentss/features/search/components/ResultList.vue";
|
||||
export default {
|
||||
name: "GlobalSearch",
|
||||
data() {
|
||||
return {
|
||||
searchInput: "",
|
||||
result: null,
|
||||
paginate: null,
|
||||
selectedCategory: null,
|
||||
categories: null,
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ResultList,
|
||||
Link,
|
||||
},
|
||||
methods: {
|
||||
ruCategories() {
|
||||
return ruCategories
|
||||
},
|
||||
categorySort: debounce(function(category) {
|
||||
this.toggleLoadingState(true);
|
||||
axios({
|
||||
method: "get",
|
||||
url: route('client.search.index'),
|
||||
params: {
|
||||
search: this.searchInput.toLowerCase(),
|
||||
category: category
|
||||
}
|
||||
}).then((response) => {
|
||||
this.paginate = response.data.paginate;
|
||||
this.result = response.data.searchRes;
|
||||
this.categories = response.data.result_type;
|
||||
this.selectedCategory = response.data.selectedCategory;
|
||||
this.toggleLoadingState(false);
|
||||
}).catch((error) => {
|
||||
console.error("Ошибка при получении данных:", error);
|
||||
this.toggleLoadingState(false);
|
||||
});
|
||||
}, 500),
|
||||
search: debounce(function () {
|
||||
axios({
|
||||
method: "get",
|
||||
url: route('client.search.index'),
|
||||
params: {
|
||||
search: this.searchInput.toLowerCase(),
|
||||
}
|
||||
}).then((response) => {
|
||||
this.paginate = response.data.paginate
|
||||
this.result = response.data.searchRes;
|
||||
this.categories = response.data.result_type
|
||||
});
|
||||
}, 500),
|
||||
|
||||
next: debounce(function (page) {
|
||||
this.toggleLoadingState(true);
|
||||
axios({
|
||||
method: "get",
|
||||
url: route('client.search.index'),
|
||||
params: {
|
||||
search: this.searchInput.toLowerCase(),
|
||||
page: page,
|
||||
category: this.selectedCategory
|
||||
}
|
||||
}).then((response) => {
|
||||
this.paginate = response.data.paginate
|
||||
this.result = response.data.searchRes;
|
||||
this.toggleLoadingState(false);
|
||||
|
||||
});
|
||||
}, 500),
|
||||
|
||||
prev: debounce(function (page) {
|
||||
this.toggleLoadingState(true);
|
||||
axios({
|
||||
method: "get",
|
||||
url: route('client.search.index'),
|
||||
params: {
|
||||
search: this.searchInput.toLowerCase(),
|
||||
page: page,
|
||||
category: this.selectedCategory
|
||||
}
|
||||
}).then((response) => {
|
||||
this.paginate = response.data.paginate
|
||||
this.result = response.data.searchRes;
|
||||
this.toggleLoadingState(false);
|
||||
});
|
||||
}, 500),
|
||||
|
||||
toggleLoadingState(state) {
|
||||
this.loading = state
|
||||
},
|
||||
|
||||
|
||||
textLimit(text, symbols) {
|
||||
if (text.length > symbols) {
|
||||
let LimitedText
|
||||
LimitedText = text.substring(0, symbols)
|
||||
return LimitedText + "..."
|
||||
}
|
||||
return text
|
||||
},
|
||||
},
|
||||
props: {
|
||||
open_id: {
|
||||
type: String,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none; /* Скрыть scrollbar для WebKit-браузеров */
|
||||
}
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none; /* Для Internet Explorer и Edge */
|
||||
scrollbar-width: none; /* Для Firefox */
|
||||
}
|
||||
|
||||
</style>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<Link v-if="item.tag === 'AdditionalEducation'" data-hs-overlay="#hs-basic-modal" :href="route('client.additionalEducation.show', item.data.id)" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 16.875h3.375m0 0h3.375m-3.375 0V13.5m0 3.375v3.375M6 10.5h2.25a2.25 2.25 0 0 0 2.25-2.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v2.25A2.25 2.25 0 0 0 6 10.5Zm0 9.75h2.25A2.25 2.25 0 0 0 10.5 18v-2.25a2.25 2.25 0 0 0-2.25-2.25H6a2.25 2.25 0 0 0-2.25 2.25V18A2.25 2.25 0 0 0 6 20.25Zm9.75-9.75H18a2.25 2.25 0 0 0 2.25-2.25V6A2.25 2.25 0 0 0 18 3.75h-2.25A2.25 2.25 0 0 0 13.5 6v2.25a2.25 2.25 0 0 0 2.25 2.25Z" />
|
||||
</svg>
|
||||
|
||||
|
||||
<span class="text-sm">{{ item.data.title }}</span>
|
||||
</Link>
|
||||
<div v-if="item.matches.length !== 0" class="">
|
||||
<template v-for="match in item.matches.slice(0,3)">
|
||||
<div class="flex gap-3 px-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<div class="ml-[8px] flex h-12 w-full items-center gap-2 overflow-hidden border-l border-gray-200 px-4 pl-[22px] text-sm text-gray-900 md:h-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" />
|
||||
</svg>
|
||||
<p class="truncate">{{ match }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "ResultAdditionalEducationItem",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: 'All',
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div v-if="item.tag === 'EducationalGroup'" data-hs-overlay="#hs-basic-modal"
|
||||
class="my-1 flex gap-3 px-2 py-2 items-center rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||
class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5m-9-6h.008v.008H12v-.008ZM12 15h.008v.008H12V15Zm0 2.25h.008v.008H12v-.008ZM9.75 15h.008v.008H9.75V15Zm0 2.25h.008v.008H9.75v-.008ZM7.5 15h.008v.008H7.5V15Zm0 2.25h.008v.008H7.5v-.008Zm6.75-4.5h.008v.008h-.008v-.008Zm0 2.25h.008v.008h-.008V15Zm0 2.25h.008v.008h-.008v-.008Zm2.25-4.5h.008v.008H16.5v-.008Zm0 2.25h.008v.008H16.5V15Z"/>
|
||||
</svg>
|
||||
<span class="text-sm">{{ item.data.title }}</span>
|
||||
</div>
|
||||
<template v-for="schedule in item.data.schedules">
|
||||
<div class="mb-2">
|
||||
<div class="flex gap-3 px-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<Link href="/"
|
||||
class="ml-[8px] flex h-12 w-full items-center gap-2 overflow-hidden border-l border-gray-200 px-4 pl-[22px] text-sm text-gray-900 md:h-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5"/>
|
||||
</svg>
|
||||
<p class="truncate">{{ schedule.title }}</p>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "ResultEducationalGroupItem",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: 'All',
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<Link v-if="item.tag === 'EducationalProgram'" data-hs-overlay="#hs-basic-modal" :href="route('client.program.show', item.data.slug)" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.62 48.62 0 0 1 12 20.904a48.62 48.62 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.636 50.636 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.717 50.717 0 0 1 12 13.489a50.702 50.702 0 0 1 7.74-3.342M6.75 15a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 0v-3.675A55.378 55.378 0 0 1 12 8.443m-7.007 11.55A5.981 5.981 0 0 0 6.75 15.75v-1.5" />
|
||||
</svg>
|
||||
|
||||
<span class="text-sm">{{ item.data.name }}</span>
|
||||
</Link>
|
||||
<div v-if="item.matches.length !== 0" class="">
|
||||
<template v-for="match in item.matches.slice(0,3)">
|
||||
<div class="flex gap-3 px-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<div class="ml-[8px] flex h-12 w-full items-center gap-2 overflow-hidden border-l border-gray-200 px-4 pl-[22px] text-sm text-gray-900 md:h-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" />
|
||||
</svg>
|
||||
<p class="truncate">{{ match }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "ResultEducationalProgramItem",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: 'All',
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<Link v-if="item.tag === 'Event'" data-hs-overlay="#hs-basic-modal" :href="route('client.event.show', item.data.slug)" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 2.994v2.25m10.5-2.25v2.25m-14.252 13.5V7.491a2.25 2.25 0 0 1 2.25-2.25h13.5a2.25 2.25 0 0 1 2.25 2.25v11.251m-18 0a2.25 2.25 0 0 0 2.25 2.25h13.5a2.25 2.25 0 0 0 2.25-2.25m-18 0v-7.5a2.25 2.25 0 0 1 2.25-2.25h13.5a2.25 2.25 0 0 1 2.25 2.25v7.5m-6.75-6h2.25m-9 2.25h4.5m.002-2.25h.005v.006H12v-.006Zm-.001 4.5h.006v.006h-.006v-.005Zm-2.25.001h.005v.006H9.75v-.006Zm-2.25 0h.005v.005h-.006v-.005Zm6.75-2.247h.005v.005h-.005v-.005Zm0 2.247h.006v.006h-.006v-.006Zm2.25-2.248h.006V15H16.5v-.005Z" />
|
||||
</svg>
|
||||
|
||||
<span class="text-sm">{{ item.data.title }}</span>
|
||||
</Link>
|
||||
<div v-if="item.matches.length !== 0" class="">
|
||||
<template v-for="match in item.matches.slice(0,3)">
|
||||
<div class="flex gap-3 px-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<div class="ml-[8px] flex h-12 w-full items-center gap-2 overflow-hidden border-l border-gray-200 px-4 pl-[22px] text-sm text-gray-900 md:h-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" />
|
||||
</svg>
|
||||
<p class="truncate">{{ match }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "ResultEventItem",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: 'All',
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<a v-if="item.tag === 'Faculty'" data-hs-overlay="#hs-basic-modal" :href="route('client.faculty.show', item.data.slug)" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
|
||||
</svg>
|
||||
<span class="text-sm">{{ item.data.title }}</span>
|
||||
</a>
|
||||
<!-- <div v-if="item.matches.length !== 0" class="">-->
|
||||
<!-- <template v-for="match in item.matches.slice(0,3)">-->
|
||||
<!-- <div class="flex gap-3 px-2 items-center hover:bg-[#F2F2F2] rounded-lg">-->
|
||||
<!-- <div class="ml-[8px] flex h-12 w-full items-center gap-2 overflow-hidden border-l border-gray-200 px-4 pl-[22px] text-sm text-gray-900 md:h-10">-->
|
||||
<!-- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">-->
|
||||
<!-- <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" />-->
|
||||
<!-- </svg>-->
|
||||
<!-- <p class="truncate">{{ match }}</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "ResultFacultyItem",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: 'All',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<Link v-if="item.tag === 'Page' && item.data.is_url == false" data-hs-overlay="#hs-basic-modal" :href="route('page.view', item.data.path) + '/'" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<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>
|
||||
<span class="text-sm">{{ item.data.title }}</span>
|
||||
</Link>
|
||||
<a v-if="item.tag === 'Page' && item.data.is_url == true" data-hs-overlay="#hs-basic-modal" :href="item.data.path" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<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>
|
||||
<span class="text-sm">{{ item.data.title }}</span>
|
||||
</a>
|
||||
<!-- <div v-if="item.matches.length !== 0" class="">-->
|
||||
<!-- <template v-for="match in item.matches.slice(0,3)">-->
|
||||
<!-- <div class="flex gap-3 px-2 items-center hover:bg-[#F2F2F2] rounded-lg">-->
|
||||
<!-- <div class="ml-[8px] flex h-12 w-full items-center gap-2 overflow-hidden border-l border-gray-200 px-4 pl-[22px] text-sm text-gray-900 md:h-10">-->
|
||||
<!-- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">-->
|
||||
<!-- <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" />-->
|
||||
<!-- </svg>-->
|
||||
<!-- <p class="truncate">{{ match }}</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- </div>-->
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "ResultPageItem",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: 'All',
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<a v-if="item.tag === 'User'" data-hs-overlay="#hs-basic-modal" :href="route('client.person.show', item.data.slug)" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
||||
</svg>
|
||||
<span class="text-sm">{{ item.data.name }}</span>
|
||||
</a>
|
||||
<!-- <div v-if="item.matches.length !== 0" class="">-->
|
||||
<!-- <template v-for="match in item.matches.slice(0,3)">-->
|
||||
<!-- <div class="flex gap-3 px-2 items-center hover:bg-[#F2F2F2] rounded-lg">-->
|
||||
<!-- <div class="ml-[8px] flex h-12 w-full items-center gap-2 overflow-hidden border-l border-gray-200 px-4 pl-[22px] text-sm text-gray-900 md:h-10">-->
|
||||
<!-- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">-->
|
||||
<!-- <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" />-->
|
||||
<!-- </svg>-->
|
||||
<!-- <p class="truncate">{{ match }}</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "ResultPersonItem",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: 'All',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<a v-if="item.tag === 'Post'" data-hs-overlay="#hs-basic-modal" :href="route('client.post.show', item.data.slug)" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 7.5h1.5m-1.5 3h1.5m-7.5 3h7.5m-7.5 3h7.5m3-9h3.375c.621 0 1.125.504 1.125 1.125V18a2.25 2.25 0 0 1-2.25 2.25M16.5 7.5V18a2.25 2.25 0 0 0 2.25 2.25M16.5 7.5V4.875c0-.621-.504-1.125-1.125-1.125H4.125C3.504 3.75 3 4.254 3 4.875V18a2.25 2.25 0 0 0 2.25 2.25h13.5M6 7.5h3v3H6v-3Z" />
|
||||
</svg>
|
||||
<span class="text-sm">{{ item.data.title }}</span>
|
||||
</a>
|
||||
<!-- <div v-if="item.matches.length !== 0" class="">-->
|
||||
<!-- <template v-for="match in item.matches.slice(0,3)">-->
|
||||
<!-- <div class="flex gap-3 px-2 items-center hover:bg-[#F2F2F2] rounded-lg">-->
|
||||
<!-- <div class="ml-[8px] flex h-12 w-full items-center gap-2 overflow-hidden border-l border-gray-200 px-4 pl-[22px] text-sm text-gray-900 md:h-10">-->
|
||||
<!-- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">-->
|
||||
<!-- <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" />-->
|
||||
<!-- </svg>-->
|
||||
<!-- <p class="truncate">{{ match }}</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "ResultPostItem",
|
||||
components: {Link},
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: 'All',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<Link data-hs-overlay="#hs-basic-modal" :href="route('client.post.index')" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 7.5h1.5m-1.5 3h1.5m-7.5 3h7.5m-7.5 3h7.5m3-9h3.375c.621 0 1.125.504 1.125 1.125V18a2.25 2.25 0 0 1-2.25 2.25M16.5 7.5V18a2.25 2.25 0 0 0 2.25 2.25M16.5 7.5V4.875c0-.621-.504-1.125-1.125-1.125H4.125C3.504 3.75 3 4.254 3 4.875V18a2.25 2.25 0 0 0 2.25 2.25h13.5M6 7.5h3v3H6v-3Z" />
|
||||
</svg>
|
||||
<span class="text-sm">Новости</span>
|
||||
</Link>
|
||||
<Link data-hs-overlay="#hs-basic-modal" :href="route('client.event.index')" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
|
||||
</svg>
|
||||
|
||||
<span class="text-sm">Мероприятия</span>
|
||||
</Link>
|
||||
<Link data-hs-overlay="#hs-basic-modal" :href="route('client.program.index')" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25" />
|
||||
</svg>
|
||||
|
||||
<span class="text-sm">Приемная компания</span>
|
||||
</Link>
|
||||
<Link data-hs-overlay="#hs-basic-modal" :href="route('client.program.index')" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.35 3.836c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75 2.25 2.25 0 0 0-.1-.664m-5.8 0A2.251 2.251 0 0 1 13.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m8.9-4.414c.376.023.75.05 1.124.08 1.131.094 1.976 1.057 1.976 2.192V16.5A2.25 2.25 0 0 1 18 18.75h-2.25m-7.5-10.5H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V18.75m-7.5-10.5h6.375c.621 0 1.125.504 1.125 1.125v9.375m-8.25-3 1.5 1.5 3-3.75" />
|
||||
</svg>
|
||||
|
||||
<span class="text-sm">Образовательные программы</span>
|
||||
</Link>
|
||||
<Link data-hs-overlay="#hs-basic-modal" :href="route('client.additionalEducation.index')" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 0 1-1.125-1.125v-3.75ZM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 0 1-1.125-1.125v-8.25ZM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 0 1-1.125-1.125v-2.25Z" />
|
||||
</svg>
|
||||
|
||||
<span class="text-sm">Курсы дополнительного образования</span>
|
||||
</Link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
export default {
|
||||
name: "PreSearchLinkList",
|
||||
components: {Link},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<PreSearchLinkList v-if="result === null" class="px-2 font-light" />
|
||||
<div v-else-if="result.length !== 0"
|
||||
:class="{'filter': loading === true}"
|
||||
class="px-2 font-light duration-300">
|
||||
<template v-for="(elements, tag) in result">
|
||||
<component
|
||||
v-for="(item, index) in elements"
|
||||
:key="index"
|
||||
:is="getComponent(tag)"
|
||||
:item="item"
|
||||
v-if="selectedCategory === null || selectedCategory === tag"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import PreSearchLinkList from "@/componentss/features/search/components/PreSearchLinkList.vue";
|
||||
import ResultPostItem from "@/componentss/features/search/components/Items/ResultPostItem.vue";
|
||||
import ResultPageItem from "@/componentss/features/search/components/Items/ResultPagetItem.vue";
|
||||
import ResultEducationalProgramItem
|
||||
from "@/componentss/features/search/components/Items/ResultEducationalProgramItem.vue";
|
||||
import ResultEventItem from "@/componentss/features/search/components/Items/ResultEventItem.vue";
|
||||
import ResultPersonItem from "@/componentss/features/search/components/Items/ResultPersonItem.vue";
|
||||
import ResultFacultyItem from "@/componentss/features/search/components/Items/ResultFacultyItem.vue";
|
||||
import ResultEducationalGroupItem from "@/componentss/features/search/components/Items/ResultEducationalGroupItem.vue";
|
||||
import ResultAdditionalEducationItem
|
||||
from "@/componentss/features/search/components/Items/ResultAdditionalEducationItem.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "ResultList",
|
||||
components: {
|
||||
ResultEducationalGroupItem,
|
||||
ResultAdditionalEducationItem,
|
||||
ResultEventItem,
|
||||
ResultEducationalProgramItem,
|
||||
ResultPageItem,
|
||||
ResultPostItem,
|
||||
PreSearchLinkList,
|
||||
ResultPersonItem,
|
||||
ResultFacultyItem,
|
||||
Link},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
Post: 'ResultPostItem',
|
||||
Page: 'ResultPageItem',
|
||||
Event: 'ResultEventItem',
|
||||
EducationalProgram: 'ResultEducationalProgramItem',
|
||||
AdditionalEducational: 'ResultAdditionalEducationItem',
|
||||
EducationalGroup: 'ResultEducationalGroupItem',
|
||||
User: 'ResultPersonItem',
|
||||
Faculty: 'ResultFacultyItem'
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
props: {
|
||||
result: {
|
||||
type: Object,
|
||||
},
|
||||
selectedCategory: {
|
||||
type: String,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.filter {
|
||||
filter: saturate(100%) blur(3px);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "PostSearchCategory"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div v-if="loading" class="relative z-0 min-h-[calc(100vh)] items-center">
|
||||
<div class="group block rounded-xl overflow-hidden animate-pulse min-h-[calc(100vh)] bg-gray-200"></div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<SliderBody :slides="slider.slides" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import SliderBody from "@/componentss/features/sliders/components/SliderBody.vue";
|
||||
|
||||
export default {
|
||||
name: 'Slider',
|
||||
components: {SliderBody},
|
||||
props: {
|
||||
sliderId: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
slider: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSlider(id) {
|
||||
axios.get(route('client.widget.slider.show', id))
|
||||
.then(response => {
|
||||
this.slider = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
});
|
||||
},
|
||||
|
||||
},
|
||||
mounted() {
|
||||
const id = this.sliderId
|
||||
this.getSlider(id)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div ref="sliderRef" class="relative z-0 min-h-[calc(70vh)] items-center">
|
||||
<!-- Слайды с изображениями -->
|
||||
<a
|
||||
:href="(!slide.settings.link_text) ? slide.link : '#'"
|
||||
v-for="(slide, index) in slides"
|
||||
:key="index"
|
||||
:style="{ filter: `brightness(${slide.image.shading})` }"
|
||||
class="absolute -z-10 h-full w-full before:absolute before:z-10 before:h-full before:w-full"
|
||||
>
|
||||
<img
|
||||
alt="Thumbnail"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
data-nimg="fill"
|
||||
class="object-cover rounded-md transition-opacity duration-1000 absolute inset-0 h-full w-full"
|
||||
sizes="100vw"
|
||||
:src="'/storage/' + slide.image.url"
|
||||
:class="{ 'opacity-1': currentIndex === index, 'opacity-0': currentIndex !== index }"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<!-- Текст слайдов с анимацией -->
|
||||
<transition name="fade" mode="out-in">
|
||||
<div :key="currentIndex">
|
||||
<div
|
||||
v-show="currentIndex === index"
|
||||
v-for="(item, index) in slides"
|
||||
:key="index"
|
||||
class="mx-auto max-w-screen-md px-5 pt-[150px] pb-0 bvi-no-styles"
|
||||
>
|
||||
<h1
|
||||
v-if="item.title"
|
||||
:class="`text-${item.settings.text_position}`"
|
||||
class="text-brand-primary mb-3 mt-2 text-3xl font-semibold tracking-tight text-white lg:text-5xl lg:leading-tight"
|
||||
>
|
||||
{{ item.title }}
|
||||
</h1>
|
||||
<div v-if="item.content" class="mt-8 space-x-3 text-gray-500 mb-8">
|
||||
<div class="gap-3 md:flex-row md:items-center">
|
||||
<div class="gap-3">
|
||||
<p :class="`text-${item.settings.text_position}`" class="text-gray-100 line-clamp-3">
|
||||
{{ item.content }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="`text-${item.settings.text_position}`">
|
||||
<a
|
||||
v-if="item.settings.link_text"
|
||||
:href="item.link"
|
||||
class="py-3 px-4 inline-flex items-center duration-300 gap-x-2 text-sm font-semibold rounded-lg border border-white text-white bg-transparent hover:bg-white hover:text-black hover:mix-blend-screen disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
{{ item.settings.link_text }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- Кнопки управления и прогресс-бар -->
|
||||
<div v-if="slides.length >= 2" class="mx-auto max-w-screen-md px-5">
|
||||
<div class="mt-8 text-gray-500 absolute bottom-[50px] left-1/2 transform -translate-x-1/2 lg:bottom-[100px]">
|
||||
<div class="">
|
||||
<div class="flex space-x-3 items-center justify-between">
|
||||
<button @click="prev" class="bg-gray-200 w-8 h-8 hover:bg-gray-300 text-gray-800 font-bold rounded-full">
|
||||
<svg class="w-4 h-4 mx-auto my-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="flex space-x-3 w-[200px] items-center font-semibold text-xl text-white">
|
||||
<span>{{ currentIndex + 1 }}</span>
|
||||
<div class="flex w-full h-1 bg-gray-200 rounded-full overflow-hidden" role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">
|
||||
<div class="flex flex-col justify-center rounded-full overflow-hidden bg-blue-600 text-xs text-white text-center whitespace-nowrap transition-all duration-500 ease-in-out" :style="{ 'width': `${progressBarStep}%` }"></div>
|
||||
</div>
|
||||
<span>{{ slides.length }}</span>
|
||||
</div>
|
||||
|
||||
<button @click="next" class="bg-gray-200 w-8 h-8 hover:bg-gray-300 text-gray-800 font-bold rounded-full">
|
||||
<svg class="w-4 h-4 mx-auto my-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19l7-7-7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions } from "vuex";
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
name: 'SliderBlockBody',
|
||||
props: {
|
||||
slides: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
intervalId: null,
|
||||
slideDuration: 5000,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalSlides() {
|
||||
return this.slides && this.slides ? this.slides.length : 0;
|
||||
},
|
||||
progressBarStep() {
|
||||
const slides = this.totalSlides;
|
||||
const step = 100 / slides;
|
||||
return (this.currentIndex + 1) * step;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['updateLastSlider']),
|
||||
next() {
|
||||
if (this.totalSlides > 0) {
|
||||
this.currentIndex = (this.currentIndex + 1) % this.totalSlides;
|
||||
this.resetTimer();
|
||||
}
|
||||
},
|
||||
prev() {
|
||||
if (this.totalSlides > 0) {
|
||||
this.currentIndex = (this.currentIndex - 1 + this.totalSlides) % this.totalSlides;
|
||||
this.resetTimer();
|
||||
}
|
||||
},
|
||||
startTimer() {
|
||||
this.stopTimer();
|
||||
this.intervalId = setInterval(this.next, this.slideDuration);
|
||||
},
|
||||
stopTimer() {
|
||||
clearInterval(this.intervalId);
|
||||
},
|
||||
resetTimer() {
|
||||
this.stopTimer();
|
||||
this.startTimer();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Передаем данные в Vuex после монтирования компонента
|
||||
this.updateLastSlider({
|
||||
url: this.$page.props.ziggy.location,
|
||||
top: this.$refs.sliderRef.getBoundingClientRect().top,
|
||||
bottom: this.$refs.sliderRef.getBoundingClientRect().bottom, // Получаем актуальное значение bottom
|
||||
});
|
||||
this.startTimer();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopTimer();
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
/* Стили для изображений слайдов */
|
||||
.opacity-1 {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.opacity-0 {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div ref="sliderRef" class="relative z-0 min-h-[calc(100vh)] items-center">
|
||||
<!-- Слайды с изображениями -->
|
||||
<a
|
||||
:href="(!slide.settings.link_text) ? slide.link : '#'"
|
||||
v-for="(slide, index) in slides"
|
||||
:key="index"
|
||||
:style="{ filter: `brightness(${slide.image.shading})` }"
|
||||
class="absolute -z-10 h-full w-full before:absolute before:z-10 before:h-full before:w-full"
|
||||
>
|
||||
<img
|
||||
alt="Thumbnail"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
data-nimg="fill"
|
||||
class="object-cover transition-opacity duration-1000 absolute inset-0 h-full w-full"
|
||||
sizes="100vw"
|
||||
:src="'/storage/' + slide.image.url"
|
||||
:class="{ 'opacity-1': currentIndex === index, 'opacity-0': currentIndex !== index }"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<!-- Текст слайдов с анимацией -->
|
||||
<transition name="fade" mode="out-in">
|
||||
<div :key="currentIndex">
|
||||
<div
|
||||
v-show="currentIndex === index"
|
||||
v-for="(item, index) in slides"
|
||||
:key="index"
|
||||
class="mx-auto max-w-screen-md px-5 pt-[150px] pb-0 bvi-no-styles"
|
||||
>
|
||||
<h1
|
||||
v-if="item.title"
|
||||
:class="`text-${item.settings.text_position}`"
|
||||
class="text-brand-primary mb-3 mt-2 text-3xl font-semibold tracking-tight text-white lg:text-5xl lg:leading-tight"
|
||||
>
|
||||
{{ item.title }}
|
||||
</h1>
|
||||
<div v-if="item.content" class="mt-8 space-x-3 text-gray-500 mb-8">
|
||||
<div class="gap-3 md:flex-row md:items-center">
|
||||
<div class="gap-3">
|
||||
<p :class="`text-${item.settings.text_position}`" class="text-gray-100 line-clamp-3">
|
||||
{{ item.content }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="`text-${item.settings.text_position}`">
|
||||
<a
|
||||
v-if="item.settings.link_text"
|
||||
:href="item.link"
|
||||
class="py-3 px-4 inline-flex items-center duration-300 gap-x-2 text-sm font-semibold rounded-lg border border-white text-white bg-transparent hover:bg-white hover:text-black hover:mix-blend-screen disabled:opacity-50 disabled:pointer-events-none"
|
||||
>
|
||||
{{ item.settings.link_text }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- Кнопки управления и прогресс-бар -->
|
||||
<div v-if="slides.length >= 2" class="mx-auto max-w-screen-md px-5">
|
||||
<div class="mt-8 text-gray-500 absolute bottom-[50px] left-1/2 transform -translate-x-1/2 lg:bottom-[100px]">
|
||||
<div class="">
|
||||
<div class="flex space-x-3 items-center justify-between">
|
||||
<button @click="prev" class="bg-gray-200 w-8 h-8 hover:bg-gray-300 text-gray-800 font-bold rounded-full">
|
||||
<svg class="w-4 h-4 mx-auto my-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="flex space-x-3 w-[200px] items-center font-semibold text-xl text-white">
|
||||
<span>{{ currentIndex + 1 }}</span>
|
||||
<div class="flex w-full h-1 bg-gray-200 rounded-full overflow-hidden" role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">
|
||||
<div class="flex flex-col justify-center rounded-full overflow-hidden bg-blue-600 text-xs text-white text-center whitespace-nowrap transition-all duration-500 ease-in-out" :style="{ 'width': `${progressBarStep}%` }"></div>
|
||||
</div>
|
||||
<span>{{ slides.length }}</span>
|
||||
</div>
|
||||
|
||||
<button @click="next" class="bg-gray-200 w-8 h-8 hover:bg-gray-300 text-gray-800 font-bold rounded-full">
|
||||
<svg class="w-4 h-4 mx-auto my-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19l7-7-7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions } from "vuex";
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
name: 'SliderBody',
|
||||
props: {
|
||||
slides: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
intervalId: null,
|
||||
slideDuration: 5000,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalSlides() {
|
||||
return this.slides && this.slides ? this.slides.length : 0;
|
||||
},
|
||||
progressBarStep() {
|
||||
const slides = this.totalSlides;
|
||||
const step = 100 / slides;
|
||||
return (this.currentIndex + 1) * step;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['updateLastSlider']),
|
||||
next() {
|
||||
if (this.totalSlides > 0) {
|
||||
this.currentIndex = (this.currentIndex + 1) % this.totalSlides;
|
||||
this.resetTimer();
|
||||
}
|
||||
},
|
||||
prev() {
|
||||
if (this.totalSlides > 0) {
|
||||
this.currentIndex = (this.currentIndex - 1 + this.totalSlides) % this.totalSlides;
|
||||
this.resetTimer();
|
||||
}
|
||||
},
|
||||
startTimer() {
|
||||
this.stopTimer();
|
||||
this.intervalId = setInterval(this.next, this.slideDuration);
|
||||
},
|
||||
stopTimer() {
|
||||
clearInterval(this.intervalId);
|
||||
},
|
||||
resetTimer() {
|
||||
this.stopTimer();
|
||||
this.startTimer();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Передаем данные в Vuex после монтирования компонента
|
||||
this.updateLastSlider({
|
||||
url: this.$page.props.ziggy.location,
|
||||
top: this.$refs.sliderRef.getBoundingClientRect().top,
|
||||
bottom: this.$refs.sliderRef.getBoundingClientRect().bottom, // Получаем актуальное значение bottom
|
||||
});
|
||||
this.startTimer();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopTimer();
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
/* Стили для изображений слайдов */
|
||||
.opacity-1 {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.opacity-0 {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div v-if="loading" class="relative z-0 min-h-[calc(100vh)] items-center">
|
||||
<div class="group block rounded-xl overflow-hidden animate-pulse min-h-[calc(100vh)] bg-gray-200"></div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<SliderBlockBody :slides="slider.slides" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import SliderBlockBody from "@/componentss/features/sliders/components/SliderBlockBody.vue";
|
||||
|
||||
export default {
|
||||
name: 'SliderBlock',
|
||||
components: {SliderBlockBody},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
slider: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalSlides() {
|
||||
return this.slidersCarousel && this.slidersCarousel.data ? this.slidersCarousel.data.length : 0;
|
||||
},
|
||||
progressBarStep() {
|
||||
const slides = this.totalSlides;
|
||||
const step = 100 / slides;
|
||||
return (this.currentIndex + 1) * step;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSlider(id) {
|
||||
axios.get(route('client.widget.slider.show', id))
|
||||
.then(response => {
|
||||
this.slider = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
});
|
||||
},
|
||||
|
||||
},
|
||||
mounted() {
|
||||
const id = this.block?.data.slider
|
||||
this.getSlider(id)
|
||||
},
|
||||
beforeUnmount() {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup>
|
||||
import { Head } from '@inertiajs/vue3'
|
||||
|
||||
defineProps({
|
||||
seo: Object,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head :title="seo?.title ? `${seo.title} - Нижнетагильский государственый-педагогический институт` : 'Нижнетагильский государственый-педагогический институт'">
|
||||
<meta name="description" :content="seo?.description ? `${seo.description} - Нижнетагильский государственый-педагогический институт` : 'Нижнетагильский государственый-педагогический институт'">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta property="og:title" :content="seo?.title ? `${seo.title} - Нижнетагильский государственый-педагогический институт` : 'Нижнетагильский государственый-педагогический институт'">
|
||||
<meta property="og:description" :content="seo?.description ? `${seo.description} - Нижнетагильский государственый-педагогический институт` : 'Нижнетагильский государственый-педагогический институт'">
|
||||
<meta property="og:image" content="/img/thumbnail-1.png">
|
||||
</Head>
|
||||
</template>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<component
|
||||
v-for="(filter, index) in filters"
|
||||
:key="index"
|
||||
:is="getComponent(filter.type)"
|
||||
:filter="filter"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import SearchBadge from "@/componentss/shared/badge/badges/SearchBadge.vue";
|
||||
import CategoryBadge from "@/componentss/shared/badge/badges/CategoryBadge.vue";
|
||||
import TagBadge from "@/componentss/shared/badge/badges/TagBadge.vue";
|
||||
import SortingByBadge from "@/componentss/shared/badge/badges/SortingByBadge.vue";
|
||||
import IsOnlineBadge from "@/componentss/shared/badge/badges/IsOnlineBadge.vue";
|
||||
export default {
|
||||
name: "BasicListBadge",
|
||||
components: {
|
||||
Link,
|
||||
SearchBadge,
|
||||
CategoryBadge,
|
||||
TagBadge,
|
||||
SortingByBadge,
|
||||
IsOnlineBadge
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
search: 'SearchBadge',
|
||||
category: 'CategoryBadge',
|
||||
tag: 'TagBadge',
|
||||
sort: 'SortingByBadge',
|
||||
is_online: 'IsOnlineBadge'
|
||||
};
|
||||
return componentMap[type] || null;
|
||||
},
|
||||
},
|
||||
props: {
|
||||
filters: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</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="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: "IsOnlineBadge",
|
||||
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,46 @@
|
||||
<template>
|
||||
<span v-if="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,46 @@
|
||||
<template>
|
||||
<span v-if="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: "SortingByBadge",
|
||||
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,176 @@
|
||||
<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-1000 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>
|
||||
|
||||
<transition name="fade">
|
||||
<SuccessNotification v-if="success" :text="message" />
|
||||
</transition>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SubmitBlock from "@/componentss/shared/builder/formBuilder/blocks/SubmitBlock.vue";
|
||||
import axios from "axios";
|
||||
import SuccessNotification from "@/Components/Notifications/SuccessNotification.vue";
|
||||
import {defineAsyncComponent} from "vue";
|
||||
|
||||
export default {
|
||||
name: "FormBuilder",
|
||||
components: {
|
||||
SuccessNotification,
|
||||
SubmitBlock,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formData: {},
|
||||
errors: null,
|
||||
success: false,
|
||||
message: null,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
getComponent(type) {
|
||||
const componentMap = {
|
||||
text: () => import('@/componentss/shared/builder/formBuilder/blocks/TextBlock.vue'),
|
||||
phone: () => import('@/componentss/shared/builder/formBuilder/blocks/PhoneBlock.vue'),
|
||||
email: () => import('@/componentss/shared/builder/formBuilder/blocks/EmailBlock.vue'),
|
||||
textarea: () => import('@/componentss/shared/builder/formBuilder/blocks/TextAreaBlock.vue'),
|
||||
multiple_choice: () => import('@/componentss/shared/builder/formBuilder/blocks/MultipleChoiceBlock.vue'),
|
||||
single_choice: () => import('@/componentss/shared/builder/formBuilder/blocks/SingleChoiceBlock.vue'),
|
||||
date: () => import('@/componentss/shared/builder/formBuilder/blocks/DateBlock.vue'),
|
||||
additional_education_choice: () => import('@/componentss/shared/builder/formBuilder/blocks/AdditionalEducationalChoiceBlock.vue'),
|
||||
educational_program_choice: () => import('@/componentss/shared/builder/formBuilder/blocks/EducationalChoiceBlock.vue'),
|
||||
captcha: () => import('@/componentss/shared/builder/formBuilder/blocks/CaptchaBlock.vue'),
|
||||
personal_data: () => import('@/componentss/shared/builder/formBuilder/blocks/PersonalDataBlock.vue'),
|
||||
|
||||
};
|
||||
return defineAsyncComponent(componentMap[type] || null);
|
||||
},
|
||||
|
||||
submitForm(event) {
|
||||
event.preventDefault();
|
||||
this.formData = this.getFormData(event.target.elements);
|
||||
this.sendDataToServer();
|
||||
},
|
||||
|
||||
getFormData(formElements) {
|
||||
const formData = {};
|
||||
for (let i = 0; i < formElements.length; i++) {
|
||||
const element = formElements[i];
|
||||
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.tagName === 'SELECT') {
|
||||
const fieldName = this.normalizeFieldName(element.name);
|
||||
|
||||
if (element.tagName === 'INPUT') {
|
||||
if (element.type === 'checkbox') {
|
||||
this.handleCheckbox(formData, fieldName, element);
|
||||
} else if (fieldName && fieldName !== 'choices') {
|
||||
formData[fieldName] = element.value;
|
||||
}
|
||||
} else if (element.tagName === 'TEXTAREA') {
|
||||
formData[fieldName] = element.value;
|
||||
} else if (element.tagName === 'SELECT') {
|
||||
formData[fieldName] = element.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return formData;
|
||||
},
|
||||
normalizeFieldName(name) {
|
||||
return name.endsWith('[]') ? name.slice(0, -2) : name;
|
||||
},
|
||||
|
||||
handleCheckbox(formData, fieldName, element) {
|
||||
if (!formData[fieldName]) {
|
||||
formData[fieldName] = [];
|
||||
}
|
||||
if (element.checked) {
|
||||
formData[fieldName].push(element.value);
|
||||
}
|
||||
},
|
||||
|
||||
sendDataToServer() {
|
||||
axios.post(route('client.widget.form.submit', this.blocks.data.id), this.formData)
|
||||
.then(this.handleResponse)
|
||||
.catch(this.handleError);
|
||||
},
|
||||
|
||||
handleResponse(response) {
|
||||
if (response.data.status === 'ok') {
|
||||
this.success = true;
|
||||
this.message = response.data.message;
|
||||
this.errors = null; // Сбрасываем ошибки при успешной отправке
|
||||
}
|
||||
},
|
||||
|
||||
handleError(error) {
|
||||
this.errors = error.response.data || ['Неизвестная ошибка']; // Обработка ошибок
|
||||
this.success = false; // Сбрасываем успешное состояние
|
||||
}
|
||||
},
|
||||
props: {
|
||||
blocks: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
</style>
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<ul v-if="loading" class="mt-5 space-y-3 flex flex-col animate-pulse">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-8 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
|
||||
<div v-else 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">
|
||||
<select :name="block.data.name_field" :disabled="isActiveProgramPage" v-model="activeProgramPage" class="py-3 px-4 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">
|
||||
<option selected="">Open this select menu</option>
|
||||
<option :value="additionalProgram.title" v-for="additionalProgram in additionalEducationalPrograms.data">{{ additionalProgram.title }}</option>
|
||||
</select>
|
||||
<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>
|
||||
<div class="flex items-center mt-2 justify-between flex-wrap">
|
||||
<p v-if="!error" class="text-sm text-gray-500" id="hs-input-helper-text">
|
||||
{{ block.data.description }}
|
||||
</p>
|
||||
<!-- <p class="text-sm text-primaryBlue">{{ text.length }} / {{ block.data.rules.max }}</p>-->
|
||||
</div>
|
||||
<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>
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
name: "AdditionalEducationalChoiceBlock",
|
||||
data() {
|
||||
return {
|
||||
additionalEducationalPrograms: null,
|
||||
loading: true, // Состояние загрузки
|
||||
activeProgramPage: null,
|
||||
isActiveProgramPage: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPrograms() {
|
||||
return axios.get(route('client.widget.additional.program.index'))
|
||||
.then(response => {
|
||||
this.additionalEducationalPrograms = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
},
|
||||
isAdditionalEducationalRoute() {
|
||||
const slug = this.getSlugFromUrl(this.$page.props.ziggy.location);
|
||||
return this.$page.props.ziggy.location === route('client.additionalEducation.show', slug);
|
||||
},
|
||||
getSlugFromUrl(url) {
|
||||
const segments = url.split('/');
|
||||
return segments[segments.length - 1];
|
||||
},
|
||||
findItemBySlug() {
|
||||
const slug = this.getSlugFromUrl(this.$page.props.ziggy.location)
|
||||
return this.additionalEducationalPrograms.data.find(item => item.slug === slug) || null;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPrograms().then(() => {
|
||||
if (this.isAdditionalEducationalRoute()) {
|
||||
this.isActiveProgramPage = true;
|
||||
this.activeProgramPage = this.findItemBySlug().title;
|
||||
}
|
||||
});
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="mb-4 sm:mb-8">
|
||||
<div class="relative">
|
||||
<!-- Компонент капчи -->
|
||||
<YSmartCaptcha v-model="token" />
|
||||
<!-- Скрытое поле ввода -->
|
||||
<input
|
||||
v-model="inputValue"
|
||||
:required="block.data.rules.required"
|
||||
:name="block.data.name_field"
|
||||
type="text"
|
||||
class="hidden"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CaptchaBlock",
|
||||
data() {
|
||||
return {
|
||||
token: null, // Токен капчи
|
||||
inputValue: "", // Значение, которое будет отправлено в input
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// Отслеживаем изменения токена
|
||||
token(newToken) {
|
||||
if (newToken) {
|
||||
// Если токен получен, считаем капчу успешно пройденной
|
||||
this.inputValue = "Капча успешно пройдена";
|
||||
} else {
|
||||
// Если токен сброшен, очищаем поле
|
||||
this.inputValue = "";
|
||||
}
|
||||
},
|
||||
},
|
||||
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"
|
||||
type="date"
|
||||
: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: "DateBlock",
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<ul v-if="loading" class="mt-5 space-y-3 flex flex-col animate-pulse">
|
||||
<li class="w-full h-4 bg-gray-200 rounded-full"></li>
|
||||
<li class="w-full h-8 bg-gray-200 rounded-full"></li>
|
||||
</ul>
|
||||
|
||||
<div v-else 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">
|
||||
<select :name="block.data.name_field" :disabled="isActiveProgramPage" v-model="activeProgramPage" class="py-3 px-4 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">
|
||||
<option selected="">Open this select menu</option>
|
||||
<option :value="additionalProgram.name" v-for="additionalProgram in additionalEducationalPrograms.data">{{ additionalProgram.name }}</option>
|
||||
</select>
|
||||
<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>
|
||||
<div class="flex items-center mt-2 justify-between flex-wrap">
|
||||
<p v-if="!error" class="text-sm text-gray-500" id="hs-input-helper-text">
|
||||
{{ block.data.description }}
|
||||
</p>
|
||||
<!-- <p class="text-sm text-primaryBlue">{{ text.length }} / {{ block.data.rules.max }}</p>-->
|
||||
</div>
|
||||
<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>
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
name: "EducationalChoiceBlock",
|
||||
data() {
|
||||
return {
|
||||
additionalEducationalPrograms: null,
|
||||
loading: true, // Состояние загрузки
|
||||
activeProgramPage: null,
|
||||
isActiveProgramPage: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPrograms() {
|
||||
return axios.get(route('client.widget.educational.program.index'))
|
||||
.then(response => {
|
||||
this.additionalEducationalPrograms = response.data;
|
||||
this.loading = false; // Установить состояние загрузки в false
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка:', error);
|
||||
this.loading = false; // Установить состояние загрузки в false даже при ошибке
|
||||
});
|
||||
},
|
||||
isAdditionalEducationalRoute() {
|
||||
const slug = this.getSlugFromUrl(this.$page.props.ziggy.location);
|
||||
return this.$page.props.ziggy.location === route('client.program.show', slug);
|
||||
},
|
||||
getSlugFromUrl(url) {
|
||||
const segments = url.split('/');
|
||||
return segments[segments.length - 1];
|
||||
},
|
||||
findItemBySlug() {
|
||||
const slug = this.getSlugFromUrl(this.$page.props.ziggy.location)
|
||||
return this.additionalEducationalPrograms.data.find(item => item.slug === slug) || null;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPrograms().then(() => {
|
||||
if (this.isAdditionalEducationalRoute()) {
|
||||
this.isActiveProgramPage = true;
|
||||
this.activeProgramPage = this.findItemBySlug().name;
|
||||
}
|
||||
});
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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,45 @@
|
||||
<template>
|
||||
<div class="mb-4 sm:mb-8">
|
||||
<label class="block mb-3 text-sm font-medium">{{ block.data.title_field }}</label>
|
||||
<div class="">
|
||||
<input
|
||||
type="checkbox"
|
||||
required="required"
|
||||
class="shrink-0 mb-0.5 border-gray-200 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
|
||||
:id="block.data.name_field + '-id'">
|
||||
<input
|
||||
:name="block.data.name_field"
|
||||
value="Даю согласие на обработку персональных данных"
|
||||
type="text"
|
||||
class="hidden"
|
||||
/>
|
||||
<label :for="block.data.name_field + '-id'" class="text-sm text-gray-500 ms-2">Даю свое согласие на сбор, обработку, хранение и использование персональных данных в соответствии с <a class="underline text-primaryBlue" target="_blank" href="https://ntspi.ru/upload/824_%D0%9E%D0%B1_%D1%83%D1%82%D0%B2_%D0%9F%D0%BE%D0%BB%D0%B8%D1%82_%D0%BE%D0%B1%D1%80_%D0%B7%D0%B0%D1%89%D0%B8%D1%82_%D0%BF%D0%B5%D1%80%D1%81_%D0%B4%D0%B0%D0%BD.pdf">Пользовательским соглашением</a> </label>
|
||||
</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: "PersonalDataBlock",
|
||||
data() {
|
||||
return {
|
||||
text: "",
|
||||
}
|
||||
},
|
||||
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,62 @@
|
||||
<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">
|
||||
<textarea
|
||||
v-model="textarea"
|
||||
:required="block.data.rules.required"
|
||||
:name="block.data.name_field"
|
||||
:minlength="block.data.rules.min"
|
||||
:maxlength="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>
|
||||
<div class="flex items-center mt-2 justify-between flex-wrap">
|
||||
<p v-if="!error" class="text-sm text-gray-500" id="hs-input-helper-text">
|
||||
{{ block.data.description }}
|
||||
</p>
|
||||
<p class="text-sm text-primaryBlue">{{ textarea.length }} / {{ block.data.rules.max }}</p>
|
||||
</div>
|
||||
|
||||
<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 {
|
||||
textarea: "",
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<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
|
||||
v-model="text"
|
||||
:required="block.data.rules.required"
|
||||
:name="block.data.name_field"
|
||||
:minlength="block.data.rules.min"
|
||||
:maxlength="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>
|
||||
<div class="flex items-center mt-2 justify-between flex-wrap">
|
||||
<p v-if="!error" class="text-sm text-gray-500" id="hs-input-helper-text">
|
||||
{{ block.data.description }}
|
||||
</p>
|
||||
<p v-if="block.data.rules.show_length" class="text-sm text-primaryBlue">{{ text.length }} / {{ block.data.rules.max }}</p>
|
||||
</div>
|
||||
<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 {
|
||||
text: "",
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div>
|
||||
<transition name="fade" mode="out-in">
|
||||
<PageSkeleton v-if="loading" key="skeleton" />
|
||||
<div class="space-y-6" v-else key="content">
|
||||
<component
|
||||
v-for="(block, index) in blocks"
|
||||
:key="index"
|
||||
:is="getComponent(block.type)"
|
||||
:block="block"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import PageSkeleton from "@/componentss/shared/builder/pageBuilder/skeletons/PageSkeleton.vue";
|
||||
|
||||
export default {
|
||||
name: "Builder",
|
||||
components: {PageSkeleton},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
componentMap: {
|
||||
heading: () => import('@/componentss/shared/builder/pageBuilder/blocks/HeadingBlock.vue'),
|
||||
paragraph: () => import('@/componentss/shared/builder/pageBuilder/blocks/ParagraphBlock.vue'),
|
||||
images: () => import('@/componentss/shared/builder/pageBuilder/blocks/BasicImageSlider.vue'),
|
||||
image: () => import('@/componentss/shared/builder/pageBuilder/blocks/ImageBlock.vue'),
|
||||
files: () => import('@/componentss/shared/builder/pageBuilder/blocks/FileBlock.vue'),
|
||||
person: () => import('@/componentss/shared/builder/pageBuilder/blocks/PersonBlock.vue'),
|
||||
stepper: () => import('@/componentss/shared/builder/pageBuilder/blocks/StepperBlock.vue'),
|
||||
video: () => import('@/componentss/shared/builder/pageBuilder/blocks/VideoBlock.vue'),
|
||||
tabs: () => import('@/componentss/shared/builder/pageBuilder/blocks/TabBlock.vue'),
|
||||
postsList: () => import('@/componentss/shared/builder/pageBuilder/blocks/PostListBlock.vue'),
|
||||
postItem: () => import('@/componentss/shared/builder/pageBuilder/blocks/PostItemBlock.vue'),
|
||||
pageItem: () => import('@/componentss/shared/builder/pageBuilder/blocks/PageItemBlock.vue'),
|
||||
customForm: () => import('@/componentss/shared/builder/pageBuilder/blocks/FormBlock.vue'),
|
||||
pageResourceList: () => import('@/componentss/shared/builder/pageBuilder/blocks/PageResourceList.vue'),
|
||||
contact: () => import('@/componentss/shared/builder/pageBuilder/blocks/contacts/ContactSectionBlock.vue'),
|
||||
slider: () => import('@/componentss/features/sliders/shared/SliderBlock.vue')
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async loadAllComponents() {
|
||||
// Создайте массив промисов для загрузки всех компонентов
|
||||
const promises = Object.values(this.componentMap).map(load => load());
|
||||
|
||||
// Дождитесь завершения всех загрузок
|
||||
await Promise.all(promises);
|
||||
this.loading = false; // Установите флаг загрузки в false
|
||||
},
|
||||
getComponent(type) {
|
||||
return defineAsyncComponent(this.componentMap[type] || null);
|
||||
},
|
||||
},
|
||||
props: {
|
||||
blocks: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
await this.loadAllComponents(); // Загрузить все компоненты при создании
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Анимация появления */
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="relative">
|
||||
<div class="flex">
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
class="w-full h-[500px] overflow-hidden relative"
|
||||
:class="{ 'block': currentIndex === index, 'hidden': currentIndex !== index }"
|
||||
>
|
||||
<div class="absolute inset-0 bg-cover bg-center blur-lg" :style="{ backgroundImage: 'url(/storage/' + item + ')' }"></div>
|
||||
<img
|
||||
@click="openLightboxOnSlide(index + 1)"
|
||||
:src="'/storage/' + item"
|
||||
class="absolute inset-0 w-full h-full object-cover rounded-md hover:opacity-95 hover:duration-200 transition"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="absolute top-1/2 left-4 transform -translate-y-1/2 bg-white rounded-full p-2 shadow-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||
@click="prevSlide"
|
||||
>
|
||||
❮
|
||||
</button>
|
||||
<button
|
||||
class="absolute top-1/2 right-4 transform -translate-y-1/2 bg-white rounded-full p-2 shadow-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||
@click="nextSlide"
|
||||
>
|
||||
❯
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<figcaption class="mt-3 text-sm text-center text-gray-500 dark:text-neutral-500">
|
||||
{{ block.data.alt }}
|
||||
</figcaption>
|
||||
|
||||
<FsLightbox class="" :slide="slide" :toggler="toggler" :sources="items.map(item => domainPath + '/storage/' + item)"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FsLightbox from "fslightbox-vue";
|
||||
|
||||
export default {
|
||||
name: "ClientImageSlider",
|
||||
components: { FsLightbox },
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
items: this.block.data.url,
|
||||
toggler: false,
|
||||
domainPath: null,
|
||||
slide: null,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
prevSlide() {
|
||||
if (this.currentIndex === 0) {
|
||||
this.currentIndex = this.items.length - 1;
|
||||
} else {
|
||||
this.currentIndex--;
|
||||
}
|
||||
},
|
||||
nextSlide() {
|
||||
if (this.currentIndex === this.items.length - 1) {
|
||||
this.currentIndex = 0;
|
||||
} else {
|
||||
this.currentIndex++;
|
||||
}
|
||||
},
|
||||
openLightboxOnSlide: function (number) {
|
||||
this.slide = number;
|
||||
this.toggler = !this.toggler;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.domainPath = window.location.origin;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.blur-lg {
|
||||
filter: blur(20px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<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">
|
||||
<BasicIcon :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 BasicIcon from "@/componentss/ui/icons/BasicIcon.vue";
|
||||
|
||||
|
||||
export default {
|
||||
name: "FileBlock",
|
||||
components: {BasicIcon },
|
||||
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>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div v-if="loading" class="flex flex-col space-y-4">
|
||||
<div class="flex-col animate-pulse mt-10">
|
||||
<div class="w-[25rem] mx-auto h-8 bg-gray-200 rounded-full"></div>
|
||||
<div class="mt-5 mx-auto w-[40rem] h-60 relative z-1000 border rounded-xl sm:mt-10 md:p-10 bg-gray-200">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<FormBuilder :blocks="form" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import axios from "axios";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
export default {
|
||||
name: "FormBlock",
|
||||
components: {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() {
|
||||
const id = this.block?.data.form || this.formId
|
||||
this.getForm(id);
|
||||
|
||||
},
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
},
|
||||
formId: {
|
||||
type: String,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
}
|
||||
</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,53 @@
|
||||
<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 v-if="block.data.alt" class="mt-3 text-sm text-center text-gray-500 dark:text-neutral-500">
|
||||
{{ block.data.alt }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FsLightbox class="" :toggler="toggler" :sources="[domainPath + '/storage/' + block.data.url]"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import slugify from "slugify";
|
||||
import FsLightbox from "fslightbox-vue";
|
||||
|
||||
|
||||
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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user