This commit is contained in:
f4ilji
2024-11-09 00:52:35 +05:00
parent 693a81d724
commit 49bb821361
17 changed files with 223 additions and 47 deletions
@@ -122,6 +122,7 @@ class ClientPostController extends Controller
$breadcrumbs = null;
}
return Inertia::render('Client/Posts/Index', compact('filters', 'posts', 'categories', 'tags', 'breadcrumbs'));
}
@@ -120,7 +120,6 @@ class ClientProgramController extends Controller
return [$formEducational->value => $formEducational->getLabel()];
});
dd($program);
$seo = $program->seo;
return Inertia::render('Client/Programs/Show', compact('program', 'formsEdu', 'seo'));
}
+21 -25
View File
@@ -6,10 +6,12 @@ use App\Http\Resources\AdditionalEducationSearchResource;
use App\Http\Resources\EducationalProgramSearchResource;
use App\Http\Resources\EducationGroupSearchResource;
use App\Http\Resources\EventSearchResource;
use App\Http\Resources\FacultySearchResource;
use App\Http\Resources\PageResource;
use App\Http\Resources\PageSearchResource;
use App\Http\Resources\PostResource;
use App\Http\Resources\PostSearchResource;
use App\Http\Resources\UserSearchResource;
use App\Models\AdditionalEducation;
use App\Models\EducationalGroup;
use App\Models\EducationalProgram;
@@ -28,6 +30,16 @@ use ProtoneMedia\LaravelCrossEloquentSearch\Search;
class SearchController extends Controller
{
private array $resourceMap = [
Post::class => PostSearchResource::class,
Page::class => PageSearchResource::class,
EducationalGroup::class => EducationGroupSearchResource::class,
EducationalProgram::class => EducationalProgramSearchResource::class,
Event::class => EventSearchResource::class,
AdditionalEducation::class => AdditionalEducationSearchResource::class,
User::class => UserSearchResource::class,
Faculty::class => FacultySearchResource::class,
];
public function index(Request $request)
{
$req = Str::lower($request->query('search'));
@@ -42,37 +54,21 @@ class SearchController extends Controller
->add(Post::where('status', '=', 'published'), ['title', 'search_data'])
->add(Page::with('section')->where('searchable', '=', true), ['title', 'search_data'])
->add(Event::where('event_date_start', '>', Date::now()), 'title')
// ->add(AdditionalEducation::where('is_active', '=', true), 'title')
// ->add(EducationalGroup::with('schedules'), 'title')
// ->add(EducationalProgram::where('status', '=', true)->whereHas('admission_plans'), 'name')
// ->add(Faculty::where('is_active', '=', true), 'title')
// ->add(User::whereHas('userDetail'), ['name', 'userDetail.education', 'userDetail.awards', 'userDetail.publications'])
->add(AdditionalEducation::where('is_active', '=', true), 'title')
->add(EducationalGroup::with('schedules'), 'title')
->add(EducationalProgram::where('status', '=', true)->whereHas('admission_plans'), 'name')
->add(Faculty::where('is_active', '=', true), 'title')
->add(User::whereHas('userDetail'), 'name')
->beginWithWildcard()
->orderByRelevance()
->includeModelType()
->ignoreCase(true)
->search($req);
// Преобразуем результаты в коллекцию и мапируем их
$resources = collect($results)->map(function ($result) {
if ($result instanceof Post) {
return new PostSearchResource($result);
}
if ($result instanceof Page) {
return new PageSearchResource($result);
}
if ($result instanceof EducationalGroup) {
return new EducationGroupSearchResource($result);
}
if ($result instanceof EducationalProgram) {
return new EducationalProgramSearchResource($result);
}
if ($result instanceof Event) {
return new EventSearchResource($result);
}
if ($result instanceof AdditionalEducation) {
return new AdditionalEducationSearchResource($result);
}
$resourceMap = $this->resourceMap;
$resources = collect($results)->map(function ($result) use ($resourceMap) {
$resourceClass = $resourceMap[get_class($result)] ?? null;
return $resourceClass ? new $resourceClass($result) : null;
});
$result_type = $this->getCategoriesSearchResult($resources);
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserSearchResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}
@@ -24,7 +24,6 @@ export default {
methods: {
clearFilter() {
let url = new URL(window.location.href);
// Создаем массив для хранения всех ключей, которые нужно удалить
const keysToDelete = [];
@@ -34,7 +33,6 @@ export default {
keysToDelete.push(key);
}
}
// Удаляем все ключи из массива
keysToDelete.forEach(key => url.searchParams.delete(key));
@@ -1,4 +1,5 @@
<template>
<component
v-for="(filter, index) in filters"
:key="index"
@@ -47,8 +47,18 @@ export default {
methods: {
filter: debounce(function () {
let url = new URL(window.location.href);
// Создаем массив для хранения всех ключей, которые нужно удалить
const keysToDelete = [];
// Перебираем все параметры и добавляем ключи, начинающиеся с 'category', в массив
for (const [key] of url.searchParams) {
if (key.startsWith('category')) {
keysToDelete.push(key);
}
}
// Удаляем все ключи из массива
keysToDelete.forEach(key => url.searchParams.delete(key));
url.searchParams.delete('page');
url.searchParams.delete('category[]');
let newUrl = url.toString();
this.$inertia.visit(newUrl, {
method: 'get',
@@ -60,7 +70,17 @@ export default {
}, 500),
clearFilter() {
let url = new URL(window.location.href);
url.searchParams.delete('category[]');
// Создаем массив для хранения всех ключей, которые нужно удалить
const keysToDelete = [];
// Перебираем все параметры и добавляем ключи, начинающиеся с 'category', в массив
for (const [key] of url.searchParams) {
if (key.startsWith('category')) {
keysToDelete.push(key);
}
}
// Удаляем все ключи из массива
keysToDelete.forEach(key => url.searchParams.delete(key));
this.category_slug = [];
this.searchTerm = ''; // Сбросить поле поиска
let newUrl = url.toString();
@@ -46,21 +46,41 @@ export default {
methods: {
filter: debounce(function () {
let url = new URL(window.location.href);
// Создаем массив для хранения всех ключей, которые нужно удалить
const keysToDelete = [];
// Перебираем все параметры и добавляем ключи, начинающиеся с 'category', в массив
for (const [key] of url.searchParams) {
if (key.startsWith('tag')) {
keysToDelete.push(key);
}
}
// Удаляем все ключи из массива
keysToDelete.forEach(key => url.searchParams.delete(key));
url.searchParams.delete('page');
url.searchParams.delete('tag[]');
let newUrl = url.toString();
this.$inertia.visit(newUrl, {
method: 'get',
preserveState: true,
data: {
tag: this.tag_slug,
category: this.category_slug,
},
});
}, 500),
clearFilter() {
let url = new URL(window.location.href);
url.searchParams.delete('tag[]');
this.tag_slug = [];
// Создаем массив для хранения всех ключей, которые нужно удалить
const keysToDelete = [];
// Перебираем все параметры и добавляем ключи, начинающиеся с 'category', в массив
for (const [key] of url.searchParams) {
if (key.startsWith('tag')) {
keysToDelete.push(key);
}
}
// Удаляем все ключи из массива
keysToDelete.forEach(key => url.searchParams.delete(key));
this.category_slug = [];
this.searchTerm = ''; // Сбросить поле поиска
let newUrl = url.toString();
this.$inertia.visit(newUrl, {
@@ -17,7 +17,9 @@
'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>
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">
@@ -56,10 +58,10 @@
<script>
import {debounce} from "lodash";
import {Link} from "@inertiajs/vue3";
import {Inertia} from "@inertiajs/inertia";
import ResultList from "@/Components/Search/ResultList.vue";
import CategoryList from "@/Components/Search/CategorySearchList.vue";
import CategorySearchList from "@/Components/Search/CategorySearchList.vue";
import {ruCategories} from "./other/ruCategories.js";
export default {
name: "ClientGlobalSearch",
data() {
@@ -69,7 +71,6 @@ export default {
paginate: null,
selectedCategory: null,
categories: null,
ruCategories: { Post: "Новости", Page: "Страницы", Event: "Мероприятия" },
loading: false,
}
},
@@ -80,6 +81,9 @@ export default {
Link,
},
methods: {
ruCategories() {
return ruCategories
},
categorySort: debounce(function(category) {
this.toggleLoadingState(true);
axios({
@@ -90,7 +94,6 @@ export default {
category: category
}
}).then((response) => {
console.log(response.data.searchRes);
this.paginate = response.data.paginate;
this.result = response.data.searchRes;
this.categories = response.data.result_type;
@@ -109,7 +112,6 @@ export default {
search: this.searchInput.toLowerCase(),
}
}).then((response) => {
console.log(response.data.searchRes)
this.paginate = response.data.paginate
this.result = response.data.searchRes;
this.categories = response.data.result_type
@@ -11,7 +11,7 @@
<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="rou"
<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">
@@ -1,5 +1,5 @@
<template>
<Link v-if="item.tag === 'EducationalProgram'" data-hs-overlay="#hs-basic-modal" :href="route('client.program.show', item.data.id)" class="my-1 flex gap-3 px-2 py-2 items-center hover:bg-[#F2F2F2] rounded-lg">
<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>
@@ -0,0 +1,47 @@
<template>
<a v-if="item.tag === 'Faculty'" 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="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";
import PreSearchLinkList from "@/Components/Search/PreSearchLinkList.vue";
export default {
name: "ResultFacultyItem",
components: {PreSearchLinkList, Link},
data() {
return {
selectedCategory: 'All',
}
},
methods: {
},
props: {
item: {
type: Object,
}
},
}
</script>
<style scoped>
</style>
@@ -0,0 +1,47 @@
<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";
import PreSearchLinkList from "@/Components/Search/PreSearchLinkList.vue";
export default {
name: "ResultPersonItem",
components: {PreSearchLinkList, Link},
data() {
return {
selectedCategory: 'All',
}
},
methods: {
},
props: {
item: {
type: Object,
}
},
}
</script>
<style scoped>
</style>
+12 -1
View File
@@ -24,13 +24,22 @@ import ResultEducationalProgramItem from "@/Components/Search/Items/ResultEducat
import ResultEventItem from "@/Components/Search/Items/ResultEventItem.vue";
import ResultAdditionalEducationItem from "@/Components/Search/Items/ResultAdditionalEducationalItem.vue";
import ResultEducationalGroupItem from "@/Components/Search/Items/ResultEducationalGroupItem.vue";
import ResultPersonItem from "@/Components/Search/Items/ResultPersonItem.vue";
import ResultFacultyItem from "@/Components/Search/Items/ResultFacultyItem.vue";
export default {
name: "ResultList",
components: {
ResultEducationalGroupItem,
ResultAdditionalEducationItem,
ResultEventItem, ResultEducationalProgramItem, ResultPageItem, ResultPostItem, PreSearchLinkList, Link},
ResultEventItem,
ResultEducationalProgramItem,
ResultPageItem,
ResultPostItem,
PreSearchLinkList,
ResultPersonItem,
ResultFacultyItem,
Link},
data() {
return {
}
@@ -44,6 +53,8 @@ export default {
EducationalProgram: 'ResultEducationalProgramItem',
AdditionalEducational: 'ResultAdditionalEducationItem',
EducationalGroup: 'ResultEducationalGroupItem',
User: 'ResultPersonItem',
Faculty: 'ResultFacultyItem'
};
return componentMap[type] || null;
},
@@ -0,0 +1,13 @@
// src/locales/translations.js
export const ruCategories = {
Post: "Новости",
Page: "Страницы",
Event: "Мероприятия",
Faculty: "Факультеты",
User: "Сотрудники",
Division: "Подразделения",
AdditionalEducation: "Доп. образование",
EducationalProgram: "Образовательная программа",
EducationalGroup: "Расписание"
};
+1 -1
View File
@@ -74,7 +74,7 @@ export default {
<div class="flex flex-col h-screen">
<main class="flex-grow">
<div class="relative mx-auto mt-[67px] max-w-screen-xl px-4 py-10 md:flex md:flex-row md:py-10">
<div class="px-4 pt-6 lg:pt-10 pb-12 sm:px-6 lg:px-8 mx-auto">
<div class="pt-6 lg:pt-10 pb-12 sm:px-6 lg:px-8 mx-auto">
<div>
<!-- <AdminIndexHeader>-->
<!-- <ClientPostSearch />-->
+6 -4
View File
@@ -11,10 +11,10 @@ import AdminIndexHeaderTitle from "@/Components/AdminIndexHeaderTitle.vue";
import ClientPostFilter from '@/Components/ClientPostFilter.vue';
import ClientPost from '@/Components/ClientPost.vue';
import ClientPostSearch from '@/Components/ClientPostSearch.vue';
import PostBadge from "@/Components/BuilderUi/Events/EventBadgeBuilder.vue";
import MainPageNavBar from "@/Navbars/MainPageNavbar.vue";
import NewsBreadcrumbs from "@/Components/BuilderUi/Posts/NewsBreadcrumbs.vue";
import AppHead from "@/Components/AppHead.vue";
import PostBadge from "@/Components/BuilderUi/Posts/PostBadge.vue";
export default {
name: "Index",
@@ -92,10 +92,12 @@ export default {
</AdminIndexHeader>
<div class="px-6">
<h3 v-if="filters.category_filter.value || filters.tag_filter.value || filters.search_filter.value" class="text-sm text-gray-500 mb-4">Найдено новостей: {{ posts.meta.total }}</h3>
<div v-if="filters.category_filter.value || filters.tag_filter.value || filters.search_filter.value" class="px-6">
<h3 class="text-sm text-gray-500 mb-4">Найдено новостей: {{ posts.meta.total }}</h3>
<div class="flex-wrap flex gap-3 md:items-center">
<PostBadge v-if="filters.length > 0" :filters="filters" />
<PostBadge :filters="filters" />
</div>
</div>