Последняя версия с сервера прошлого разработчика

This commit is contained in:
2025-07-10 04:35:51 +00:00
commit c731570032
1174 changed files with 134314 additions and 0 deletions

View File

@@ -0,0 +1,178 @@
<template>
<meta-head title="Написать нам">
<meta name="description" content="Написать нам">
</meta-head>
<!-- <header-auth /> -->
<!-- min-h-[calc(100vh-64px)] -->
<div class="min-h-screen place-items-center grid md:pt-3 bg-center bg-cover"
style="background-image: url('/image/auth-bg.jpg');"
>
<div class="w-full max-w-3xl mx-auto ">
<div class="md:grid grid-cols-12 ">
<div class="col-span-12 bg-indigo-200">
<div class="py-12 lg:py-20 px-10">
<inertia-link
:href="route('dashboard')"
class="inline-flex gap-1 items-center text-gray-light"
>
<svg xmlns="http://www.w3.org/2000/svg" width="18"
height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="flex-shrink-0"
><polyline points="9 10 4 15 9 20"></polyline><path d="M20 4v7a4 4 0 0 1-4 4H4"></path></svg> Вернуться
</inertia-link>
<h1 class="mt-5 font-medium text-xl md:text-2xl lg:text-4xl text-white text-center">
Написать нам
</h1>
<div v-if="$page.props.flash.status">
<p class="text-xl text-orange text-center mt-3">
{{ $page.props.flash.status }}
</p>
</div>
<div class="auth-form mt-7">
<form class="space-y-6" @submit.prevent="submit">
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<path
d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
>
</path>
<polyline points="22,6 12,13 2,6"></polyline>
</svg>
</div>
<input
v-model="form.email"
autocapitalize="off"
required
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="email" placeholder="E-mail"
>
</div>
<div v-if="form.errors.email" class="form-error text-sm text-red">
{{ form.errors.email }}
</div>
</div>
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="feather feather-edit-2"
><path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"></path></svg>
</div>
<input
v-model="form.title"
autocapitalize="off"
required
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="text" placeholder="Тема"
>
</div>
<div v-if="form.errors.title" class="form-error text-sm text-red">
{{ form.errors.title }}
</div>
</div>
<div>
<div class="relative">
<textarea
v-model="form.body"
autocapitalize="off"
required
rows="5"
class="w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
placeholder="Сообщение"
/>
</div>
<div v-if="form.errors.body" class="form-error text-sm text-red">
{{ form.errors.body }}
</div>
</div>
<div class="text-center">
<loading-button :loading="form.processing" class="mx-3 my-1 px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white bg-orange focus:outline-none"
type="submit"
>
Отправить
</loading-button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- <div class="bg-indigo-300 mt-auto">
<div class="container mx-auto p-2 text-sm text-gray-light">
<div class="-mx-2 -my-2 flex flex-wrap justify-between">
<div class="mx-2 my-2">
<a href="/company/about">О компании</a>
</div>
<div class="mx-2 my-2">
<a href="/company/offer">Оферта</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-payment">Условия оплаты</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-service">Условия предоставления услуг</a>
</div>
<div class="mx-2 my-2">
<a href="/company/cookies">Cookies</a>
</div>
<div class="mx-2 my-2">
<a href="/company/privacy-policy">Privacy Policy</a>
</div>
</div>
</div>
</div> -->
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
LoadingButton,
},
setup() {
const form = Inertia.form({
title: null,
body: null,
email: null,
})
const submit = () => {
form.post(route('common.write-to-us'), {
onSuccess: () => form.reset(),
})
}
return {
form,
submit,
}
},
}
</script>

View File

@@ -0,0 +1,139 @@
<template>
<meta-head title="Восстановить пароль">
<meta name="description" content="Восстановить пароль">
</meta-head>
<!-- <header-auth /> -->
<!-- min-h-[calc(100vh-64px)] -->
<div class="min-h-screen place-items-center grid md:pt-3 bg-center bg-cover"
style="background-image: url('/image/auth-bg.jpg');"
>
<div class="w-full max-w-3xl mx-auto ">
<div class="md:grid grid-cols-12 ">
<div class="col-span-12 bg-indigo-200">
<div class="py-12 lg:py-20 px-10">
<inertia-link
:href="route('login')"
class="inline-flex gap-1 items-center text-gray-light"
>
<svg xmlns="http://www.w3.org/2000/svg" width="18"
height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="flex-shrink-0"
><polyline points="9 10 4 15 9 20"></polyline><path d="M20 4v7a4 4 0 0 1-4 4H4"></path></svg> Вернуться
</inertia-link>
<h1 class="mt-5 font-medium text-xl md:text-2xl lg:text-4xl text-white text-center">
Восстановить пароль
</h1>
<div v-if="$page.props.flash.status">
<p class="text-xl text-orange text-center mt-3">
{{ $page.props.flash.status }}
</p>
</div>
<div class="auth-form mt-7">
<form class="space-y-6" @submit.prevent="submit">
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<path
d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
>
</path>
<polyline points="22,6 12,13 2,6"></polyline>
</svg>
</div>
<input
v-model="form.email"
autocapitalize="off"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="email" placeholder="E-mail"
>
</div>
<div v-if="form.errors.email" class="form-error text-sm text-red">
{{ form.errors.email }}
</div>
</div>
<div class="text-center">
<loading-button :loading="form.processing" class="mx-3 my-1 px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white bg-orange focus:outline-none"
type="submit"
>
Отправить
</loading-button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- <div class="bg-indigo-300 mt-auto">
<div class="container mx-auto p-2 text-sm text-gray-light">
<div class="-mx-2 -my-2 flex flex-wrap justify-between">
<div class="mx-2 my-2">
<a href="/company/about">О компании</a>
</div>
<div class="mx-2 my-2">
<a href="/company/offer">Оферта</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-payment">Условия оплаты</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-service">Условия предоставления услуг</a>
</div>
<div class="mx-2 my-2">
<a href="/company/cookies">Cookies</a>
</div>
<div class="mx-2 my-2">
<a href="/company/privacy-policy">Privacy Policy</a>
</div>
</div>
</div>
</div> -->
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
// import HeaderAuth from '@/Shared/LayoutParts/HeaderAuth.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
// HeaderAuth,
LoadingButton,
},
setup() {
const form = Inertia.form({
email: null,
})
const submit = () => {
form.post(route('password.email'), {
onSuccess: () => form.reset('email'),
})
}
return {
form,
submit,
}
},
}
</script>

196
resources/js/Pages/Auth/Login.vue Executable file
View File

@@ -0,0 +1,196 @@
<template>
<meta-head title="Авторизация">
<meta name="description" content="Авторизация">
</meta-head>
<!-- <header-auth /> -->
<div class="min-h-screen place-items-center grid md:pt-3 bg-center bg-cover"
style="background-image: url('/image/auth-bg.jpg');"
>
<div class="w-full max-w-7xl mx-auto ">
<div class="md:grid grid-cols-12 ">
<div class="col-span-7 bg-indigo-200">
<div class="p-4 lg:p-28">
<inertia-link
:href="route('dashboard')"
class="inline-flex gap-1 items-center text-gray-light"
>
<svg xmlns="http://www.w3.org/2000/svg" width="18"
height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="flex-shrink-0"
><polyline points="9 10 4 15 9 20"></polyline><path d="M20 4v7a4 4 0 0 1-4 4H4"></path></svg> Вернуться
</inertia-link>
<h1 class="font-medium text-xl md:text-2xl lg:text-4xl text-white text-center">
Войти
</h1>
<div v-if="$page.props.flash.status">
<p class="text-lg md:text-xl text-orange text-center mt-3">
{{ $page.props.flash.status }}
</p>
</div>
<div class="auth-form mt-7">
<form class="space-y-6" @submit.prevent="submit">
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<path
d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
>
</path>
<polyline points="22,6 12,13 2,6"></polyline>
</svg>
</div>
<input
v-model="form.email"
autocapitalize="off"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="email" placeholder="E-mail"
>
</div>
<div v-if="form.errors.email" class="form-error text-sm text-red">
{{ form.errors.email }}
</div>
</div>
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<rect x="3" y="11"
width="18" height="11"
rx="2" ry="2"
></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
</div>
<input
v-model="form.password"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="password" placeholder="Пароль"
>
</div>
<div v-if="form.errors.password" class="form-error text-sm text-red">
{{ form.errors.password }}
</div>
</div>
<div class="flex items-center">
<input id="remember" v-model="form.remember"
type="checkbox" class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="remember" class="select-none ml-3 text-gray">Запомни меня</label>
</div>
<div class="flex items-center justify-between gap-1">
<loading-button :loading="form.processing" class="mx-3 my-1 px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white bg-orange focus:outline-none"
type="submit"
>
Войти
</loading-button>
<inertia-link :href="route('password.request')" class="hover:underline text-sm text-gray-light">
Восстановить пароль
</inertia-link>
</div>
</form>
</div>
</div>
</div>
<div class="col-span-5 bg-center bg-cover" style="background-image: url('image/orange-auth.jpg');">
<div class="py-4 lg:py-28 px-4">
<div class="font-medium text-xl md:text-2xl lg:text-4xl !leading-relaxed text-white text-center">
Заработай на своем
творчестве первый миллион
</div>
<div class="font-medium text-xl md:text-2xl lg:text-4xl !leading-relaxed text-white text-center">
Присоединяйся к teeaseer
прямо сейчас!
</div>
<div class="text-center mt-4 md:mt-10">
<inertia-link
:href="route('register')"
class="mx-3 my-1 px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white border border-white bg-transparent focus:outline-none"
>
Зарегистрироваться
</inertia-link>
</div>
</div>
</div>
</div>
</div>
<!-- <div class="bg-indigo-300 mt-auto">
<div class="container mx-auto p-2 text-sm text-gray-light">
<div class="-mx-2 -my-2 flex flex-wrap justify-between">
<div class="mx-2 my-2">
<a href="/company/about">О компании</a>
</div>
<div class="mx-2 my-2">
<a href="/company/offer">Оферта</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-payment">Условия оплаты</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-service">Условия предоставления услуг</a>
</div>
<div class="mx-2 my-2">
<a href="/company/cookies">Cookies</a>
</div>
<div class="mx-2 my-2">
<a href="/company/privacy-policy">Privacy Policy</a>
</div>
</div>
</div>
</div> -->
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
// import HeaderAuth from '@/Shared/LayoutParts/HeaderAuth.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
// HeaderAuth,
LoadingButton,
},
setup() {
const form = Inertia.form({
email: null,
password: null,
remember: false,
})
const submit = () => {
form.post(route('login.store'))
}
return {
form,
submit,
}
},
}
</script>

View File

@@ -0,0 +1,219 @@
<template>
<meta-head title="Авторизация">
<meta name="description" content="Авторизация">
</meta-head>
<!-- <header-auth /> -->
<div class="min-h-screen place-items-center grid md:pt-3 bg-center bg-cover"
style="background-image: url('/image/auth-bg.jpg');"
>
<div class="w-full max-w-7xl mx-auto ">
<div class="md:grid grid-cols-12 ">
<div class="col-span-7 bg-indigo-200">
<div class="p-4 lg:p-28">
<inertia-link
:href="route('dashboard')"
class="inline-flex gap-1 items-center text-gray-light"
>
<svg xmlns="http://www.w3.org/2000/svg" width="18"
height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="flex-shrink-0"
><polyline points="9 10 4 15 9 20"></polyline><path d="M20 4v7a4 4 0 0 1-4 4H4"></path></svg> Вернуться
</inertia-link>
<h1 class="font-medium text-xl md:text-2xl lg:text-4xl text-white text-center">
Регистрация
</h1>
<div class="auth-form mt-7">
<form class="space-y-6" @submit.prevent="submit">
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7"
r="4"
></circle></svg>
</div>
<input
v-model="form.first_name"
autocapitalize="off"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="text" placeholder="Имя"
>
</div>
<div v-if="form.errors.email" class="form-error text-sm text-red">
{{ form.errors.first_name }}
</div>
</div>
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<path
d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
>
</path>
<polyline points="22,6 12,13 2,6"></polyline>
</svg>
</div>
<input
v-model="form.email"
autocapitalize="off"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="email" placeholder="E-mail"
>
</div>
<div v-if="form.errors.email" class="form-error text-sm text-red">
{{ form.errors.email }}
</div>
</div>
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<rect x="3" y="11"
width="18" height="11"
rx="2" ry="2"
></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
</div>
<input
v-model="form.password"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="password" placeholder="Пароль"
>
</div>
<div v-if="form.errors.password" class="form-error text-sm text-red">
{{ form.errors.password }}
</div>
</div>
<div class="flex">
<input id="check" v-model="form.check"
type="checkbox" class="mt-1 h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="check" class="select-none ml-3 text-gray"> Нажимая на кнопку я даю согласие на
<a class="underline" href="/docs/personal_data.pdf"
target="_blank" rel="noopener noreferrer nofollow"
>обработку персональных данных</a> и соглашаюсь
<a class="underline" href="/docs/privacy_policy.pdf"
target="_blank" rel="noopener noreferrer nofollow"
>с условиями политики конфеденциальности</a>.</label>
</div>
<div class="text-center">
<loading-button :loading="form.processing" class="mx-3 my-1 px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white bg-orange focus:outline-none"
type="submit"
>
Зарегистрироваться
</loading-button>
</div>
</form>
</div>
</div>
</div>
<div class="col-span-5 bg-center bg-cover" style="background-image: url('image/orange-auth.jpg');">
<div class="py-4 lg:py-28 px-4 flex flex-col h-full justify-center">
<div class="font-medium text-xl md:text-2xl lg:text-4xl !leading-relaxed text-white text-center">
Уже есть учетная запись?
</div>
<div class="text-center mt-4 md:mt-10">
<inertia-link
:href="route('login')"
class="mx-3 my-1 px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white border border-white bg-transparent focus:outline-none"
>
Войти
</inertia-link>
</div>
</div>
</div>
</div>
</div>
<!-- <div class="bg-indigo-300 mt-auto">
<div class="container mx-auto p-2 text-sm text-gray-light">
<div class="-mx-2 -my-2 flex flex-wrap justify-between">
<div class="mx-2 my-2">
<a href="/company/about">О компании</a>
</div>
<div class="mx-2 my-2">
<a href="/company/offer">Оферта</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-payment">Условия оплаты</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-service">Условия предоставления услуг</a>
</div>
<div class="mx-2 my-2">
<a href="/company/cookies">Cookies</a>
</div>
<div class="mx-2 my-2">
<a href="/company/privacy-policy">Privacy Policy</a>
</div>
</div>
</div>
</div> -->
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
// import HeaderAuth from '@/Shared/LayoutParts/HeaderAuth.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
// HeaderAuth,
LoadingButton,
},
setup() {
const form = Inertia.form({
first_name: null,
email: null,
password: null,
check: false,
})
const submit = () => {
if(form.check){
form.post(route('register.store'))
}
// else{
// alert('Нужно дать согласие на обработку персональных данных')
// }
}
return {
form,
submit,
}
},
}
</script>

View File

@@ -0,0 +1,203 @@
<template>
<meta-head title="Восстановить пароль">
<meta name="description" content="Восстановить пароль">
</meta-head>
<!-- <header-auth /> -->
<div class="min-h-screen place-items-center grid md:pt-3 bg-center bg-cover"
style="background-image: url('/image/auth-bg.jpg');"
>
<div class="w-full max-w-3xl mx-auto ">
<div class="md:grid grid-cols-12 ">
<div class="col-span-12 bg-indigo-200">
<div class="py-12 lg:py-20 px-10">
<inertia-link
:href="route('dashboard')"
class="inline-flex gap-1 items-center text-gray-light"
>
<svg xmlns="http://www.w3.org/2000/svg" width="18"
height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="flex-shrink-0"
><polyline points="9 10 4 15 9 20"></polyline><path d="M20 4v7a4 4 0 0 1-4 4H4"></path></svg> Вернуться
</inertia-link>
<h1 class="mt-5 font-medium text-xl md:text-2xl lg:text-4xl text-white text-center">
Восстановить пароль
</h1>
<div v-if="$page.props.flash.status">
<p class="text-lg md:text-xl text-orange text-center mt-3">
{{ $page.props.flash.status }}
</p>
</div>
<div class="auth-form mt-7">
<form class="space-y-6" @submit.prevent="submit">
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<path
d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
>
</path>
<polyline points="22,6 12,13 2,6"></polyline>
</svg>
</div>
<input
v-model="form.email"
autocapitalize="off"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="email" placeholder="E-mail"
>
</div>
<div v-if="form.errors.email" class="form-error text-sm text-red">
{{ form.errors.email }}
</div>
</div>
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<rect x="3" y="11"
width="18" height="11"
rx="2" ry="2"
></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
</div>
<input
v-model="form.password"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="password" placeholder="Пароль"
>
</div>
<div v-if="form.errors.password" class="form-error text-sm text-red">
{{ form.errors.password }}
</div>
</div>
<div>
<div class="relative">
<div class="absolute inset-y-0 left-5 text-gray flex items-center">
<svg 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" class="flex-shrink-0"
>
<rect x="3" y="11"
width="18" height="11"
rx="2" ry="2"
></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
</div>
<input
v-model="form.password_confirmation"
class="w-full pl-14 h-12 md:h-16 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border-transparent bg-indigo-100 rounded-md placeholder-gray"
type="password" placeholder="Повторите пароль"
>
</div>
<div v-if="form.errors.password_confirmation" class="form-error text-sm text-red">
{{ form.errors.password_confirmation }}
</div>
</div>
<div class="text-center">
<loading-button :loading="form.processing" class="mx-3 my-1 px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white bg-orange focus:outline-none"
type="submit"
>
Отправить
</loading-button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- <div class="bg-indigo-300 mt-auto">
<div class="container mx-auto p-2 text-sm text-gray-light">
<div class="-mx-2 -my-2 flex flex-wrap justify-between">
<div class="mx-2 my-2">
<a href="/company/about">О компании</a>
</div>
<div class="mx-2 my-2">
<a href="/company/offer">Оферта</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-payment">Условия оплаты</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-service">Условия предоставления услуг</a>
</div>
<div class="mx-2 my-2">
<a href="/company/cookies">Cookies</a>
</div>
<div class="mx-2 my-2">
<a href="/company/privacy-policy">Privacy Policy</a>
</div>
</div>
</div>
</div> -->
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
// import HeaderAuth from '@/Shared/LayoutParts/HeaderAuth.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
// HeaderAuth,
LoadingButton,
},
props: {
token: {
type: String,
default: ''
}
},
setup(props) {
const form = Inertia.form({
email: null,
password: null,
password_confirmation: null,
token: props.token
})
const submit = () => {
form.post(route('password.store'))
}
return {
form,
submit,
}
},
}
</script>

View File

@@ -0,0 +1,62 @@
<template>
<meta-head title="Авторизация">
<meta name="description" content="Авторизация">
</meta-head>
<!-- <header-auth /> -->
<div class="min-h-screen place-items-center grid md:pt-3 bg-center bg-cover"
style="background-image: url('/image/auth-bg.jpg');"
>
<div class="w-full md:w-auto max-w-7xl mx-auto ">
<div class="md:grid grid-cols-12 ">
<div class="col-span-12 bg-center bg-cover" style="background-image: url('image/orange-auth.jpg');">
<div class="py-4 lg:py-28 px-10">
<div class="font-medium text-xl md:text-2xl lg:text-4xl !leading-relaxed text-white text-center">
Пожалуйста подтвердите свой адрес электронной почты
</div>
</div>
</div>
</div>
</div>
<!-- <div class="bg-indigo-300 mt-auto">
<div class="container mx-auto p-2 text-sm text-gray-light">
<div class="-mx-2 -my-2 flex flex-wrap justify-between">
<div class="mx-2 my-2">
<a href="/company/about">О компании</a>
</div>
<div class="mx-2 my-2">
<a href="/company/offer">Оферта</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-payment">Условия оплаты</a>
</div>
<div class="mx-2 my-2">
<a href="/company/terms-service">Условия предоставления услуг</a>
</div>
<div class="mx-2 my-2">
<a href="/company/cookies">Cookies</a>
</div>
<div class="mx-2 my-2">
<a href="/company/privacy-policy">Privacy Policy</a>
</div>
</div>
</div>
</div> -->
</div>
</template>
<script>
// import HeaderAuth from '@/Shared/LayoutParts/HeaderAuth.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
// HeaderAuth,
},
}
</script>

View File

@@ -0,0 +1,130 @@
<template>
<meta-head title="Пожаловаться на контент"></meta-head>
<modal-feed
:modal-feed="modalFeed"
:is_exist_menu="false"
:open="show"
@close-modal="closeModal"
/>
<div class="mt-16 md:container !max-w-5xl mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<form class=" bg-indigo-200 shadow-classic rounded-md p-5" @submit.prevent="submit">
<div class="mb-4 flex items-center text-gray-light text-lg font-medium">
<link-back class="default block hover:underline">
Вернуться
</link-back>
<span class="px-3">/</span>
<h1 class="text-gray">
Пожаловаться на контент
</h1>
</div>
<div class="mt-8 grid grid-cols-2">
<div class="space-y-4">
<div v-for="reason in reasons" :key="reason.id"
class="flex"
>
<input :id="`reason_${reason.id}`" v-model="form.reason"
:value="reason.id" name="reason"
type="radio" class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label :for="`reason_${reason.id}`" class="select-none ml-3 text-gray leading-none">{{ reason.name }}</label>
</div>
</div>
<div>
<feed-preview class="ml-auto w-20 h-20 lg:w-48 lg:h-48 object-cover cursor-pointer"
:type="feed.type"
:source="feed.entity.preview"
@click="openModal(feed)"
/>
</div>
</div>
<div class="mt-12 flex flex-wrap -my-1 -mx-3">
<progress
v-if="form.progress"
class="mx-3 my-1 w-full"
:value="form.progress.percentage"
max="100"
>
{{ form.progress.percentage }}%
</progress>
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Отправить
</loading-button>
<link-back class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none">
Отменить
</link-back>
</div>
</form>
</div>
</template>
<script>
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import { toRefs } from 'vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import LinkBack from '@/Shared/Misc/LinkBack.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import ModalFeed from '@/Shared/Overlay/ModalFeed.vue'
import FeedPreview from '@/Shared/Feed/FeedPreview.vue'
export default {
components: {
MetaHead,
LoadingButton,
ModalFeed,
FeedPreview,
LinkBack,
},
layout: Layout,
props: {
feed: Object,
user: Object,
reasons: Array,
},
setup(props) {
const { feed } = toRefs(props)
const form = useForm({
reason: null,
feed: feed.value.id,
})
const submit = () => {
form.post(route('complaint.store'))
form.reason = null
}
return {
form,
submit,
}
},
data() {
return {
show: false,
modalFeed: {},
}
},
computed: {
authUser() {
return usePage().props.value.auth.user
},
},
methods: {
openModal(feed) {
this.show = true
this.modalFeed = feed
},
closeModal() {
this.show = false
},
},
}
</script>

View File

@@ -0,0 +1,100 @@
<template>
<meta-head title="Пожаловаться на комментарий"></meta-head>
<div class="mt-16 md:container !max-w-5xl mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<form class=" bg-indigo-200 shadow-classic rounded-md p-5" @submit.prevent="submit">
<div class="mb-4 flex items-center text-gray-light text-sm md:text-lg font-medium">
<link-back class="default block hover:underline">
Вернуться
</link-back>
<span class="px-3">/</span>
<h1 class="text-gray">
Пожаловаться на комментарий
</h1>
</div>
<div class="mt-8 md:grid grid-cols-2">
<div class="space-y-4">
<div v-for="reason in reasons" :key="reason.id"
class="flex"
>
<input :id="`reason_${reason.id}`" v-model="form.reason"
:value="reason.id" name="reason"
type="radio" class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label :for="`reason_${reason.id}`" class="select-none ml-3 text-gray leading-none">{{ reason.name }}</label>
</div>
</div>
<div class="mt-5 md:mt-0">
<inertia-link :href="route('profile.user', user.username)" class=" flex items-center mr-3">
<user-avatar
:user="user"
size="small"
class="text-xs w-12 h-12"
/>
<span class="text-gray-light ml-3">{{ user.name }}</span>
</inertia-link>
<h3 class="mt-3 font-medium text-gray-light">
Текст комментария:
</h3>
<div class="mt-3 text-lg text-white">
{{ comment.body }}
</div>
</div>
</div>
<div class="mt-12 flex flex-wrap -my-1 -mx-3">
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Отправить
</loading-button>
<link-back class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none">
Отменить
</link-back>
</div>
</form>
</div>
</template>
<script>
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import { toRefs } from 'vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import LinkBack from '@/Shared/Misc/LinkBack.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
export default {
components: {
MetaHead,
LoadingButton,
LinkBack,
UserAvatar
},
layout: Layout,
props: {
comment: Object,
user: Object,
reasons: Array,
},
setup(props) {
const { comment } = toRefs(props)
const form = useForm({
reason: null,
comment: comment.value.id,
})
const submit = () => {
form.post(route('complaint.store.comment'))
form.reason = null
}
return {
form,
submit,
}
},
}
</script>

View File

@@ -0,0 +1,206 @@
<template>
<meta-head title="Новинки"></meta-head>
<div class="mb-6 py-10 md:py-14 xl:py-20 banner relative bg-center bg-no-repeat bg-cover"
style="background-image: url('/image/bg-home.jpg');"
>
<div class="min-h-[24rem] flex justify-center items-center text-center">
<div class="max-w-4xl text-gray px-3">
<p class="text-md md:text-2xl">
Социальная сеть для творческих людей
</p>
<h1
class="mt-4 md:mt-8 text-xl md:text-2xl lg:text-3xl xl:text-5xl text-white font-semibold xl:leading-relaxed"
>
Зарабатывай на
своем творчестве вместе с teeaseer
</h1>
<div v-if="!$page.props.auth.user" class="mt-4 md:flex flex-wrap -mx-3 justify-center">
<inertia-link
:href="route('login')"
class="min-w-[260px] mx-3 my-1 px-6 md:px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white border border-white bg-transparent focus:outline-none"
>
Войти
</inertia-link>
<inertia-link
:href="route('register')"
class="min-w-[260px] mx-3 my-1 px-6 md:px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white border border-white bg-transparent focus:outline-none"
>
Зарегистрироваться
</inertia-link>
<inertia-link
:href="route('common.write-to-us.get')"
class="min-w-[260px] mx-3 my-1 px-6 md:px-12 py-3 transition shadow-none hover:shadow-classic2 inline-flex items-center justify-center text-base rounded-full text-white border border-white bg-transparent focus:outline-none"
>
Написать нам
</inertia-link>
</div>
</div>
</div>
<div class="mt-3 text-gray mx-auto max-w-2xl">
<ul class="flex justify-center flex-wrap gap-3 text-xs px-3">
<li>
<a target="_blank" class="hover:underline"
href="/docs/company.pdf"
rel="noopener noreferrer nofollow"
>О компании</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/offer_authors.pdf"
rel="noopener noreferrer nofollow"
>Оферта для авторов</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/offer_paid_subscription.pdf"
rel="noopener noreferrer nofollow"
>Оферта на платную подписку</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/security_policy.pdf"
rel="noopener noreferrer nofollow"
>Политика безопасности</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/privacy_policy.pdf"
rel="noopener noreferrer nofollow"
>Политика конфиденциальности</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/terms_use.pdf"
rel="noopener noreferrer nofollow"
>Пользовательское соглашение</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/personal_data.pdf"
rel="noopener noreferrer nofollow"
>Согласие на обработку персональных данных</a>
</li>
</ul>
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 buttons-filter-line">
<div class=" relative mb-5">
<div class="absolute inset-y-0 left-3 flex items-center z-[1]">
<svg class="flex-shrink-0 h-5 w-5 text-gray-light" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" fill="currentColor"
>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11 4a7 7 0 100 14 7 7 0 000-14zm-9 7a9 9 0 1118 0 9 9 0 01-18 0z"
/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M15.943 15.943a1 1 0 011.414 0l4.35 4.35a1 1 0 01-1.414 1.414l-4.35-4.35a1 1 0 010-1.414z"
/>
</svg>
</div>
<input
v-model="form.search"
class="relative w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light !pl-10 h-14" placeholder="Поиск"
type="search"
>
</div>
<div class="grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<inertia-link :href="route('dashboard')"
:class="[active_filter == 'new' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center rounded-md text-white focus:outline-none']"
>
Новинки
</inertia-link>
<inertia-link :href="route('dashboard')"
:data="{ filter: 'hot' }"
:class="[active_filter == 'hot' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center shadow-classic rounded-md text-white focus:outline-none']"
>
<svg class="-ml-1 mr-2 h-4 w-4 md:h-5 md:w-5 flex-shrink-0" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
>
<path
d="M14.422 6.243c-3.57-2.172-1.895-5.238-1.824-5.365A.586.586 0 0012.09 0c-1.837 0-3.276.522-4.276 1.552-1.71 1.76-1.63 4.498-1.597 5.667.004.13.007.242.007.325 0 .868.14 1.67.263 2.377.079.456.147.85.16 1.159.012.331-.048.407-.05.41-.01.01-.081.049-.276.049a.657.657 0 01-.53-.235c-.565-.634-.604-2.365-.496-3.307a.586.586 0 00-.582-.654c-1.521 0-2.658 2.488-2.658 4.712 0 1.046.21 2.07.625 3.045a8.081 8.081 0 001.7 2.527C5.894 19.157 7.89 20 10 20c2.119 0 4.114-.83 5.618-2.34a7.892 7.892 0 002.327-5.605c0-2.692-2.107-4.95-3.523-5.812zM10 18.828c-3.671 0-6.773-3.101-6.773-6.773 0-.893.213-1.83.585-2.571.086-.173.175-.322.263-.447.01.967.163 2.284.841 3.046.366.41.852.627 1.405.627.518 0 .912-.156 1.172-.466.486-.578.337-1.436.148-2.524-.115-.66-.245-1.409-.245-2.176 0-.1-.003-.22-.007-.359-.032-1.107-.099-3.412 1.265-4.817.613-.63 1.467-1.014 2.55-1.145-.123.406-.228.92-.232 1.497-.01 1.27.476 3.085 2.841 4.524 1.145.696 2.96 2.618 2.96 4.811A6.78 6.78 0 0110 18.828z"
/>
</svg>
Популярные
</inertia-link>
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 my-6">
<button class="button-default text-gray text-lg" @click="resetFilter">
Очистить фильтры
</button>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 cards-block rounded-md bg-indigo-200 shadow-classic grid gap-2 lg:gap-4 grid-cards p-2 lg:p-5">
<feed
:next-cursor="nextCursor"
:feeds="feeds"
/>
</div>
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import pickBy from 'lodash/pickBy'
import throttle from 'lodash/throttle'
import Feed from '@/Shared/Feed/Feed.vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
Feed
},
layout: Layout,
props: {
feeds: Array,
nextCursor: String,
active_filter: String,
searchFilters: Object,
},
data() {
return {
form: {
search: this.searchFilters.search,
},
}
},
watch: {
form: {
deep: true,
handler: throttle(function () {
Inertia.get(route('dashboard'), pickBy(this.form), {
preserveScroll: true, preserveState: true
})
}, 500),
},
},
methods: {
resetFilter() {
Inertia.get(
route('dashboard'),
{},
{
preserveScroll: true,
}
)
},
},
}
</script>

113
resources/js/Pages/Feed/Index.vue Executable file
View File

@@ -0,0 +1,113 @@
<template>
<meta-head title="Новости"></meta-head>
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<div class="items-center grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4 2xl:gap-12">
<dropdown-menu>
<MenuItems class="origin-top-left absolute left-0 mt-2 w-64 bg-indigo-300 shadow-lg max-h-60 rounded-md text-base ring-1 ring-indigo-200 overflow-auto focus:outline-none">
<MenuItem>
<inertia-link :href="route('videos.create')" class="group flex items-center px-4 py-2 text-base hover:bg-indigo-200 text-gray-light">
<VideoCameraIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
Загрузить видео
</inertia-link>
</MenuItem>
<MenuItem>
<inertia-link :href="route('images.create')" class="group flex items-center px-4 py-2 text-base hover:bg-indigo-200 text-gray-light">
<PhotographIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
Загрузить изображение
</inertia-link>
</MenuItem>
<MenuItem>
<inertia-link :href="route('musics.create')" class="group flex items-center px-4 py-2 text-base hover:bg-indigo-200 text-gray-light">
<MusicNoteIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
Загрузить музыку
</inertia-link>
</MenuItem>
</MenuItems>
</dropdown-menu>
<inertia-link
:href="route('feeds.layoutsidebar.image')"
:class="[active_button == 'image' ? 'bg-orange' : 'bg-indigo-200 hover:bg-orange', 'transition inline-flex items-center px-3 py-3 lg:px-7 lg:py-5 justify-center shadow-classic text-sm rounded-md text-white focus:outline-none']"
>
<svg class="-ml-1 mr-2 h-4 w-4 md:h-5 md:w-5 flex-shrink-0" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M4.167 3.333a.833.833 0 00-.834.834v11.666c0 .46.373.834.834.834h11.666c.46 0 .834-.373.834-.834V4.167a.833.833 0 00-.834-.834H4.167zm-2.5.834a2.5 2.5 0 012.5-2.5h11.666a2.5 2.5 0 012.5 2.5v11.666a2.5 2.5 0 01-2.5 2.5H4.167a2.5 2.5 0 01-2.5-2.5V4.167z"
/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M7.083 6.667a.417.417 0 100 .833.417.417 0 000-.833zM5 7.083a2.083 2.083 0 114.167 0 2.083 2.083 0 01-4.167 0zM12.744 7.744a.833.833 0 011.179 0l4.166 4.167a.834.834 0 01-1.178 1.178l-3.578-3.577-8.577 8.577a.833.833 0 01-1.179-1.178l9.167-9.167z"
/>
</svg>
<span class="truncate">Изображения</span>
</inertia-link>
<inertia-link
:href="route('feeds.layoutsidebar.video')"
:class="[active_button == 'video' ? 'bg-orange' : 'bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-7 lg:py-5 justify-center shadow-classic text-sm rounded-md text-white focus:outline-none']"
>
<svg class="-ml-1 mr-2 h-4 w-4 md:h-5 md:w-5 flex-shrink-0" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
><path fill-rule="evenodd" clip-rule="evenodd"
d="M3.483 2.5a.983.983 0 00-.983.983v13.034c0 .543.44.983.983.983h13.034c.543 0 .983-.44.983-.983V3.483a.983.983 0 00-.983-.983H3.483zm-2.65.983a2.65 2.65 0 012.65-2.65h13.034a2.65 2.65 0 012.65 2.65v13.034a2.65 2.65 0 01-2.65 2.65H3.483a2.65 2.65 0 01-2.65-2.65V3.483z"
/><path fill-rule="evenodd" clip-rule="evenodd"
d="M5.833.833c.46 0 .834.373.834.834v16.666a.833.833 0 11-1.667 0V1.667c0-.46.373-.834.833-.834zM14.167.833c.46 0 .833.373.833.834v16.666a.833.833 0 11-1.667 0V1.667c0-.46.373-.834.834-.834z"
/><path fill-rule="evenodd" clip-rule="evenodd"
d="M.833 10c0-.46.373-.833.834-.833h16.666a.833.833 0 010 1.666H1.667A.833.833 0 01.833 10zM.833 5.833c0-.46.373-.833.834-.833h4.166a.833.833 0 010 1.667H1.667a.833.833 0 01-.834-.834zM.833 14.167c0-.46.373-.834.834-.834h4.166a.833.833 0 010 1.667H1.667a.833.833 0 01-.834-.833zM13.333 14.167c0-.46.373-.834.834-.834h4.166a.833.833 0 010 1.667h-4.166a.833.833 0 01-.834-.833zM13.333 5.833c0-.46.373-.833.834-.833h4.166a.833.833 0 010 1.667h-4.166a.833.833 0 01-.834-.834z"
/></svg>
<span class="truncate">Видео</span>
</inertia-link>
<inertia-link
:href="route('feeds.layoutsidebar.music')"
:class="[active_button == 'music' ? 'bg-orange' : 'bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-7 lg:py-5 justify-center shadow-classic text-sm rounded-md text-white focus:outline-none']"
>
<svg class="-ml-1 mr-2 h-4 w-4 md:h-5 md:w-5 flex-shrink-0" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
><path fill-rule="evenodd" clip-rule="evenodd"
d="M12.399 12.399a2.5 2.5 0 011.768-.732H17.5c.46 0 .833.373.833.833v1.667a2.5 2.5 0 01-2.5 2.5h-1.666a2.5 2.5 0 01-1.768-4.268zm1.768.934a.833.833 0 000 1.667h1.666a.833.833 0 00.834-.833v-.834h-2.5zm-11.768.733a2.5 2.5 0 011.768-.733H7.5c.46 0 .833.373.833.834v1.666a2.5 2.5 0 01-2.5 2.5H4.167a2.5 2.5 0 01-1.768-4.267zM4.167 15a.833.833 0 100 1.667h1.666a.833.833 0 00.834-.834V15h-2.5z"
/><path fill-rule="evenodd" clip-rule="evenodd"
d="M18.039 1.864c.187.158.294.391.294.636v10a.833.833 0 01-1.666 0V3.484L8.333 4.873v9.294a.833.833 0 01-1.666 0v-10c0-.408.294-.755.696-.822l10-1.667a.833.833 0 01.676.186z"
/></svg>
<span class="truncate">Музыка</span>
</inertia-link>
</div>
</div>
<div class="feeds-user-list mt-5 container mx-auto px-2 md:px-6 2xl:px-28 space-y-4 lg:space-y-8">
<feed-list :feeds="feeds" :next-cursor="nextCursor" />
</div>
</template>
<script>
import {
PhotographIcon,
VideoCameraIcon,
MusicNoteIcon,
} from '@heroicons/vue/solid'
import { MenuItem, MenuItems } from '@headlessui/vue'
import FeedList from '@/Shared/FeedList/FeedList.vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import DropdownMenu from '@/Shared/Form/DropdownMenu.vue'
export default {
components: {
FeedList,
MetaHead,
DropdownMenu,
MenuItem,
MenuItems,
PhotographIcon,
MusicNoteIcon,
VideoCameraIcon
},
layout: Layout,
props: {
nextCursor: String,
feeds: Array,
active_button: String,
},
}
</script>

View File

@@ -0,0 +1,171 @@
<template>
<meta-head title="Загрузить изображение"></meta-head>
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<form class=" bg-indigo-200 shadow-classic rounded-md p-5" @submit.prevent="submit">
<div class="mb-4 flex items-center text-gray-light text-lg font-medium">
<link-back class="default block hover:underline">
Вернуться
</link-back>
<span class="px-3">/</span>
<h1 class="text-gray">
Загрузка изображения
</h1>
</div>
<div class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.title" :error="form.errors.title"
type="text" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Название"
/>
<warning-text />
</div>
<div class="flex flex-col">
<textarea-input v-model="form.body" :error="form.errors.body"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md" cols="30"
rows="4" label="Описание"
/>
</div>
<div class="flex flex-col">
<file-input-multiple
v-model="form.photos"
accept="image/png, image/jpeg, image/jpg"
:error="form.errors.photos"
label="Выбрать изображения"
/>
</div>
<div class="text-gray-light">
<TagInput v-model="form.tags" />
</div>
<div>
<div class="text-gray-light text-lg mb-2">
Тип контента
</div>
<div class="flex flex-wrap gap-4">
<div class="flex items-center">
<input id="feed-paid-1" v-model="form.is_paid"
value="0" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-1" class="select-none ml-3 text-gray">Бесплатный</label>
</div>
<div class="flex items-center">
<input id="feed-paid-2" v-model="form.is_paid"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-2" class="select-none ml-3 text-gray">Эксклюзивный</label>
</div>
<div v-if="authUser.private" class="flex items-center">
<input id="feed-paid-3" v-model="form.is_paid"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-3" class="select-none ml-3 text-gray">Доступен по личной подписке</label>
</div>
</div>
</div>
<!-- <div class="flex items-center">
<input id="is_adult_feed" v-model="form.is_adult"
type="checkbox"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="is_adult_feed" class="select-none ml-3 text-gray">Контент для взрослых</label>
</div> -->
<div v-if="form.is_paid == 1" class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.price" :error="form.errors.price"
type="number" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Цена"
/>
</div>
<div class="flex flex-col">
<file-input-multiple
v-model="form.photos_paid"
:error="form.errors.photos_paid"
accept="image/png, image/jpeg, image/jpg"
label="Выбрать изображения"
/>
</div>
</div>
</div>
<div class="mt-12 flex flex-wrap -my-1 -mx-3">
<progress
v-if="form.progress"
class="mx-3 my-1 w-full"
:value="form.progress.percentage"
max="100"
>
{{ form.progress.percentage }}%
</progress>
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Создать
</loading-button>
<link-back class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none">
Отменить
</link-back>
</div>
</form>
</div>
</template>
<script>
import { ref, computed } from 'vue'
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import FileInputMultiple from '@/Shared/Form/FileInputMultiple.vue'
import TextareaInput from '@/Shared/Form/TextareaInput.vue'
import TagInput from '@/Shared/Form/TagInput.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import LinkBack from '@/Shared/Misc/LinkBack.vue'
import WarningText from '@/Shared/Misc/WarningText.vue'
export default {
components: {
MetaHead,
TextInput,
FileInputMultiple,
LoadingButton,
TextareaInput,
LinkBack,
TagInput,
WarningText,
},
layout: Layout,
setup() {
// let isFeedPaid = ref(0)
// let openPaidBlock = ref(0)
const form = useForm({
title: null,
body: null,
price: null,
photos: null,
photos_paid: null,
tags: [],
is_paid: 0,
})
// watch(isFeedPaid, (value) => {
// openPaidBlock.value = value
// form.is_paid = value
// })
const authUser = computed(() => usePage().props.value.auth.user)
const submit = () => {
form.post(route('images.store'))
}
return { form, submit, authUser }
},
}
</script>

313
resources/js/Pages/Image/Edit.vue Executable file
View File

@@ -0,0 +1,313 @@
<template>
<meta-head title="Обновить изображение"></meta-head>
<modal-warning
:feed_id="feed.id"
:open="modalShow"
@action="deleteActionModal"
/>
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<div v-if="feed.status != 1" class="bg-indigo-200 shadow-classic rounded-md p-5 mb-10">
<p class="text-lg font-medium text-gray">
Контент запрещен к публикации, причина:
</p>
<p v-if="feed.status_note " class="text-gray-light">
<ul>
<li v-for="textBreak in textsBreak" :key="textBreak">
{{ textBreak }}
</li>
</ul>
</p>
<p v-else class="text-gray-light">
модерация созданного\обновленного контента
</p>
<p v-if="feed.status == 3" class="pt-3 text-green">
Обновления отправлены, ожидайте!
</p>
</div>
<form class="bg-indigo-200 shadow-classic rounded-md p-5" @submit.prevent="submit">
<div class="mb-4 flex items-center text-gray-light text-lg font-medium">
<link-back class="default block hover:underline">
Вернуться
</link-back>
<span class="px-3">/</span>
<h1 class="text-gray">
Редактирование изображения
</h1>
</div>
<div class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.title" :error="form.errors.title"
type="text" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Название"
/>
<warning-text />
</div>
<div class="flex flex-col">
<textarea-input v-model="form.body" :error="form.errors.body"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md" cols="30"
rows="4" label="Описание"
/>
</div>
<div class="border border-indigo-300 flex flex-col p-4 rounded-md">
<div>
<file-input-multiple
v-model="form.photos"
accept="image/png, image/jpeg, image/jpg"
:error="form.errors.photos"
label="Загрузить новые изображения"
/>
</div>
<div>
<h2 class="mt-3 mb-2 text-lg text-gray-light select-none">
Загруженные изображения:
</h2>
<div class="grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
<created-media-item
v-for="mediaCommon in mediasCommon" :key="mediaCommon.id"
:media="mediaCommon"
@addRemoveId="stateToggle"
/>
</div>
</div>
</div>
<div class="text-gray-light">
<TagInput v-model="form.tags" />
</div>
<div v-if="isFreeFeed">
<div class="text-gray-light text-lg mb-2">
Тип контента
</div>
<div class="flex flex-wrap gap-4">
<div class="flex items-center">
<input id="feed-paid-1" v-model="form.is_paid"
value="0" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-1" class="select-none ml-3 text-gray">Бесплатный</label>
</div>
<div class="flex items-center">
<input id="feed-paid-2" v-model="form.is_paid"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-2" class="select-none ml-3 text-gray">Эксклюзивный</label>
</div>
<div v-if="authUser.private" class="flex items-center">
<input id="feed-paid-3" v-model="form.is_paid"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-3" class="select-none ml-3 text-gray">Доступен по личной подписке</label>
</div>
</div>
</div>
<!-- <div class="flex items-center">
<input id="is_adult_feed" v-model="form.is_adult"
type="checkbox"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="is_adult_feed" class="select-none ml-3 text-gray">Контент для взрослых</label>
</div> -->
<div v-if="form.is_paid == 1" class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.price" :error="form.errors.price"
type="number" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Цена"
/>
</div>
<div class="border border-indigo-300 flex flex-col p-4 rounded-md space-y-3">
<div v-if="isFreeFeed">
<file-input-multiple
v-model="form.photos_paid"
:error="form.errors.photos_paid"
accept="image/png, image/jpeg, image/jpg"
label="Выбрать изображения"
/>
</div>
<div v-if="mediasPaid.length">
<h2 class="mb-2 text-lg text-gray-light select-none">
Загруженные платные изображения:
</h2>
<div class="grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
<created-media-item
v-for="mediaPaid in mediasPaid" :key="mediaPaid.id"
:edit="false"
:media="mediaPaid"
/>
</div>
</div>
</div>
</div>
</div>
<div class="mt-12 flex flex-wrap -my-1 -mx-3">
<progress
v-if="form.progress"
class="mx-3 my-1 w-full"
:value="form.progress.percentage"
max="100"
>
{{ form.progress.percentage }}%
</progress>
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Обновить
</loading-button>
<link-back class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none">
Отменить
</link-back>
<button type="button" class="mx-3 my-1 default text-lg text-pink"
@click="openModal"
>
Удалить
</button>
</div>
</form>
</div>
</template>
<script>
import find from 'lodash/find'
import { toRefs, watch, ref, computed } from 'vue'
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import { Inertia } from '@inertiajs/inertia'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import FileInputMultiple from '@/Shared/Form/FileInputMultiple.vue'
import TextareaInput from '@/Shared/Form/TextareaInput.vue'
import TagInput from '@/Shared/Form/TagInput.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import LinkBack from '@/Shared/Misc/LinkBack.vue'
import WarningText from '@/Shared/Misc/WarningText.vue'
import CreatedMediaItem from '@/Shared/Edit/CreatedMediaItem.vue'
import ModalWarning from '@/Shared/Overlay/ModalWarning.vue'
export default {
components: {
MetaHead,
CreatedMediaItem,
TextInput,
FileInputMultiple,
LoadingButton,
WarningText,
TextareaInput,
LinkBack,
TagInput,
ModalWarning,
},
layout: Layout,
props: {
feed: Object,
tags: Array,
mediasCommon: Array,
mediasPaid: Array,
mediasCount: Number,
},
setup(props) {
const { feed, tags, mediasCount } = toRefs(props)
const modalShow = ref(false)
const textsBreak = computed(() => feed.value.status_note.split('\n'))
// let isFeedPaid, openPaidBlock
let form_fields = {
id: feed.value.id,
title: feed.value.title,
body: feed.value.body,
price: feed.value.price,
tags: tags.value,
is_paid: feed.value.is_paid,
photos: null,
photos_paid: null,
removedItems: [],
totalItems: mediasCount,
}
if (feed.value.is_paid) {
// isFeedPaid = ref(1)
// openPaidBlock = ref(1)
delete form_fields.photos_paid
}
// else {
// isFeedPaid = ref(0)
// openPaidBlock = ref(0)
// }
const form = useForm(form_fields)
// watch(isFeedPaid, (value) => {
// openPaidBlock.value = value
// form.is_paid = value
// })
const submit = () => {
form.post(route('images.update', feed.value.id), {
onSuccess: () => {
form.reset('photos')
form.reset('photos_paid')
},
})
}
const stateToggle = (id) => {
let existID = find(form.removedItems, (itemID) => itemID === id)
if (existID) {
const index = form.removedItems.indexOf(existID)
if (index > -1) {
form.removedItems.splice(index, 1)
}
} else {
form.removedItems.push(id)
}
}
const deleteActionModal = (remove) => {
if (remove) {
Inertia.delete(route('feed.destroy', feed.value.id), {
preserveScroll: true,
preserveState: true,
})
}
modalShow.value = false
}
const openModal = () => modalShow.value = true
const authUser = computed(() => usePage().props.value.auth.user)
const isFreeFeed = computed(() => feed.value.is_paid === 0)
return {
form,
submit,
stateToggle,
authUser,
// isFeedPaid,
// openPaidBlock,
isFreeFeed,
modalShow,
deleteActionModal,
openModal,
textsBreak
}
},
updated(){
if (this.feed.is_paid) {
delete this.form.photos_paid
}
},
}
</script>

View File

@@ -0,0 +1,151 @@
<template>
<meta-head title="Изображения"></meta-head>
<div class="mb-6 py-3 banner relative bg-center bg-no-repeat bg-cover"
style="background-image: url('/image/bg-home.jpg');"
>
<div class="h-52 flex justify-center items-center text-center">
<div class="max-w-4xl text-gray px-3">
<h1 class="text-xl md:text-2xl lg:text-3xl xl:text-5xl text-white font-semibold xl:leading-relaxed">
Изображения
</h1>
<h2 v-if="tag" class="text-lg md:text-3xl text-gray-light">
#{{ tag.name }}
</h2>
<inertia-link :href="route('images.create')"
class="mt-8 inline-flex tracking-wide items-center px-8 md:px-12 py-3 border border-white text-sm lg:text-lg text-white rounded-full bg-transparent hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2"
>
Загрузить
</inertia-link>
</div>
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 buttons-filter-line">
<div class=" relative mb-5">
<div class="absolute inset-y-0 left-3 flex items-center z-[1]">
<svg class="flex-shrink-0 h-5 w-5 text-gray-light" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" fill="currentColor"
>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11 4a7 7 0 100 14 7 7 0 000-14zm-9 7a9 9 0 1118 0 9 9 0 01-18 0z"
/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M15.943 15.943a1 1 0 011.414 0l4.35 4.35a1 1 0 01-1.414 1.414l-4.35-4.35a1 1 0 010-1.414z"
/>
</svg>
</div>
<input
v-model="form.search"
class="relative w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light !pl-10 h-14" placeholder="Поиск"
type="search"
>
</div>
<div class="grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<inertia-link :href="local_route"
:class="[active_filter == 'new' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center rounded-md text-white focus:outline-none']"
>
Новинки
</inertia-link>
<inertia-link :href="local_route"
:data="{ filter: 'hot' }"
:class="[active_filter == 'hot' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center shadow-classic rounded-md text-white focus:outline-none']"
>
<svg class="-ml-1 mr-2 h-4 w-4 md:h-5 md:w-5 flex-shrink-0" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
>
<path
d="M14.422 6.243c-3.57-2.172-1.895-5.238-1.824-5.365A.586.586 0 0012.09 0c-1.837 0-3.276.522-4.276 1.552-1.71 1.76-1.63 4.498-1.597 5.667.004.13.007.242.007.325 0 .868.14 1.67.263 2.377.079.456.147.85.16 1.159.012.331-.048.407-.05.41-.01.01-.081.049-.276.049a.657.657 0 01-.53-.235c-.565-.634-.604-2.365-.496-3.307a.586.586 0 00-.582-.654c-1.521 0-2.658 2.488-2.658 4.712 0 1.046.21 2.07.625 3.045a8.081 8.081 0 001.7 2.527C5.894 19.157 7.89 20 10 20c2.119 0 4.114-.83 5.618-2.34a7.892 7.892 0 002.327-5.605c0-2.692-2.107-4.95-3.523-5.812zM10 18.828c-3.671 0-6.773-3.101-6.773-6.773 0-.893.213-1.83.585-2.571.086-.173.175-.322.263-.447.01.967.163 2.284.841 3.046.366.41.852.627 1.405.627.518 0 .912-.156 1.172-.466.486-.578.337-1.436.148-2.524-.115-.66-.245-1.409-.245-2.176 0-.1-.003-.22-.007-.359-.032-1.107-.099-3.412 1.265-4.817.613-.63 1.467-1.014 2.55-1.145-.123.406-.228.92-.232 1.497-.01 1.27.476 3.085 2.841 4.524 1.145.696 2.96 2.618 2.96 4.811A6.78 6.78 0 0110 18.828z"
/>
</svg>
Популярные
</inertia-link>
</div>
<div v-if="tag" class="mt-4 grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<feed-tags-buttons :slug="tag.slug" />
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 my-6">
<button class="button-default text-gray text-lg" @click="resetFilter">
Очистить фильтры
</button>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div v-if="feeds.length" class="grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 cards-block rounded-md bg-indigo-200 shadow-classic grid gap-2 lg:gap-4 grid-cards p-2 lg:p-5">
<feed
:next-cursor="nextCursor"
:feeds="feeds"
/>
</div>
<div v-else class="font-bold text-xl lg:text-3xl text-gray text-center">
Записи не найдены
</div>
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import throttle from 'lodash/throttle'
import pickBy from 'lodash/pickBy'
import Feed from '@/Shared/Feed/Feed.vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import FeedTagsButtons from '@/Shared/Misc/FeedTagsButtons.vue'
export default {
components: {
MetaHead,
FeedTagsButtons,
Feed,
},
layout: Layout,
props: {
feeds: Array,
local_route: String,
active_filter: String,
tag: { type: [Object, Boolean], default: false },
searchFilters: Object,
nextCursor: String,
},
data() {
return {
form: {
search: this.searchFilters.search,
},
}
},
watch: {
form: {
deep: true,
handler: throttle(function () {
Inertia.get(this.local_route, pickBy(this.form), {
preserveScroll: true,
preserveState: true,
})
}, 500),
},
},
methods: {
resetFilter() {
Inertia.get(
this.local_route,
{},
{
preserveScroll: true,
}
)
},
},
}
</script>

View File

@@ -0,0 +1,78 @@
<template>
<Head>
<title>{{feed.entity.title}}</title>
<meta name="description" :content="feed.entity.body">
<meta property="og:url" :content="route(`${feed.entity.type}.show`, feed.entity.slug)">
<meta property="og:type" content="website">
<meta property="og:title" :content="feed.entity.title">
<meta property="og:description" :content="feed.entity.body">
<meta property="og:image" :content="feed.entity.preview">
<meta name="twitter:card" content="summary_large_image">
<!-- <meta name="twitter:title" :content="feed.entity.title">
<meta name="twitter:description" :content="feed.entity.body">
<meta name="twitter:image" :content="feed.entity.preview">
<meta name="twitter:card" content="summary_large_image"> -->
</Head>
<header-auth v-if="!$page.props.auth.user" />
<modal-share :entity='feed.entity' ref="shareModalNode" />
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 md:grid grid-cols-12 ">
<modal-feed-media
:type='feed.type'
:feed_id='feed.id'
:title='feed.entity.title'
:preview='feed.entity.preview'
:medias='feed.entity.collection_medias'
class="border-l border-r md:border-r-0 border-t border-b border-indigo-100"
/>
<modal-feed-body
@openShare='showShareModal'
:user='user'
:feed_id='feed.id'
:entity='feed.entity'
class="border-l border-r md:border-t border-b border-indigo-100"
/>
</div>
</template>
<script>
import { Head } from "@inertiajs/inertia-vue3";
import Layout from "@/Shared/Layout.vue";
import ModalFeedMedia from "@/Shared/Overlay/ModalFeedMedia.vue";
import ModalFeedBody from "@/Shared/Overlay/ModalFeedBody.vue";
import ModalShare from "@/Shared/Overlay/ModalShare.vue";
import HeaderAuth from "@/Shared/LayoutParts/HeaderAuth.vue";
export default {
layout: Layout,
components: {
Head,
ModalShare,
ModalFeedMedia,
ModalFeedBody,
HeaderAuth,
},
props: {
feed: Object,
user: Object,
},
provide() {
return {
is_exist_menu: false
}
},
methods: {
showShareModal() {
this.$refs.shareModalNode.openModal();
},
},
};
</script>

View File

@@ -0,0 +1,263 @@
<template>
<meta-head title="Мессенджер"></meta-head>
<div class="flex sm:grid grid-cols-12 h-full overflow-hidden">
<!-- flex-shrink-0 -->
<div ref="userSidebar" class="h-full sm:h-auto transition-transform z-50 absolute transform-gpu -translate-x-full w-64 sm:relative sm:transform-none sm:w-auto col-span-4 bg-indigo-200 border-l border-r border-indigo-300 py-7">
<div class="mb-7 px-2 xl:px-8">
<div class="relative">
<text-input v-model="inputSearchRoom"
class="pr-12 focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light w-full" placeholder="Поиск"
/>
<svg 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" class="text-gray-light absolute right-3 top-2"
><circle cx="11" cy="11"
r="8"
></circle><line x1="21" y1="21"
x2="16.65" y2="16.65"
></line></svg>
</div>
<div class="flex flex-col lg:flex-row lg:space-x-6 mt-2">
<div class="flex items-center">
<input id="room-search-1" v-model="typeSearch"
:value="1" type="radio"
class="text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent cursor-pointer"
>
<label for="room-search-1" class="cursor-pointer select-none ml-2 text-gray-light text-sm">по сообщениям</label>
</div>
<div class="flex items-center">
<input id="room-search-2" v-model="typeSearch"
:value="2" type="radio"
class="text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent cursor-pointer"
>
<label for="room-search-2" class="cursor-pointer select-none ml-2 text-gray-light text-sm">по пользователям</label>
</div>
</div>
</div>
<div class="text-right">
<button class="default mb-2 text-orange text-sm mr-1" @click="openFriendList">
открыть список друзей
</button>
</div>
<div data-simplebar class="messsage-height overflow-auto">
<div class="divide-y divide-indigo-300 border-t border-b border-indigo-300">
<board-room-list v-for="room in userRooms" :key="room.id"
class="pt-5 pb-5"
:class="{ 'bg-indigo-300': room.classActive }"
:room="room"
@select-room="changeRoom"
/>
</div>
</div>
</div>
<div class="flex-1 w-full sm:flex-none sm:w-auto col-span-8 bg-indigo-200 overflow-hidden relative">
<button class="default text-center left-2 top-3 absolute z-40 flex sm:hidden items-center text-white text-xs" @click="openSidebar">
<svg 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" class="feather feather-menu"
><line x1="3" y1="12"
x2="21" y2="12"
></line><line x1="3" y1="6"
x2="21" y2="6"
></line><line x1="3" y1="18"
x2="21" y2="18"
></line></svg>
<span class="ml-1">открыть список пользователей</span>
</button>
<board-message :room-id="selectRoom" :user="selectUser"
:frozen="selectFrozenRoom" :search="searchOnlyMessage"
@disable="interactDisable" @last-message="updateRoom"
@change-room="touchRoom" @leave-room="leaveRoom"
/>
</div>
</div>
</template>
<script>
import { ref, toRef, watch } from 'vue'
import { onClickOutside } from '@vueuse/core'
import { usePage } from '@inertiajs/inertia-vue3'
import { Inertia } from '@inertiajs/inertia'
import debounce from 'lodash/debounce'
import pickBy from 'lodash/pickBy'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import BoardRoomList from '@/Shared/Messanger/BoardRoomList.vue'
import BoardMessage from '@/Shared/Messanger/BoardMessage.vue'
export default {
components: {
MetaHead,
TextInput,
BoardRoomList,
BoardMessage,
},
layout: Layout,
props: {
rooms: {
type: Array,
default: () => []
},
roomSearch: {
type: String,
default: ''
}
},
setup(props){
var inputSearchRoom = ref(props.roomSearch)
const typeSearch = ref(1)
const searchOnlyMessage = ref('')
var userRooms = toRef(props, 'rooms')
var selectUser = ref(null)
var selectRoom = ref(0)
var selectFrozenRoom = ref(false)
var userSidebar = ref(null)
var canChangeRoom = ref(true)
watch(inputSearchRoom, debounce((search) => {
selectUser.value = null
selectRoom.value = 0
if(typeSearch.value === 1){
searchOnlyMessage.value = search
}
Inertia.get(route('messenger.index'), pickBy({ search: search, type: search ? typeSearch.value : null }), { preserveState: true })
}, 500))
watch(typeSearch, () => {
inputSearchRoom.value = ''
})
const checkSelectedActiveUser = () => {
const activeRoom = userRooms.value.find(userRoom => userRoom.classActive === true)
if(activeRoom){
selectUser.value = activeRoom.correspond
selectRoom.value = activeRoom.id
selectFrozenRoom.value = activeRoom.is_freeze
}
}
checkSelectedActiveUser()
function openFriendList() {
selectUser.value = null
selectRoom.value = 0
userRooms.value.forEach(item => item.classActive = false)
}
const touchRoom = (id) => {
const activeRoom = userRooms.value.find(userRoom => userRoom.id === id)
if(activeRoom){
activeRoom.classActive = true
selectUser.value = activeRoom.correspond
selectRoom.value = activeRoom.id
selectFrozenRoom.value = activeRoom.is_freeze
}
}
const leaveRoom = () => {
selectRoom.value = 0
selectUser.value = null
selectFrozenRoom.value = false
}
const changeRoom = (room) => {
closeSidebar()
if(canChangeRoom.value){
selectRoom.value = room.id
selectUser.value = room.correspond
selectFrozenRoom.value = room.is_freeze
const lastActiveRoom = userRooms.value.find(userRoom => userRoom.classActive === true)
if(lastActiveRoom){
lastActiveRoom.classActive = false
}
const nowActiveRoom = userRooms.value.find(userRoom => userRoom.id === room.id)
nowActiveRoom.classActive = true
if(!nowActiveRoom.is_my_message && !nowActiveRoom.is_reading){
nowActiveRoom.is_reading = true
usePage().props.value.message_reading_count--
}
}
}
let openSidebar = () => {
if (window.matchMedia('(max-width: 640px)').matches) {
userSidebar.value.classList.remove('-translate-x-full')
userSidebar.value.classList.add('-translate-x-0')
userSidebar.value.classList.add('shadow-classic')
}
}
let closeSidebar = () => {
if (window.matchMedia('(max-width: 640px)').matches) {
userSidebar.value.classList.add('-translate-x-full')
userSidebar.value.classList.remove('-translate-x-0')
userSidebar.value.classList.remove('shadow-classic')
}
}
let interactDisable = (type) => {
if(type === true){
canChangeRoom.value = false
}else{
canChangeRoom.value = true
}
}
let updateRoom = (message) => {
const upRoom = userRooms.value.find(userRoom => userRoom.id === message.chat_room_id)
upRoom.message = message.message
upRoom.is_my_message = true
upRoom.is_reading = false
upRoom.updated_at = message.updated_at_room
upRoom.updated_at_human = message.updated_at_room_human
userRooms.value.sort(function(a, b) {
if (a.updated_at > b.updated_at) {
return -1
}
if (a.updated_at < b.updated_at) {
return 1
}
return 0
})
}
if (window.matchMedia('(max-width: 640px)').matches) {
onClickOutside(userSidebar, () => {
closeSidebar()
})
}
return {
changeRoom,
selectUser,
userRooms,
selectRoom,
openSidebar,
userSidebar,
closeSidebar,
interactDisable,
updateRoom,
inputSearchRoom,
typeSearch,
searchOnlyMessage,
selectFrozenRoom,
touchRoom,
leaveRoom,
openFriendList,
}
},
}
</script>

View File

@@ -0,0 +1,243 @@
<template>
<meta-head title="Загрузить музыку"></meta-head>
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<form class=" bg-indigo-200 shadow-classic rounded-md p-5" @submit.prevent="submit">
<div class="mb-4 flex items-center text-gray-light text-lg font-medium">
<link-back class="default block hover:underline">
Вернуться
</link-back>
<span class="px-3">/</span>
<h1 class="text-gray">
Загрузка музыки
</h1>
</div>
<div class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.title" :error="form.errors.title"
type="text" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Название"
/>
<warning-text />
</div>
<div class="flex flex-col">
<textarea-input v-model="form.body" :error="form.errors.body"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md" cols="30"
rows="4" label="Описание"
/>
</div>
<div class="flex flex-col">
<file-input
v-model="form.preview"
accept="image/png, image/jpeg, image/jpg"
:error="form.errors.preview"
label="Загрузить превью"
/>
</div>
<div class="flex flex-col">
<file-input-multiple-decode
v-model="form.musics"
accept=".mp3"
:error="form.errors.musics"
label="Выбрать музыку"
@fileTime="saveTimeFile"
@loadFileStart="incrementLoadFile"
/>
</div>
<div class="text-gray-light">
<TagInput v-model="form.tags" />
</div>
<div>
<div class="text-gray-light text-lg mb-2">
Тип контента
</div>
<div class="flex flex-wrap gap-4">
<div class="flex items-center">
<input id="feed-paid-1" v-model="form.is_paid"
value="0" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-1" class="select-none ml-3 text-gray">Бесплатный</label>
</div>
<div class="flex items-center">
<input id="feed-paid-2" v-model="form.is_paid"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-2" class="select-none ml-3 text-gray">Эксклюзивный</label>
</div>
<div v-if="authUser.private" class="flex items-center">
<input id="feed-paid-3" v-model="form.is_paid"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-3" class="select-none ml-3 text-gray">Доступен по личной подписке</label>
</div>
</div>
</div>
<!-- <div class="flex items-center">
<input id="is_adult_feed" v-model="form.is_adult"
type="checkbox"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="is_adult_feed" class="select-none ml-3 text-gray">Контент для взрослых</label>
</div> -->
<div v-if="form.is_paid == 1" class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.price" :error="form.errors.price"
type="number" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Цена"
/>
</div>
<div class="flex flex-col">
<file-input-multiple-decode
v-model="form.musics_paid"
accept=".mp3"
:error="form.errors.musics_paid"
label="Выбрать музыку"
@fileTime="savePaidTimeFile"
@loadFileStart="incrementLoadFile"
/>
</div>
</div>
</div>
<div class="mt-12 flex flex-wrap -my-1 -mx-3">
<progress
v-if="form.progress"
class="mx-3 my-1 w-full"
:value="form.progress.percentage"
max="100"
>
{{ form.progress.percentage }}%
</progress>
<div v-if="!is_disabled_create">
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Создать
</loading-button>
<link-back class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none">
Отменить
</link-back>
</div>
<div v-else>
<button type="button" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none ease-in-out duration-150 cursor-not-allowed"
disabled
>
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24"
><circle class="opacity-25" cx="12"
cy="12" r="10"
stroke="currentColor" stroke-width="4"
></circle><path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path></svg>
Обработка
</button>
</div>
</div>
</form>
</div>
</template>
<script>
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import { ref, computed } from 'vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import FileInputMultipleDecode from '@/Shared/Form/FileInputMultipleDecode.vue'
import FileInput from '@/Shared/Form/FileInput.vue'
import TextareaInput from '@/Shared/Form/TextareaInput.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import TagInput from '@/Shared/Form/TagInput.vue'
import WarningText from '@/Shared/Misc/WarningText.vue'
import LinkBack from '@/Shared/Misc/LinkBack.vue'
export default {
components: {
MetaHead,
TextInput,
FileInput,
FileInputMultipleDecode,
LoadingButton,
TextareaInput,
TagInput,
WarningText,
LinkBack,
},
layout: Layout,
setup() {
let is_disabled_create = ref(0)
let countFiles = ref(0)
let countFilesLoaded = ref(0)
// let isFeedPaid = ref(0)
// let openPaidBlock = ref(0)
const form = useForm({
title: null,
body: null,
preview: null,
musics: null,
musics_paid: null,
price: null,
is_paid: 0,
times: [],
times_paid: [],
tags: [],
})
// watch(isFeedPaid, (value) => {
// openPaidBlock.value = value
// form.is_paid = value
// })
const submit = () => {
form.post(route('musics.store'))
}
const saveTimeFile = (time) => {
form.times.push(time)
countFilesLoaded.value++
if (countFilesLoaded.value === countFiles.value) {
is_disabled_create.value = 0
}
}
const savePaidTimeFile = (time) => {
form.times_paid.push(time)
countFilesLoaded.value++
if (countFilesLoaded.value === countFiles.value) {
is_disabled_create.value = 0
}
}
const incrementLoadFile = () => {
countFiles.value++
is_disabled_create.value = 1
}
const authUser = computed(() => usePage().props.value.auth.user)
return {
form,
submit,
saveTimeFile,
savePaidTimeFile,
incrementLoadFile,
is_disabled_create,
authUser,
// isFeedPaid,
// openPaidBlock,
}
},
}
</script>

416
resources/js/Pages/Music/Edit.vue Executable file
View File

@@ -0,0 +1,416 @@
<template>
<meta-head title="Обновить музыку"></meta-head>
<modal-warning
:feed_id="feed.id"
:open="modalShow"
@action="deleteActionModal"
/>
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<div v-if="feed.status != 1" class="bg-indigo-200 shadow-classic rounded-md p-5 mb-10">
<p class="text-lg font-medium text-gray">
Контент запрещен к публикации, причина:
</p>
<p v-if="feed.status_note " class="text-gray-light">
<ul>
<li v-for="textBreak in textsBreak" :key="textBreak">
{{ textBreak }}
</li>
</ul>
</p>
<p v-else class="text-gray-light">
модерация созданного\обновленного контента
</p>
<p v-if="feed.status == 3" class="pt-3 text-green">
Обновления отправлены, ожидайте!
</p>
</div>
<form class="bg-indigo-200 shadow-classic rounded-md p-5" @submit.prevent="submit">
<div class="mb-4 flex items-center text-gray-light text-lg font-medium">
<link-back class="default block hover:underline">
Вернуться
</link-back>
<span class="px-3">/</span>
<h1 class="text-gray">
Редактирование музыки
</h1>
</div>
<div class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.title" :error="form.errors.title"
type="text" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Название"
/>
<warning-text />
</div>
<div class="flex flex-col">
<textarea-input v-model="form.body" :error="form.errors.body"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md" cols="30"
rows="4" label="Описание"
/>
</div>
<div class="flex flex-col">
<file-input
v-model="form.preview"
accept="image/png, image/jpeg, image/jpg"
:error="form.errors.preview"
:label="textPreview()"
/>
</div>
<div v-if="mediaPreview" class="border border-indigo-300 p-4 rounded-md">
<button type="button"
class="flex-shrink-0 px-6 py-2 bg-pink focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:outline-none focus:border-transparent rounded-sm text-sm text-white" @click="removePreview(mediaPreview.id)"
>
Удалить старое превью
</button>
<div class="mt-4 flex-shrink-0 self-start overflow-hidden">
<img class="object-contain h-48" :src="mediaPreview.url"
alt=""
>
</div>
</div>
<div class="border border-indigo-300 flex flex-col p-4 rounded-md">
<file-input-multiple-decode
v-model="form.musics"
accept=".mp3"
:error="form.errors.musics"
label="Загрузить новую музыку"
@fileTime="saveTimeFile"
@loadFileStart="incrementLoadFile"
/>
<div>
<h2 class="mt-3 mb-2 text-lg text-gray-light select-none">
Загруженная музыка:
</h2>
<div class="grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
<created-media-item
v-for="mediaCommon in mediasCommon"
:key="mediaCommon.id" :type="feed.type"
:media="mediaCommon"
@addRemoveId="stateToggle"
/>
</div>
</div>
</div>
<div class="text-gray-light">
<TagInput v-model="form.tags" />
</div>
<div v-if="isFreeFeed">
<div class="text-gray-light text-lg mb-2">
Тип контента
</div>
<div class="flex flex-wrap gap-4">
<div class="flex items-center">
<input id="feed-paid-1" v-model="form.is_paid"
value="0" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-1" class="select-none ml-3 text-gray">Бесплатный</label>
</div>
<div class="flex items-center">
<input id="feed-paid-2" v-model="form.is_paid"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-2" class="select-none ml-3 text-gray">Эксклюзивный</label>
</div>
<div v-if="authUser.private" class="flex items-center">
<input id="feed-paid-3" v-model="form.is_paid"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-3" class="select-none ml-3 text-gray">Доступен по личной подписке</label>
</div>
</div>
</div>
<!-- <div class="flex items-center">
<input id="is_adult_feed" v-model="form.is_adult"
type="checkbox"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="is_adult_feed" class="select-none ml-3 text-gray">Контент для взрослых</label>
</div> -->
<div v-if="form.is_paid == 1" class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.price" :error="form.errors.price"
type="number" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Цена"
/>
</div>
<div class="border border-indigo-300 flex flex-col p-4 rounded-md space-y-3">
<div v-if="isFreeFeed">
<file-input-multiple-decode
v-model="form.musics_paid"
accept=".mp3"
:error="form.errors.musics_paid"
label="Выбрать музыку"
@fileTime="savePaidTimeFile"
@loadFileStart="incrementLoadFile"
/>
</div>
<div v-if="mediasPaid.length">
<h2 class="mb-2 text-lg text-gray-light select-none">
Загруженная платная музыка:
</h2>
<div class="grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
<created-media-item
v-for="mediaPaid in mediasPaid" :key="mediaPaid.id"
:edit="false"
:type="feed.type"
:media="mediaPaid"
/>
</div>
</div>
</div>
</div>
</div>
<div class="mt-12 flex flex-wrap -my-1 -mx-3">
<progress
v-if="form.progress"
class="mx-3 my-1 w-full"
:value="form.progress.percentage"
max="100"
>
{{ form.progress.percentage }}%
</progress>
<div v-if="!is_disabled_create">
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Обновить
</loading-button>
<link-back class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none">
Отменить
</link-back>
<button type="button" class="mx-3 my-1 default text-lg text-pink"
@click="openModal"
>
Удалить
</button>
</div>
<div v-else>
<button type="button" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none ease-in-out duration-150 cursor-not-allowed"
disabled
>
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24"
><circle class="opacity-25" cx="12"
cy="12" r="10"
stroke="currentColor" stroke-width="4"
></circle><path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path></svg>
Обработка
</button>
</div>
</div>
</form>
</div>
</template>
<script>
import { toRefs, watch, ref, computed } from 'vue'
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import { Inertia } from '@inertiajs/inertia'
import { useStore } from 'vuex'
import find from 'lodash/find'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import FileInput from '@/Shared/Form/FileInput.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import FileInputMultipleDecode from '@/Shared/Form/FileInputMultipleDecode.vue'
import TextareaInput from '@/Shared/Form/TextareaInput.vue'
import TagInput from '@/Shared/Form/TagInput.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import LinkBack from '@/Shared/Misc/LinkBack.vue'
import WarningText from '@/Shared/Misc/WarningText.vue'
import CreatedMediaItem from '@/Shared/Edit/CreatedMediaItem.vue'
import ModalWarning from '@/Shared/Overlay/ModalWarning.vue'
export default {
components: {
MetaHead,
CreatedMediaItem,
TextInput,
FileInput,
FileInputMultipleDecode,
LoadingButton,
WarningText,
TextareaInput,
LinkBack,
TagInput,
ModalWarning,
},
layout: Layout,
props: {
feed: Object,
tags: Array,
mediasCommon: Array,
mediasPaid: Array,
mediasCount: Number,
mediaPreview: Object,
},
setup(props) {
const store = useStore()
const { feed, tags, mediasCount, mediaPreview } = toRefs(props)
const textsBreak = computed(() => feed.value.status_note.split('\n'))
const modalShow = ref(false)
let is_disabled_create = ref(0)
let countFiles = ref(0)
let countFilesLoaded = ref(0)
// let isFeedPaid, openPaidBlock
let form_fields = {
id: feed.value.id,
title: feed.value.title,
body: feed.value.body,
price: feed.value.price,
tags: tags.value,
is_paid: feed.value.is_paid,
preview: null,
musics: null,
musics_paid: null,
times: [],
times_paid: [],
removedItems: [],
totalItems: mediasCount,
}
if (feed.value.is_paid) {
// isFeedPaid = ref(1)
// openPaidBlock = ref(1)
delete form_fields.musics_paid
}
// else {
// isFeedPaid = ref(0)
// openPaidBlock = ref(0)
// }
const form = useForm(form_fields)
// watch(isFeedPaid, (value) => {
// openPaidBlock.value = value
// form.is_paid = value
// })
const submit = () => {
form.post(route('musics.update', feed.value.id), {
onSuccess: () => {
form.reset('musics')
form.reset('musics_paid')
form.reset('preview')
},
})
}
const stateToggle = (id) => {
let existID = find(form.removedItems, (itemID) => itemID === id)
if (existID) {
const index = form.removedItems.indexOf(existID)
if (index > -1) {
form.removedItems.splice(index, 1)
}
} else {
form.removedItems.push(id)
}
}
const resetPlaylist = () => {
store.dispatch('defaultPlaylistReset')
}
const deleteActionModal = (remove) => {
if (remove) {
resetPlaylist()
Inertia.delete(route('feed.destroy', feed.value.id), {
preserveScroll: true,
preserveState: true,
})
}
modalShow.value = false
}
const openModal = () => (modalShow.value = true)
const authUser = computed(() => usePage().props.value.auth.user)
const isFreeFeed = computed(() => feed.value.is_paid === 0)
const textPreview = () => {
if (mediaPreview.value) {
return 'Обновить превью'
}
return 'Загрузить превью'
}
const saveTimeFile = (time) => {
form.times.push(time)
countFilesLoaded.value++
if (countFilesLoaded.value === countFiles.value) {
is_disabled_create.value = 0
}
}
const savePaidTimeFile = (time) => {
form.times_paid.push(time)
countFilesLoaded.value++
if (countFilesLoaded.value === countFiles.value) {
is_disabled_create.value = 0
}
}
const incrementLoadFile = () => {
countFiles.value++
is_disabled_create.value = 1
}
const removePreview = (id) => {
Inertia.delete(route('feed.preview.destroy', id))
}
return {
form,
submit,
saveTimeFile,
savePaidTimeFile,
incrementLoadFile,
stateToggle,
authUser,
// isFeedPaid,
// openPaidBlock,
isFreeFeed,
modalShow,
deleteActionModal,
openModal,
textPreview,
is_disabled_create,
removePreview,
textsBreak
}
},
updated() {
if (this.feed.is_paid) {
delete this.form.musics_paid
}
},
}
</script>

139
resources/js/Pages/Music/Feed.vue Executable file
View File

@@ -0,0 +1,139 @@
<template>
<meta-head title="Музыка"></meta-head>
<div class="mb-6 py-3 banner relative bg-center bg-no-repeat bg-cover"
style="background-image: url('/image/bg-home.jpg');">
<div class="h-52 flex justify-center items-center text-center">
<div class="max-w-4xl text-gray px-3">
<h1
class=" text-xl md:text-2xl lg:text-3xl xl:text-5xl text-white font-semibold xl:leading-relaxed">
Музыка</h1>
<h2 v-if="tag" class="text-lg md:text-3xl text-gray-light">
#{{tag.name}}
</h2>
<inertia-link :href="route('musics.create')"
class="mt-8 inline-flex tracking-wide items-center px-8 md:px-12 py-3 border border-white text-sm lg:text-lg text-white rounded-full bg-transparent hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2">
Загрузить
</inertia-link>
</div>
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 buttons-filter-line">
<div class=" relative mb-5">
<div class="absolute inset-y-0 left-3 flex items-center z-[1]">
<svg class="flex-shrink-0 h-5 w-5 text-gray-light" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" fill="currentColor">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11 4a7 7 0 100 14 7 7 0 000-14zm-9 7a9 9 0 1118 0 9 9 0 01-18 0z" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M15.943 15.943a1 1 0 011.414 0l4.35 4.35a1 1 0 01-1.414 1.414l-4.35-4.35a1 1 0 010-1.414z" />
</svg>
</div>
<input
v-model="form.search"
class="relative w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light !pl-10 h-14" placeholder="Поиск"
type="search">
</div>
<div class="grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<inertia-link :href="local_route"
:class="[active_filter == 'new' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center rounded-md text-white focus:outline-none']">
Новинки
</inertia-link >
<inertia-link :href="local_route"
:data="{ filter: 'hot' }"
:class="[active_filter == 'hot' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center shadow-classic rounded-md text-white focus:outline-none']">
<svg class="-ml-1 mr-2 h-4 w-4 md:h-5 md:w-5 flex-shrink-0" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path
d="M14.422 6.243c-3.57-2.172-1.895-5.238-1.824-5.365A.586.586 0 0012.09 0c-1.837 0-3.276.522-4.276 1.552-1.71 1.76-1.63 4.498-1.597 5.667.004.13.007.242.007.325 0 .868.14 1.67.263 2.377.079.456.147.85.16 1.159.012.331-.048.407-.05.41-.01.01-.081.049-.276.049a.657.657 0 01-.53-.235c-.565-.634-.604-2.365-.496-3.307a.586.586 0 00-.582-.654c-1.521 0-2.658 2.488-2.658 4.712 0 1.046.21 2.07.625 3.045a8.081 8.081 0 001.7 2.527C5.894 19.157 7.89 20 10 20c2.119 0 4.114-.83 5.618-2.34a7.892 7.892 0 002.327-5.605c0-2.692-2.107-4.95-3.523-5.812zM10 18.828c-3.671 0-6.773-3.101-6.773-6.773 0-.893.213-1.83.585-2.571.086-.173.175-.322.263-.447.01.967.163 2.284.841 3.046.366.41.852.627 1.405.627.518 0 .912-.156 1.172-.466.486-.578.337-1.436.148-2.524-.115-.66-.245-1.409-.245-2.176 0-.1-.003-.22-.007-.359-.032-1.107-.099-3.412 1.265-4.817.613-.63 1.467-1.014 2.55-1.145-.123.406-.228.92-.232 1.497-.01 1.27.476 3.085 2.841 4.524 1.145.696 2.96 2.618 2.96 4.811A6.78 6.78 0 0110 18.828z" />
</svg>
Популярные
</inertia-link >
</div>
<div v-if="tag" class="mt-4 grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<feed-tags-buttons :slug='tag.slug' />
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 my-6">
<button @click="resetFilter" class="button-default text-gray text-lg">Очистить фильтры</button>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div v-if="feeds.length" class="grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 cards-block rounded-md bg-indigo-200 shadow-classic grid gap-2 lg:gap-4 grid-cards p-2 lg:p-5">
<feed
:nextCursor="nextCursor"
:feeds="feeds"
/>
</div>
<div v-else class="font-bold text-xl lg:text-3xl text-gray text-center">Записи не найдены</div>
</div>
</template>
<script>
import { Inertia } from "@inertiajs/inertia";
import throttle from "lodash/throttle";
import pickBy from "lodash/pickBy";
import Feed from "@/Shared/Feed/Feed.vue";
import Layout from "@/Shared/Layout.vue";
import MetaHead from "@/Shared/MetaHead.vue";
import FeedTagsButtons from "@/Shared/Misc/FeedTagsButtons.vue";
export default {
layout: Layout,
components: {
MetaHead,
FeedTagsButtons,
Feed,
},
props: {
feeds: Array,
local_route: String,
active_filter: String,
tag: { type: [Object, Boolean], default: false },
searchFilters: Object,
nextCursor: String,
},
data() {
return {
form: {
search: this.searchFilters.search,
},
};
},
watch: {
form: {
deep: true,
handler: throttle(function () {
Inertia.get(this.local_route, pickBy(this.form), {
preserveScroll: true, preserveState: true
});
}, 500),
},
},
methods: {
resetFilter() {
Inertia.get(
this.local_route,
{},
{
preserveScroll: true,
}
);
},
},
};
</script>

View File

@@ -0,0 +1,78 @@
<template>
<Head>
<title>{{feed.entity.title}}</title>
<meta name="description" :content="feed.entity.body">
<meta property="og:url" :content="route(`${feed.entity.type}.show`, feed.entity.slug)">
<meta property="og:type" content="website">
<meta property="og:title" :content="feed.entity.title">
<meta property="og:description" :content="feed.entity.body">
<meta property="og:image" :content="feed.entity.preview">
<meta name="twitter:card" content="summary_large_image">
<!-- <meta name="twitter:title" :content="feed.entity.title">
<meta name="twitter:description" :content="feed.entity.body">
<meta name="twitter:image" :content="feed.entity.preview">
<meta name="twitter:card" content="summary_large_image"> -->
</Head>
<header-auth v-if="!$page.props.auth.user" />
<modal-share :entity='feed.entity' ref="shareModalNode" />
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 md:grid grid-cols-12 ">
<modal-feed-media
:type='feed.type'
:feed_id='feed.id'
:title='feed.entity.title'
:preview='feed.entity.preview'
:medias='feed.entity.collection_medias'
class="border-l border-r md:border-r-0 border-t border-b border-indigo-100"
/>
<modal-feed-body
@openShare='showShareModal'
:user='user'
:feed_id='feed.id'
:entity='feed.entity'
class="border-r md:border-t border-b border-indigo-100"
/>
</div>
</template>
<script>
import { Head } from "@inertiajs/inertia-vue3";
import Layout from "@/Shared/Layout.vue";
import HeaderAuth from "@/Shared/LayoutParts/HeaderAuth.vue";
import ModalFeedMedia from "@/Shared/Overlay/ModalFeedMedia.vue";
import ModalFeedBody from "@/Shared/Overlay/ModalFeedBody.vue";
import ModalShare from "@/Shared/Overlay/ModalShare.vue";
export default {
layout: Layout,
components: {
Head,
ModalShare,
ModalFeedMedia,
ModalFeedBody,
HeaderAuth
},
props: {
feed: Object,
user: Object,
},
provide() {
return {
is_exist_menu: false
}
},
methods: {
showShareModal() {
this.$refs.shareModalNode.openModal();
},
},
};
</script>

View File

@@ -0,0 +1,116 @@
<template>
<meta-head title="Профиль юзера"></meta-head>
<profile-header :is_leader="is_leader" :user="user"
:counts="counts" :limit-leader="limitLeader"
:auth-user-active-subscription="authUserActiveSubscription"
:package-completed="packageCompleted"
/>
<profile-menu :user="user" />
<div v-if="packet?.id && !packageCompleted && !user.is_auth_user && user.private" class="mt-12 xl:container xl:mx-auto ">
<div class="p-5 text-lg text-white flex items-center gap-10 bg-indigo-200 mx-3">
<div>
<h2>Купить подписку на пользователя</h2>
<div>Цена: <span>{{ packet.price }}</span></div>
</div>
<div>
<button type="button"
class="p-5 text-lg bg-orange rounded-lg twext-white"
@click="createSubsPacket"
>
Оплатить
</button>
</div>
</div>
</div>
<!-- <div v-if="testPeriod" class="mt-12 xl:container xl:mx-auto px-2 md:px-3 ">
<div class="bg-pink p-10 text-center text-white text-xl rounded-xl">
<p>Вам предоставлен тестовый доступ в течении часа!</p>
<p>
после ознакомления, Вам нужно будет оплатить подписку, <inertia-link class="underline" :href="route('setting.tarif')">
по данной ссылке
</inertia-link> : (или перейти в настройки -> тарифы)
</p>
</div>
</div> -->
<div v-if="feeds.length" class="mt-12 xl:container xl:mx-auto px-2 md:px-3">
<div class="grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 cards-block rounded-md bg-indigo-200 shadow-classic grid gap-2 lg:gap-4 grid-cards p-2 lg:p-5">
<feed
:feeds="feeds"
:next-cursor="nextCursor"
/>
</div>
</div>
<div v-else class="mt-12 xl:container xl:mx-auto px-2 md:px-3 ">
<div class="font-bold text-xl lg:text-3xl text-gray text-center">
Записи не найдены
</div>
</div>
</template>
<script>
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import ProfileHeader from '@/Shared/Partials/ProfileHeader.vue'
import ProfileMenu from '@/Shared/Partials/ProfileMenu.vue'
import Feed from '@/Shared/Feed/Feed.vue'
import { Inertia } from '@inertiajs/inertia'
export default {
components: {
Feed,
MetaHead,
ProfileHeader,
ProfileMenu,
},
layout: Layout,
props: {
user: {
type: Object,
default: () => {}
},
packet: {
type: Object,
default: () => {}
},
feeds: {
type: Array,
default: () => {}
},
counts: {
type: Object,
default: () => {}
},
nextCursor: {
type: String,
default: ''
},
limitLeader: {
type: Boolean,
default: false
},
packageCompleted: {
type: Boolean,
default: false
},
authUserActiveSubscription: Boolean,
close_account: Boolean,
is_leader: Boolean,
},
methods: {
createSubsPacket()
{
// asdasd
Inertia.post(
route('user.package.subs'),
{ packet_id: this.packet.id },
// { preserveScroll: true, preserveState: true }
)
}
}
}
</script>

View File

@@ -0,0 +1,232 @@
<template>
<meta-head title="Читаемые пользователи"></meta-head>
<profile-header :is_leader="is_leader" :user="user"
:counts="counts" :limit-leader="limitLeader"
:auth-user-active-subscription="authUserActiveSubscription"
:package-completed="packageCompleted"
/>
<profile-menu :user="user" />
<div v-if="user.is_auth_user || (user.private === false || packageCompleted)" class="mt-12 xl:container xl:mx-auto px-2 md:px-3">
<div class="cards-block rounded-md bg-indigo-200 shadow-classic p-2 lg:p-5">
<div class="">
<search-filter v-model="form.search" class="w-full max-w-3xl mr-4"
@reset="reset"
>
<div class="flex flex-col space-y-6">
<div class="flex items-center">
<input id="user-leader-1" v-model="form.leader"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-leader-1" class="select-none ml-3 text-gray text-xs md:text-base">По лидерам</label>
</div>
<div class="flex items-center">
<input id="user-leader-2" v-model="form.leader"
value="0" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-leader-2" class="select-none ml-3 text-gray text-xs md:text-base">Не по лидерам</label>
</div>
</div>
</search-filter>
</div>
<div v-show="readers.length" class="mt-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 grid gap-2 lg:gap-4 grid-cards">
<InfinityScroll :node-element="lastNodeLement" :next-cursor="nextCursor"
@fromPagination="putFromPagination"
>
<div v-for="reader in userLists" :key="reader.id"
:ref="el => { if (el && reader.id === lastElementID) lastNodeLement = el }" class="group mb-5 user-card relative"
>
<div v-if="user.is_auth_user" class="absolute inset-x-0 top-4 z-10 flex justify-center">
<toggle
v-if="authUserActiveSubscription"
:user_id="reader.id"
:enabled="reader.is_leader"
:disabled="limitLeader === true && reader.is_leader === false ? true : false"
textin="Сделать лидером"
textout="Убрать лидера" @clicked="leader"
/>
</div>
<div class="absolute inset-x-0 bottom-4 z-10 flex justify-center">
<div class="flex flex-col items-center">
<inertia-link :href="route('profile.user', reader.username)" class="block flex-shrink-0">
<user-avatar :user="reader" size="small"
class="border border-white shadow-classic h-20 w-20 text-lg"
/>
</inertia-link>
<inertia-link :href="route('profile.user', reader.username)" class="mt-2 block text-white text-sm text-center">
<p>{{ reader.name }}</p>
<p class="text-xs">
{{ reader.username }}
</p>
</inertia-link>
</div>
</div>
<div class="gradient-profile relative overflow-hidden">
<user-banner class="w-full h-72 bg-indigo-300" :user="reader"
size="banner"
/>
</div>
<!-- myPaidListSubscribeTo[reader.id] -->
<div v-if="user.is_auth_user && (reader.private === false ? true : false) " class="transition-opacity sm:opacity-0 group-hover:opacity-100 absolute w-full text-center">
<button class="leading-none focus:outline-none hover:underline text-sm text-orange-dark" @click="susbscribe(reader.id)">
Отписаться
</button>
</div>
</div>
</InfinityScroll>
</div>
</div>
<div v-show="readers.length == 0">
<p class="mt-4 text-center md:text-2xl text-gray-light">
Пользователи не найдены
</p>
</div>
</div>
<div v-else class="mt-12 xl:container xl:mx-auto px-2 md:px-3 text-gray-light text-lg">
Аккаунт закрыт
</div>
</template>
<script>
import { ref } from 'vue'
import { Inertia } from '@inertiajs/inertia'
import filter from 'lodash/filter'
import pickBy from 'lodash/pickBy'
import throttle from 'lodash/throttle'
import mapValues from 'lodash/mapValues'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import ProfileHeader from '@/Shared/Partials/ProfileHeader.vue'
import ProfileMenu from '@/Shared/Partials/ProfileMenu.vue'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
import UserBanner from '@/Shared/Misc/UserBanner.vue'
import Toggle from '@/Shared/Form/Toggle.vue'
import SearchFilter from '@/Shared/Form/SearchFilter.vue'
import InfinityScroll from '@/Shared/Misc/InfinityScroll.vue'
export default {
components: {
MetaHead,
Toggle,
UserAvatar,
UserBanner,
ProfileHeader,
ProfileMenu,
SearchFilter,
InfinityScroll,
},
layout: Layout,
props: {
nextCursor: String,
user: Object,
myPaidListSubscribeTo: Object,
readers: Array,
counts: Object,
filters: Object,
is_leader: Boolean,
authUserActiveSubscription: Boolean,
close_account: Boolean,
limitLeader: {
type: Boolean,
default: false
},
packageCompleted: {
type: Boolean,
default: false
},
},
setup() {
const containerRef = ref(null)
return {
lastNodeLement: containerRef,
}
},
data() {
return {
userLists: [],
form: {
search: this.filters.search,
leader: this.filters.leader,
},
}
},
computed: {
lastElementID() {
return this.userLists[this.userLists.length - 1]?.id
},
// limitChoiceLeader() {
// return this.userLists.filter(item => item.is_leader === true).length === 2
// },
},
mounted() {
this.updateRequest()
},
watch: {
readers() {
this.updateRequest()
},
form: {
deep: true,
handler: throttle(function () {
const url = route('profile.readers', this.user.username)
Inertia.get(url, pickBy(this.form), {
preserveState: true,
preserveScroll: true,
})
}, 300),
},
},
methods: {
putFromPagination(lists) {
for (let list of lists) {
this.userLists.push(list)
}
},
updateRequest() {
this.userLists = this.readers
},
reset() {
this.form = mapValues(this.form, () => null)
},
susbscribe(user_id) {
Inertia.post(
route('users.subs', user_id),
{},
{ preserveScroll: true, preserveState: true }
)
},
leader(user_id) {
let current_user = filter(this.readers, function (x) {
return x.id === user_id
})
if (current_user) {
current_user = current_user[0]
Inertia.post(
route('users.leader', user_id),
{ vote: current_user.is_leader },
{ preserveScroll: true, preserveState: true }
)
}
},
},
}
</script>

View File

@@ -0,0 +1,204 @@
<template>
<meta-head title="Подписчики"></meta-head>
<profile-header :is_leader="is_leader" :user="user"
:counts="counts" :limit-leader="limitLeader"
:auth-user-active-subscription="authUserActiveSubscription"
:package-completed="packageCompleted"
/>
<profile-menu :user="user" />
<div v-if="user.is_auth_user || (user.private === false || packageCompleted)" class="mt-12 xl:container xl:mx-auto px-2 md:px-3">
<div class="cards-block rounded-md bg-indigo-200 shadow-classic p-2 lg:p-5">
<div class="">
<search-filter v-model="form.search" class="w-full max-w-3xl mr-4"
@reset="reset"
>
<div class="flex flex-col space-y-6">
<div class="flex items-center">
<input id="user-sub-1" v-model="form.sub"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-sub-1" class="select-none ml-3 text-gray text-xs md:text-base">По подписчикам</label>
</div>
<div class="flex items-center">
<input id="user-sub-2" v-model="form.sub"
value="0" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-sub-2" class="select-none ml-3 text-gray text-xs md:text-base">Не по подписчикам</label>
</div>
</div>
</search-filter>
</div>
<div v-show="subscribers.length" class="mt-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 grid gap-2 lg:gap-4 grid-cards">
<InfinityScroll :node-element="lastNodeLement" :next-cursor="nextCursor"
@fromPagination="putFromPagination"
>
<div v-for="subscriber in userLists" :key="subscriber.id"
:ref="el => { if (el && subscriber.id === lastElementID) lastNodeLement = el }" class=" user-card relative"
>
<div v-if="user.is_auth_user && !subscriber.private" class="absolute inset-x-0 top-4 z-10 flex justify-center">
<toggle
:user_id="subscriber.id"
:enabled="subscriber.is_sub"
textin="Подписаться"
textout="Отписаться" @clicked="susbscribe"
/>
</div>
<div class="absolute inset-x-0 bottom-4 z-10 flex justify-center">
<div class="flex flex-col items-center">
<inertia-link :href="route('profile.user', subscriber.username)" class="block flex-shrink-0">
<user-avatar :user="subscriber" size="small"
class="border border-white shadow-classic h-20 w-20 text-lg"
/>
</inertia-link>
<inertia-link :href="route('profile.user', subscriber.username)" class="mt-2 block text-white text-sm text-center">
<p>{{ subscriber.name }}</p>
<p class="text-xs">
{{ subscriber.username }}
</p>
</inertia-link>
</div>
</div>
<div class="gradient-profile relative overflow-hidden">
<user-banner class="w-full h-72 bg-indigo-300" :user="subscriber"
size="banner"
/>
</div>
</div>
</InfinityScroll>
</div>
</div>
<div v-show="subscribers.length == 0">
<p class="mt-4 text-center md:text-2xl text-gray-light">
Пользователи не найдены
</p>
</div>
</div>
<div v-else class="mt-12 xl:container xl:mx-auto px-2 md:px-3 text-gray-light text-lg">
Аккаунт закрыт
</div>
</template>
<script>
import { ref } from 'vue'
import { Inertia } from '@inertiajs/inertia'
import pickBy from 'lodash/pickBy'
import throttle from 'lodash/throttle'
import mapValues from 'lodash/mapValues'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import ProfileHeader from '@/Shared/Partials/ProfileHeader.vue'
import ProfileMenu from '@/Shared/Partials/ProfileMenu.vue'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
import UserBanner from '@/Shared/Misc/UserBanner.vue'
import Toggle from '@/Shared/Form/Toggle.vue'
import SearchFilter from '@/Shared/Form/SearchFilter.vue'
import InfinityScroll from '@/Shared/Misc/InfinityScroll.vue'
export default {
components: {
MetaHead,
Toggle,
UserAvatar,
UserBanner,
ProfileHeader,
ProfileMenu,
SearchFilter,
InfinityScroll,
},
layout: Layout,
props: {
nextCursor: String,
user: Object,
subscribers: Array,
counts: Object,
filters: Object,
is_leader: Boolean,
close_account: Boolean,
authUserActiveSubscription: Boolean,
limitLeader: {
type: Boolean,
default: false
},
packageCompleted: {
type: Boolean,
default: false
},
},
setup() {
const containerRef = ref(null)
return {
lastNodeLement: containerRef,
}
},
data() {
return {
userLists: [],
form: {
search: this.filters.search,
sub: this.filters.sub,
},
}
},
computed: {
lastElementID() {
return this.userLists[this.userLists.length - 1]?.id
},
},
mounted() {
this.updateRequest()
},
watch: {
subscribers() {
this.updateRequest()
},
form: {
deep: true,
handler: throttle(function () {
const url = route('profile.subs', this.user.username)
Inertia.get(url, pickBy(this.form), {
preserveState: true,
preserveScroll: true,
})
}, 300),
},
},
methods: {
putFromPagination(lists) {
for (let list of lists) {
this.userLists.push(list)
}
},
updateRequest() {
this.userLists = this.subscribers
},
reset() {
this.form = mapValues(this.form, () => null)
},
susbscribe(user_id) {
Inertia.post(
route('users.subs', user_id),
{},
{ preserveScroll: true, preserveState: true }
)
},
},
}
</script>

View File

@@ -0,0 +1,79 @@
<template>
<meta-head title="Написать нам"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div class="mx-4 2xl:mx-28 my-8">
<div class="mt-10">
<ul class="flex flex-col text-white gap-3 text-base px-3">
<li>
<a target="_blank" class="hover:underline"
href="/docs/company.pdf"
rel="noopener noreferrer nofollow"
>О компании</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/offer_authors.pdf"
rel="noopener noreferrer nofollow"
>Оферта для авторов</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/offer_paid_subscription.pdf"
rel="noopener noreferrer nofollow"
>Оферта на платную подписку</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/security_policy.pdf"
rel="noopener noreferrer nofollow"
>Политика безопасности</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/privacy_policy.pdf"
rel="noopener noreferrer nofollow"
>Политика конфиденциальности</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/terms_use.pdf"
rel="noopener noreferrer nofollow"
>Пользовательское соглашение</a>
</li>
<li>
<a target="_blank" class="hover:underline"
href="/docs/personal_data.pdf"
rel="noopener noreferrer nofollow"
>Согласие на обработку персональных данных</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
SettingsMenu,
},
layout: Layout,
props: {},
setup() {}
}
</script>

View File

@@ -0,0 +1,40 @@
<template>
<meta-head title="Понравилось"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div v-if="feeds.length" class="m-4 lg:m-8 grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-4 gap-2 lg:gap-4">
<feed :feeds="feeds" :next-cursor="nextCursor" />
</div>
<div v-else class="m-4 lg:m-8 text-lg text-gray-light">
Данных нет
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import Feed from '@/Shared/Feed/Feed.vue'
export default {
components: {
Feed,
MetaHead,
SettingsMenu,
},
layout: Layout,
props: {
feeds: Array,
nextCursor: String,
},
methods: {},
}
</script>

View File

@@ -0,0 +1,196 @@
<template>
<meta-head title="Доход"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div class="border-b border-indigo-300">
<div class="py-5 px-4 2xl:px-28">
<a rel="noopener noreferrer nofollow" target="_blank"
href="https://yoomoney.ru/pay/page?id=526623"
>
<svg class="h-10 mb-5" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1440 320"
><defs></defs><path fill="#fff" d="M1265.12,160.92c0-39.25-29.7-71-71.65-71-41.12,0-70.4,30.53-70.4,72.48,0,42.16,30.11,72.9,73.31,72.9,27.41,0,50.46-12.26,64.79-37.18l-29.07-13.5c-6.85,12.46-21.81,20.56-35.72,20.56-17.65,0-38-13.29-39.25-31.77h106.95A77.88,77.88,0,0,0,1265.12,160.92Zm-107.37-12c2.5-18.69,15.37-30.94,35.93-30.94,20.35,0,32.81,12,34.47,30.94Z" /><path fill="#fff" d="M864.79,89.89c-42.16,0-72.06,30.33-72.06,73.11,0,42.36,29.9,72.27,72.48,72.27,42.78,0,72.68-29.91,72.68-71.65C937.89,120.63,907.16,89.89,864.79,89.89Zm.42,113.6c-21.19,0-34.27-15.78-34.27-40.29,0-24.92,13.29-41.95,34.27-41.95,21.18,0,34.47,16.82,34.47,41.95C899.68,187.92,886.6,203.49,865.21,203.49Z" /><path fill="#fff" d="M714.67,89.93c-17.27,0-30.28,5.9-43.29,20.53h-4.27c-8.74-13.21-23-20.53-39-20.53A50.44,50.44,0,0,0,587,110.46h-4.27l-.21-.2V92.78H546.78V232.22H585.4V160.46c0-23.17,9.76-36.79,26.63-36.79,15.65,0,25.81,11.59,25.81,29.27v79.28h38.82V160.46c0-23,9.76-36.79,26.84-36.79,15.44,0,25.4,11.59,25.4,29.48v79.07h38.83V148.47C767.73,112.09,748.21,89.93,714.67,89.93Z" /><path fill="#fff" d="M1351,183.86h-2.76c0-2.48-16.84-46.64-27.88-74l-6.9-17.11h-38.91l55.2,139.93L1308,286h38.64l79.49-193.2H1387.2l-6.35,14.63-21.53,53.27C1353.25,175.58,1350.76,182.48,1351,183.86Z" /><path fill="#fff" d="M1045,89.93c-17.28,0-32.12,7.52-42.08,20.94h-4.07l-.4-.2V92.78H962.69V232.22h38.62V160.87c0-22.76,11-37,29.07-37,16.87,0,29.27,12.6,29.27,32.52v75.82h38.62V148.07C1098.27,113.92,1075.91,89.93,1045,89.93Z" /><path fill="#8b3ffd" d="M284.49,10C201.05,10,134,77.5,134,160c0,83.18,67.71,150,150.47,150S435,242.5,435,160,367.25,10,284.49,10Zm0,205.91c-30.78,0-56.08-25.23-56.08-55.91s25.3-55.91,56.08-55.91,56.09,25.23,56.09,55.91C339.89,190.68,315.27,215.91,284.49,215.91Z" /><path fill="#8b3ffd" d="M134,53.58V271.76H80.68L12.28,53.58Z" /></svg>
</a>
<form method="GET" class="flex"
action="/actions-to-payments"
>
<input v-model="sum" type="number"
name="sum"
placeholder="Введите сумму"
class="w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
/>
<button type="submit" class="ml-5 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-pink focus:outline-none">
Пополнить
</button>
</form>
<p v-show="sum" class="text-white mt-1">
ИТОГО + 3.5% комиссия сервиса yoomoney = {{ amount }}
</p>
</div>
<div class="flex justify-between px-4 2xl:px-28 my-4 lg:my-8">
<div class="flex flex-col text-white">
<span class="text-lg">Баланс:</span>
<span class="text-3xl">{{ $page.props.balance }}</span>
</div>
<inertia-link :href="route('setting.payouts')" type="button"
class="my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
>
Вывести
</inertia-link>
</div>
<div class="text-white flex justify-end gap-3 flex-wrap pb-2 px-4 2xl:px-28">
<div class="flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" width="16"
height="16" viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="feather feather-check-square"
><polyline points="9 11 12 14 22 4"></polyline><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path></svg>
Ознакомлен:
</div>
<p>
<a class="underline" rel="noopener noreferrer nofollow"
target="_blank"
href="https://yoomoney.ru/pay/page?id=526623"
>Yoomoney</a>
</p>
<inertia-link class="underline" :href="route('setting.tarif')">
Тарифы
</inertia-link>
</div>
</div>
<div v-if="pointLists.length" class="my-4 lg:my-8">
<div class="py-4 px-4 2xl:px-28 md:text-lg text-white">
История операций:
<!-- <button class="text-xs" @click="testPaid">
Пополнить
</button> -->
</div>
<div class="divide-y divide-indigo-300 ">
<InfinityScroll :node-element="lastNodeLement" :next-cursor="nextCursor"
@fromPagination="putFromPagination"
>
<div v-for="point in pointLists" :key="point.id"
:ref="el => { if (el && point.id === lastElementID) lastNodeLement = el }"
class="py-4 px-4 2xl:px-28 lg:items-center flex flex-col lg:grid gap-2 lg:gap-5 grid-cols-12"
>
<div class="col-span-5 flex items-center">
<div class="flex-shrink-0 mr-3 lg:mr-8">
<svg :class="[point.direction == 2 ? 'text-white rotate-180' : point.direction == 0 ? 'text-green rotate-90' : 'text-red -rotate-90', 'transform w-8 h-8']">
<use xlink:href="#arrow-up-circle"></use>
</svg>
</div>
<div class="flex flex-col">
<p class="truncate text-base lg:text-lg font-semibold text-gray">
{{ point.date }}
</p>
<p class="truncate lg:mt-1 text-base text-gray-light">
{{ point.time }}
</p>
</div>
</div>
<div class="col-span-2 text-left lg:text-center text-white xl:text-xl font-semibold">
{{ point.point }}
</div>
<div class="col-span-5 text-left lg:text-right text-gray-light xl:text-lg">
{{ point.type }}
<div v-if="point.direction == 2">
<button class="text-green underline" @click="checkPaid(point.id)">
Проверить оплату
</button>
</div>
</div>
</div>
</InfinityScroll>
</div>
</div>
<div v-else class="py-4 px-4 2xl:px-28 md:text-lg text-gray-light">
Нет данных
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import { Inertia } from '@inertiajs/inertia'
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import InfinityScroll from '@/Shared/Misc/InfinityScroll.vue'
export default {
components: {
MetaHead,
SettingsMenu,
InfinityScroll,
},
layout: Layout,
props: {
nextCursor: String,
points: Array,
},
setup() {
const containerRef = ref(null)
return {
lastNodeLement: containerRef,
}
},
data() {
return {
pointLists: [],
sum: null,
}
},
computed: {
amount(){
return ((Math.abs(this.sum) * 0.035) + Math.abs(this.sum)).toFixed(1)
},
lastElementID() {
return this.pointLists[this.pointLists.length - 1]?.id
},
},
watch: {
points(points){
this.pointLists = points
}
},
mounted() {
this.pointLists = this.points
},
methods: {
putFromPagination(lists) {
for (let list of lists) {
this.pointLists.push(list)
}
},
checkPaid(id) {
Inertia.post(route('payouts.checkPointPay', id), {
preserveScroll: true,
preserveState: true,
})
},
},
}
</script>

View File

@@ -0,0 +1,105 @@
<template>
<meta-head title="Оповещения"></meta-head>
<modal-feed
:modal-feed="modalFeed"
:is_exist_menu="false"
:open="show"
@close-modal="closeModal"
/>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div v-if="notifications.length" data-simplebar
class="max-h-[500px] overflow-auto my-4 lg:my-8"
>
<div class="divide-y divide-indigo-300">
<div v-for="notification in notifications" :key="notification.id"
class="py-4 px-4 2xl:px-28 lg:items-center flex flex-col lg:grid gap-2 lg:gap-5 grid-cols-12 md:space-x-4"
>
<div class="col-span-4 flex items-center">
<inertia-link :href="route('profile.user', notification.user.username)" class="flex-shrink-0 block mr-5">
<user-avatar :user="notification.user" size="small"
class="w-14 h-14 md:w-20 md:h-20 text-xl"
/>
</inertia-link>
<inertia-link :href="route('profile.user', notification.user.username)" class="flex flex-col">
<p class="truncate text-base lg:text-lg font-semibold text-orange">
{{ notification.user.name }}
</p>
<p class="truncate lg:mt-1 text-base text-gray-light">
{{ notification.created_at }}
</p>
</inertia-link>
</div>
<div class="col-span-6 text-white xl:text-xl">
<notify-text
:type="notification.type"
:content="notification.data"
/>
</div>
<div v-if="notification.feed" class="col-span-2 flex lg:justify-end">
<feed-preview class="w-20 h-20 object-cover cursor-pointer"
:type="notification.feed.type"
:source="notification.feed.entity.preview"
@click="openModal(notification.feed)"
/>
</div>
</div>
</div>
</div>
<div v-else class="m-4 lg:m-8 text-lg text-gray-light">
Оповещений нет
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import ModalFeed from '@/Shared/Overlay/ModalFeed.vue'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
import FeedPreview from '@/Shared/Feed/FeedPreview.vue'
import NotifyText from '@/Shared/Notification/NotifyText.vue'
export default {
components: {
MetaHead,
SettingsMenu,
ModalFeed,
UserAvatar,
FeedPreview,
NotifyText,
},
layout: Layout,
props: {
notifications: Array,
},
data() {
return {
show: false,
modalFeed: {},
}
},
methods: {
openModal(feed) {
this.show = true
this.modalFeed = feed
},
closeModal() {
this.show = false
},
},
}
</script>

View File

@@ -0,0 +1,174 @@
<template>
<meta-head title="Настроить оплату за подписку"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div class="mx-4 2xl:mx-20 my-8">
<form v-if="user.private" class="border-b border-indigo-400 pb-10 mt-10 grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-7 gap-5 lg:gap-10 items-start"
@submit.prevent="submit"
>
<div class="2xl:col-span-3 flex flex-col">
<text-input v-model="form.price" :error="form.errors.price"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light" label="Цена подписки"
type="number"
/>
</div>
<div class="lg:col-span-2 2xl:col-span-1 2xl:mt-8">
<loading-button :loading="form.processing" class="transition shadow-none hover:shadow-classic2 inline-flex items-center px-3 py-2.5 w-full justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Обновить
</loading-button>
</div>
</form>
<hr />
<div v-if="buyers.length || subscriptions.length" class="grid md:grid-cols-2 gap-3">
<div v-if="buyers.length">
<h2 class="mt-5 text-base text-gray-light font-medium">
Ваши платные подписчики
</h2>
<div class="mt-3 space-y-4">
<div v-for="buyer in buyers" :key="buyer.id"
class="bg-indigo-300 flex flex-col p-4 rounded-sm"
>
<div class="flex items-center">
<inertia-link :href="route('profile.user', buyer.user.username)" class="flex-shrink-0 block mr-2 md:mr-4">
<user-avatar :user="buyer.user" size="small"
class="w-10 h-10 md:w-14 md:h-14 text-lg"
/>
</inertia-link>
<inertia-link :href="route('profile.user', buyer.user.username)" class="flex flex-col">
<span class="hover:underline text-sm md:text-base block font-medium text-white">{{ buyer.user.first_name }} {{ buyer.user.last_name }}</span>
</inertia-link>
</div>
<div>
<ul>
<li v-for="list in buyer.lists" :key="list.time_end">
<span :class="[list.active ? 'text-green' : 'text-orange']" class="text-xs">
Дата окончания подписки: {{ list.time_end }}{{ list.active ? ' - активно' : '' }}. Цена {{ list.price }}
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<div v-if="subscriptions.length">
<h2 class="mt-5 text-base text-gray-light font-medium">
Ваши платные подписки
</h2>
<div class="mt-3 space-y-4">
<div v-for="subscription in subscriptions" :key="subscription.id"
class="bg-indigo-300 flex flex-col p-4 rounded-sm"
>
<toggle
:user_id="subscription.user.id"
:enabled="subscription.autosubscription"
textin="Автоматическое списание отключено"
textout="Автоматическое списание включено" @clicked="makeAutoSubsUser"
/>
<div class="mt-3 flex items-center gap-1">
<inertia-link :href="route('profile.user', subscription.user.username)" class="flex-shrink-0 block mr-2 md:mr-4">
<user-avatar :user="subscription.user" size="small"
class="w-10 h-10 md:w-14 md:h-14 text-lg"
/>
</inertia-link>
<inertia-link :href="route('profile.user', subscription.user.username)" class="flex flex-col">
<span class="hover:underline text-sm md:text-base block font-medium text-white">{{ subscription.user.first_name }} {{ subscription.user.last_name }}</span>
</inertia-link>
<div v-if="subscription.active === false" class="ml-auto text-gray-light text-xs">
<button class="hover:underline" @click="removePaidUser(subscription.user.id)">
Отписаться от пользователя
</button>
</div>
</div>
<div>
<ul>
<li v-for="list in subscription.lists" :key="list.time_end">
<span :class="[list.active ? 'text-green' : 'text-orange']" class="text-xs">
Дата окончания подписки: {{ list.time_end }}{{ list.active ? ' - активно' : '' }}. Цена {{ list.price }}
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div v-else class="mt-10 text-gray-light text-lg">
Данных нет!
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { useForm } from '@inertiajs/inertia-vue3'
import { Inertia } from '@inertiajs/inertia'
import { toRefs } from 'vue'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import Toggle from '@/Shared/Form/Toggle.vue'
export default {
components: {
MetaHead,
SettingsMenu,
LoadingButton,
TextInput,
UserAvatar,
Toggle
},
layout: Layout,
props: {
user: Object,
packet: Object,
buyers: Array,
subscriptions: Array,
},
setup(props) {
const { user, packet } = toRefs(props)
const form = useForm({
id: packet.value?.id,
price: packet.value?.price,
user: user.value.id,
})
const submit = () => {
form.post(route('user.package.update'))
}
const removePaidUser = (id) => {
Inertia.post(route('users.removePaidSubs', id), {
preserveScroll: true,
preserveState: true,
})
}
const makeAutoSubsUser = (id) => {
Inertia.post(route('users.settingsPaidSubsUser', id), {
preserveScroll: true,
preserveState: true,
})
}
return { form, submit, removePaidUser, makeAutoSubsUser }
},
}
</script>

View File

@@ -0,0 +1,198 @@
<template>
<meta-head title="Выплаты"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div class="border-b border-indigo-300">
<div v-if="isVerified" class="py-5 px-4 2xl:px-28">
<div class="flex">
<input v-model="amount" type="number"
placeholder="Введите сумму"
class="w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
/>
<button class="ml-5 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-pink focus:outline-none" @click="pay">
Вывести
</button>
</div>
</div>
<div v-else class="py-5 px-4 2xl:px-28">
<div class="text-white text-lg">
Заполните номер телефона в <inertia-link :href="route('setting.index')" class="underline">
профиле
</inertia-link> и пройдите <inertia-link :href="route('setting.verification')" class="underline">
верификацию
</inertia-link>
</div>
</div>
<!-- <div class="py-5 px-4 2xl:px-28">
<div class="flex">
<input v-model="bank_info.number" type="number"
placeholder="Введите номер карты"
class="w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light" label="Фамилия"
/>
<button class="ml-5 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-pink focus:outline-none" @click="updateRequisites">
{{ bank_info?.id ? 'Обновить' : 'Создать' }}
</button>
</div>
<div class="text-red text-sm">
Банковские реквизиты
</div>
</div> -->
<div class="flex justify-between px-4 2xl:px-28 my-4 lg:my-8">
<div class="flex flex-col text-white">
<span class="text-lg">Баланс:</span>
<span class="text-3xl">{{ $page.props.balance }}</span>
</div>
</div>
</div>
<div v-if="withdrawalsLists.length" class="my-4 lg:my-8">
<div class="py-4 px-4 2xl:px-28 md:text-lg text-white">
История операций:
</div>
<div class="divide-y divide-indigo-300 ">
<InfinityScroll :node-element="lastNodeLement" :next-cursor="nextCursor"
@fromPagination="putFromPagination"
>
<div v-for="withdrawal in withdrawalsLists" :key="withdrawal.id"
:ref="el => { if (el && withdrawal.id === lastElementID) lastNodeLement = el }"
class="py-4 px-4 2xl:px-28 lg:items-center flex flex-col lg:grid gap-2 lg:gap-5 grid-cols-12"
>
<div class="col-span-4 flex items-center">
<div class="flex-shrink-0 mr-3 lg:mr-8">
<svg class="transform w-8 h-8 rotate-90" :class="{
'text-gray-light': withdrawal.status === 'pending',
'text-green': withdrawal.status === 'success',
'text-red': withdrawal.status === 'cancel',
}"
>
<use xlink:href="#arrow-up-circle"></use>
</svg>
</div>
<div class="flex flex-col">
<p class="truncate text-base lg:text-lg font-semibold text-gray">
{{ withdrawal.datePart }}
</p>
<p class="truncate lg:mt-1 text-base text-gray-light">
{{ withdrawal.timePart }}
</p>
</div>
</div>
<div class="col-span-2 text-left lg:text-center text-white xl:text-xl font-semibold">
{{ withdrawal.amount }}
</div>
<div class="col-span-6 text-left lg:text-right text-gray-light xl:text-lg flex flex-col">
<span v-html="withdrawal.history_payment_details"></span>
<span class="mt-2 text-orange">{{ withdrawal.meta }}</span>
<span v-show="withdrawal.description" class="mt-2">{{ withdrawal.description }}</span>
</div>
</div>
</InfinityScroll>
</div>
</div>
<div v-else class="py-4 px-4 2xl:px-28 md:text-lg text-gray-light">
Нет данных
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import { ref } from 'vue'
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import InfinityScroll from '@/Shared/Misc/InfinityScroll.vue'
export default {
components: {
MetaHead,
SettingsMenu,
InfinityScroll,
},
layout: Layout,
props: {
nextCursor: {
type: String,
default: ''
},
isVerified: {
type: Boolean,
default: false
},
withdrawals: {
type: Array,
default: () => []
},
// requisites: {
// type: Object,
// default: () => {}
// }
},
setup() {
const containerRef = ref(null)
return {
lastNodeLement: containerRef,
}
},
data() {
return {
withdrawalsLists: [],
bank_info: {},
amount: null,
}
},
computed: {
lastElementID() {
return this.withdrawalsLists[this.withdrawalsLists.length - 1]?.id
},
},
watch: {
withdrawals(withdrawals){
this.withdrawalsLists = withdrawals
}
},
mounted() {
this.withdrawalsLists = this.withdrawals
// this.bank_info = this.requisites
},
methods: {
// updateRequisites() {
// if(!this.bank_info?.number) return
// Inertia.post(route('requisites.bank'), {
// number: this.bank_info.number,
// })
// },
pay() {
if(!this.amount) return
const that = this
Inertia.post(route('payouts.start'), {
amount: that.amount,
}, {
onSuccess: () => {
that.amount = null
}
})
},
putFromPagination(lists) {
for (let list of lists) {
this.withdrawalsLists.push(list)
}
},
},
}
</script>

View File

@@ -0,0 +1,358 @@
<template>
<meta-head title="Мои настройки"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div class="m-4 lg:m-8">
<div class="flex flex-col xl:flex-row">
<div class="xl:mr-24">
<div class="flex items-center">
<div class="flex-shrink-0 mr-5">
<user-avatar :user="user" class="w-20 h-20 text-lg" />
</div>
<div class="flex flex-col">
<p class="text-base lg:text-xl font-semibold text-gray">
{{ user.name }}
</p>
<p class="lg:mt-1 text-base text-gray-light">
@{{ user.username }}
</p>
</div>
</div>
<input ref="fileavatar" class="hidden"
accept="image/png, image/jpeg, image/jpg" type="file"
@change="previewFiles"
>
<input ref="filebanner" class="hidden"
accept="image/png, image/jpeg, image/jpg" type="file"
@change="previewFilesBanner"
>
<div>
<div v-if="$page.props.errors.avatar" class="text-red lg:text-lg">
{{ $page.props.errors.avatar }}
</div>
<button v-if="!user.photo_path" class="hover:underline mt-2 text-left lg:mt-5 default lg:text-lg text-orange"
@click="changeAvatar"
>
Изменить фото профиля
</button>
<button v-else class="hover:underline mt-2 text-left lg:mt-5 default lg:text-lg text-red"
@click="removeAvatar"
>
Удалить фото
</button>
</div>
</div>
<div class="mt-5 xl:mt-0">
<div class="flex items-center">
<div class="flex-shrink-0 mr-5">
<user-banner class="w-60 lg:w-96 h-20 rounded-xl bg-indigo-300" :user="user"
size="banner"
/>
</div>
</div>
<div>
<div v-if="$page.props.errors.banner" class="text-red lg:text-lg">
{{ $page.props.errors.banner }}
</div>
<button v-if="!user.banner_path" class="hover:underline mt-2 text-left lg:mt-5 default lg:text-lg text-orange"
@click="changeBanner"
>
Изменить баннер профиля
</button>
<button v-else class="hover:underline mt-2 text-left lg:mt-5 default lg:text-lg text-red"
@click="removeBanner"
>
Удалить баннер
</button>
</div>
</div>
</div>
<form class="border-b border-indigo-400 pb-10 mt-10 grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-7 gap-5 lg:gap-10 items-start" @submit.prevent="submitPassword">
<div class="2xl:col-span-3 flex flex-col">
<text-input v-model="passwordForm.old_password" :error="passwordForm.errors.old_password"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light" label="Старый пароль"
type="password"
/>
</div>
<div class="2xl:col-span-3 flex flex-col">
<text-input v-model="passwordForm.new_password" :error="passwordForm.errors.new_password"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light" label="Новый пароль"
type="password"
/>
</div>
<div class="lg:col-span-2 2xl:col-span-1 2xl:mt-8">
<loading-button :loading="form.processing" class="transition shadow-none hover:shadow-classic2 inline-flex items-center px-3 py-2.5 w-full justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Обновить
</loading-button>
</div>
</form>
<form class="mt-10 grid gap-4 lg:gap-10 grid-cols-1 lg:grid-cols-2" @submit.prevent="submit">
<div class="flex flex-col">
<text-input v-model="form.first_name" :error="form.errors.first_name"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light" label="Имя"
/>
</div>
<div class="flex flex-col">
<text-input v-model="form.last_name" :error="form.errors.last_name"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light" label="Фамилия"
/>
</div>
<div class="flex flex-col">
<text-input v-model="form.username" :error="form.errors.username"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light" label="Тег"
/>
</div>
<div class="flex flex-col">
<text-input v-model="form.date_of_birth" :error="form.errors.date_of_birth"
type="date" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Дата рождения"
/>
</div>
<div class="flex flex-col">
<text-input v-model="form.email" :error="form.errors.email"
type="email" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Почта"
/>
</div>
<div class="flex flex-col">
<text-input v-model="form.phone" :error="form.errors.phone"
type="tel" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Телефон"
/>
</div>
<div class="flex flex-col">
<text-input v-model="form.inn" :error="form.errors.inn"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="ИНН"
/>
</div>
<div class="flex flex-col">
<text-input v-model="form.checking_account" :error="form.errors.checking_account"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Расчетный счет"
/>
</div>
<div class="flex flex-col">
<text-input v-model="form.bik" :error="form.errors.bik"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Бик банка"
/>
</div>
<div>
<div class="text-gray-light text-lg mb-2">
Пол
</div>
<div class="flex space-x-6">
<div class="flex items-center">
<input id="user-sex-1" v-model="form.sex"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-sex-1" class="select-none ml-3 text-gray">Женский</label>
</div>
<div class="flex items-center">
<input id="user-sex-2" v-model="form.sex"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-sex-2" class="select-none ml-3 text-gray">Мужской</label>
</div>
</div>
</div>
<div class="lg:col-span-2">
<div class="text-gray-light text-lg mb-2">
Тип
</div>
<div class="flex space-x-6">
<div class="flex items-center">
<input id="user-type-1" v-model="form.type"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-type-1" class="select-none ml-3 text-gray">Физ. лицо</label>
</div>
<div class="flex items-center">
<input id="user-type-2" v-model="form.type"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-type-2" class="select-none ml-3 text-gray">Самозанятый</label>
</div>
<div class="flex items-center">
<input id="user-type-3" v-model="form.type"
value="3" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-type-3" class="select-none ml-3 text-gray">Юридическое лицо</label>
</div>
<div class="flex items-center">
<input id="user-type-4" v-model="form.type"
value="4" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-type-4" class="select-none ml-3 text-gray">ИП</label>
</div>
</div>
</div>
<div class="lg:col-span-2">
<div class="flex flex-col">
<textarea-input v-model="form.about" label="О себе"
:error="form.errors.about" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md"
cols="30" rows="2"
></textarea-input>
</div>
</div>
<div class="lg:col-span-2">
<!-- <div class="text-gray-light text-lg mb-2">
Ограничить доступ
</div> -->
<div class="flex items-center">
<input id="user-private" v-model="form.private"
type="checkbox" class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-private" class="select-none ml-3 text-gray">Включить эксклюзивный акаунт, доступ к контенту только по подписке</label>
</div>
<!-- <div class="flex items-center">
<input id="user-commercial" v-model="form.allow_adult_content"
type="checkbox" class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="user-commercial" class="select-none ml-3 text-gray">Разрешить контент для взрослых</label>
</div> -->
</div>
<div class="flex flex-wrap -my-1 -mx-3">
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Сохранить
</loading-button>
<!-- <button type="button"
class="mx-3 my-1 default text-lg text-orange"
>
Удалить аккаунт
</button> -->
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { useForm } from '@inertiajs/inertia-vue3'
import { Inertia } from '@inertiajs/inertia'
import { toRefs } from 'vue'
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
import UserBanner from '@/Shared/Misc/UserBanner.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import TextareaInput from '@/Shared/Form/TextareaInput.vue'
export default {
components: {
MetaHead,
UserAvatar,
UserBanner,
TextInput,
TextareaInput,
LoadingButton,
SettingsMenu,
},
layout: Layout,
props: {
user: Object,
},
setup(props) {
const { user } = toRefs(props)
const form = useForm({
first_name: user.value.first_name,
last_name: user.value.last_name,
username: user.value.username,
email: user.value.email,
phone: user.value.phone,
sex: user.value.sex,
type: user.value.type,
date_of_birth: user.value.date_of_birth,
about: user.value.about,
private: user.value.private,
inn: user.value.inn,
checking_account: user.value.checking_account,
bik: user.value.bik,
password: null,
})
const passwordForm = useForm({
old_password: null,
new_password: null,
})
const submit = () => {
form.put(route('users.update', user.value.id))
}
const submitPassword = () => {
passwordForm.put(route('users.update.password', user.value.id), {
onSuccess: () => passwordForm.reset(),
})
}
return { form, passwordForm, submit, submitPassword }
},
methods: {
previewFilesBanner(event){
const file = event.target.files[0]
Inertia.post(route('image.banner'), {
_method: 'put',
banner: file,
})
},
previewFiles(event){
const file = event.target.files[0]
Inertia.post(route('image.avatar'), {
_method: 'put',
avatar: file,
})
},
changeBanner(){
this.$refs.filebanner.click()
},
removeBanner(){
Inertia.post(route('image.banner.remove'), {
_method: 'delete',
})
},
changeAvatar(){
this.$refs.fileavatar.click()
},
removeAvatar(){
Inertia.post(route('image.avatar.remove'), {
_method: 'delete',
})
}
},
}
</script>

View File

@@ -0,0 +1,96 @@
<template>
<meta-head title="Покупки"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div v-if="feedLists.length" class="m-4 lg:m-8 grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-4 gap-2 lg:gap-4">
<InfinityScroll :node-element="lastNodeLement" :next-cursor="nextCursor"
@fromPagination="putFromPagination"
>
<div v-for="feed in feedLists" :key="feed.id"
:ref="el => { if (el && feed.id === lastElementID) lastNodeLement = el }"
>
<inertia-link :href="route('setting.show.purchases', feed.id)" class="block contain relative overflow-hidden">
<feed-header-misc :count="1" :type="feed.type" />
<div>
<feed-preview class="w-full h-36 md:h-72 object-cover" :type="feed.type"
:source="feed.preview"
/>
</div>
<p class="mt-2 text-gray-light text-sm">
Цена: {{ feed.price }}
</p>
<p class="mt-2 text-gray-light text-sm">
Дата покупки: {{ feed.purchase_date }}
</p>
</inertia-link>
</div>
</InfinityScroll>
</div>
<div v-else class="m-4 lg:m-8 text-lg text-gray-light">
Вы еще не совершали покупки
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import FeedHeaderMisc from '@/Shared/Feed/HeaderMisc.vue'
import FeedPreview from '@/Shared/Feed/FeedPreview.vue'
import InfinityScroll from '@/Shared/Misc/InfinityScroll.vue'
export default {
components: {
MetaHead,
SettingsMenu,
FeedHeaderMisc,
FeedPreview,
InfinityScroll,
},
layout: Layout,
props: {
nextCursor: String,
feeds: Array,
},
setup() {
const containerRef = ref(null)
return {
lastNodeLement: containerRef,
}
},
data() {
return {
feedLists: [],
}
},
computed: {
lastElementID() {
return this.feedLists[this.feedLists.length - 1]?.id
},
},
mounted() {
this.feedLists = this.feeds
},
methods: {
putFromPagination(lists) {
for (let list of lists) {
this.feedLists.push(list)
}
},
},
}
</script>

View File

@@ -0,0 +1,106 @@
<template>
<meta-head title="Скачать медиа контент"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4 p-5">
<div class="mb-4 flex items-center text-gray-light text-lg font-medium">
<inertia-link :href="route('setting.purchases')" class="block hover:underline">
Вернуться
</inertia-link>
<span class="px-3">/</span>
<h1 class="text-gray">
Скачать медиа контент
</h1>
</div>
<div class="my-8 flex items-center justify-between">
<inertia-link :href="route('profile.user', seller.username)" class="flex items-center">
<div class="flex-shrink-0 block mr-2 md:mr-4">
<user-avatar :user="seller" size="small"
class="w-10 h-10 md:w-16 md:h-16 text-lg"
/>
</div>
<div class="flex flex-col">
<span class="text-sm md:text-base block font-medium text-white">{{ seller.name }}</span>
<span class="text-xs text-gray-light">продавец</span>
</div>
</inertia-link>
<div class="text-right">
<p class="mt-2 text-gray-light text-sm">
Цена: {{ purchase.price }}
</p>
<p class="mt-2 text-gray-light text-sm">
Дата покупки: {{ purchase.purchase_date }}
</p>
</div>
</div>
<div v-if="purchase.title" class="text-lg text-gray font-semibold">
{{ purchase.title }}
</div>
<div v-if="purchase.body" class="text-lg mt-4 text-gray-light"
v-html="purchase.body"
></div>
<div class="mt-4">
<component
:is="currentTypeNode"
:purchase="purchase"
></component>
</div>
<div class="mt-12 flex -mx-3 -my-1 flex-wrap">
<a :href="route('download.purchases', purchase.id)" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none">
Скачать архив
</a>
<inertia-link :href="route('setting.purchases')"
class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none"
>
Вернуться
</inertia-link>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import PurchaseImages from '@/Shared/Purchase/PurchaseImages.vue'
import PurchaseMusics from '@/Shared/Purchase/PurchaseMusics.vue'
import PurchaseVideos from '@/Shared/Purchase/PurchaseVideos.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
export default {
components: {
MetaHead,
PurchaseImages,
PurchaseMusics,
PurchaseVideos,
SettingsMenu,
UserAvatar,
},
layout: Layout,
props: {
purchase: Object,
seller: Object,
},
computed: {
currentTypeNode() {
return 'purchase-' + this.purchase.type
},
},
}
</script>

View File

@@ -0,0 +1,115 @@
<template>
<meta-head title="Тарифы"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div class="mx-4 2xl:mx-28 my-8">
<h2 class="text-xl lg:text-2xl xl:text-4xl text-white font-semibold">
Выберите тариф, который подходит именно вам
</h2>
<p class="mt-4 lg:text-xl xl:text-2xl text-white">
Более 1 млн фотографий, видео и музыки в постоянном доступе для вас!
</p>
<p v-if="lastSubscription" class="mt-4 xl:text-xl text-green">
Дата окончания подписки:
<span class="block md:inline-block">{{ lastSubscription.endDateTime }} ({{ lastSubscription.package.name }})</span>
<span v-if="!is_active_sub" class="text-pink"> (Подписка закончилась)</span>
</p>
<div class="rounded-xl bg-indigo-300 px-3 py-4 mt-5">
<toggle
:user_id="user_id"
:enabled="user_autosubscription"
textin="Автоматическое списание отключено"
textout="Автоматическое списание включно" @clicked="makeAutoSubs"
/>
</div>
<div v-for="plan in plans" :key="plan.id"
:class="[is_active_sub && lastSubscription?.package.id === plan.id ? 'border-l-4 lg:border-l-8 border-orange' : '' ,'mt-7 p-7 xl:mt-14 xl:p-14 shadow-classic rounded-md bg-indigo-100']"
>
<div class="flex flex-col lg:grid grid-cols-6 gap-3 items-center">
<div class="col-start-1 col-end-4 flex flex-col items-center lg:items-start text-center lg:text-left">
<span class="text-xl md:text-2xl lg:text-3xl xl:text-4xl text-white font-medium">{{ plan.name }}</span>
</div>
<div class="col-start-5 col-end-7 flex flex-col items-center">
<span class="text-3xl lg:text-4xl xl:text-6xl text-white font-bold">{{ plan.price }}</span>
<button :disabled="offerCheck == false" type="button"
:class="[is_active_sub && lastSubscription?.package.id === plan.id ? 'bg-white border border-green text-green' : 'text-white bg-green' , 'mt-4 my-1 transition shadow-none inline-flex items-center px-8 py-3 justify-center text-base rounded-3xl focus:outline-none disabled:bg-gray-light disabled:cursor-not-allowed']"
@click="subsPlan(plan.id)"
>
<span v-if="is_active_sub && lastSubscription?.package.id === plan.id">
активен
</span>
<span v-else>
купить тариф
</span>
</button>
</div>
</div>
</div>
<div class="mt-5 flex items-center">
<input id="offer_check" v-model="offerCheck"
type="checkbox" class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="offer_check" class="select-none ml-3 text-gray text-lg md:text-xl">Согласен с условиями
<a href="/docs/offer_paid_subscription.pdf" class="underline"
target="_blank"
rel="noopener noreferrer nofollow"
>оферты</a></label>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import Toggle from '@/Shared/Form/Toggle.vue'
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
export default {
components: {
MetaHead,
SettingsMenu,
Toggle,
},
layout: Layout,
props: {
plans: Array,
is_active_sub: Boolean,
user_autosubscription: Boolean,
user_id: Number,
lastSubscription: Object,
},
data: function() {
return {
offerCheck: true
}
},
methods: {
makeAutoSubs(id){
Inertia.patch(route('users.settingsAutoSubs', id), {
preserveScroll: true,
preserveState: true,
})
},
subsPlan(id) {
Inertia.post(route('users.plan', id), {
preserveScroll: true,
preserveState: true,
})
},
},
}
</script>

View File

@@ -0,0 +1,178 @@
<template>
<meta-head title="Верификация"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div class="mx-4 2xl:mx-28 my-8">
<h2 class="text-xl lg:text-2xl xl:text-3xl text-white font-semibold">
Верификация: паспорт
</h2>
<div>
<div v-if="isPassportVerified">
<div class="mt-5 text-green font-semibold text-lg">
Аккаунт успешно верифицирован
</div>
</div>
<div v-else>
<form class="flex flex-col" @submit.prevent="submit">
<file-input
v-model="form.docs"
accept="image/png, image/jpeg, image/jpg"
:error="form.errors.docs"
label="Скан 1ой страницы паспорта(.png,.jpg)"
/>
<div class="mt-3 flex flex-wrap">
<progress
v-if="form.progress"
class="mx-3 my-1 w-full"
:value="form.progress.percentage"
max="100"
>
{{ form.progress.percentage }}%
</progress>
<loading-button :loading="form.processing" class=" transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-1.5 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Загрузить
</loading-button>
</div>
</form>
<div v-if="docUuid">
<p class="mt-4 text-sm text-white">
<a :href="route('users.get.document', {'uuid': docUuid}) " class="underline">
<span>Ваш паспорт</span>
</a>
</p>
<p class="text-sm text-white">
(Ожидайте верификацию, для замены загрузите скан заново)
</p>
</div>
</div>
</div>
<!-- <div class="my-5">
<hr>
</div> -->
<!-- <h2 class="text-xl lg:text-2xl xl:text-3xl text-white font-semibold">
Верификация: телефон
</h2>
<div v-if="isPhoneVerify" class="mt-5 text-green font-semibold text-lg">
Ваш номер успешно верифицирован!
</div> -->
<!-- <div v-else class="mt-2">
<p class=" text-white text-lg">
На номер телефона {{ phone }}, будет совершен звонок, нужно ввести 4-х значный код
</p>
<div v-if="isToken" class="mt-5">
<div class="flex flex-col">
<text-input v-model="token"
type="text" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Введите код"
/>
<div>
<button type="button"
class="text-white bg-orange hover:shadow-classic2 mt-4 my-1 transition shadow-none inline-flex items-center px-8 py-2 justify-center text-base rounded-md"
@click="sendVerifyCode"
>
Отправить
</button>
</div>
</div>
</div>
<button v-else type="button"
class="text-white bg-pink hover:shadow-pink mt-5 my-1 transition shadow-none inline-flex items-center px-8 py-2 justify-center text-base rounded-md"
@click="sendCode"
>
Отправить код
</button>
</div> -->
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import TextInput from '@/Shared/Form/TextInput.vue'
import { Inertia } from '@inertiajs/inertia'
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import FileInput from '@/Shared/Form/FileInput.vue'
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import { ref } from 'vue'
export default {
components: {
MetaHead,
SettingsMenu,
FileInput,
LoadingButton,
TextInput
},
layout: Layout,
props: {
docUuid: {
type: String,
default: null
},
phone: {
type: String,
default: ''
},
isToken: {
type: Boolean,
default: false
},
isPhoneVerify: {
type: Boolean,
default: false
},
isPassportVerified: {
type: Boolean,
default: false
},
},
setup(props) {
const form = useForm({
docs: null,
})
const token = ref('')
const submit = () => {
if(!form.docs) return
form.post(route('users.document'), {
onSuccess: () => form.reset(),
})
}
const sendCode = () => {
Inertia.post(route('users.phone.verify.request'))
}
const sendVerifyCode = () => {
Inertia.post(route('users.phone.verify.token'), {
token: token.value,
})
}
return {
form,
submit,
sendCode,
sendVerifyCode,
token,
}
}
}
</script>

View File

@@ -0,0 +1,90 @@
<template>
<meta-head title="Написать нам"></meta-head>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div class="mt-16 shadow-classic rounded-md bg-indigo-200">
<div class="flex flex-col md:grid grid-cols-6 lg:grid-cols-5">
<settings-menu />
<div class="col-span-4">
<div class="mx-4 2xl:mx-28 my-8">
<form class="flex flex-col gap-4" @submit.prevent="submit">
<div class="flex flex-col">
<text-input v-model="form.title" :error="form.errors.title"
type="text" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Тема"
/>
</div>
<div class="flex flex-col">
<textarea-input v-model="form.body" :error="form.errors.body"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md" cols="30"
rows="4" label="Сообщение"
/>
</div>
<div>
<loading-button :loading="form.processing" class="my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Отправить
</loading-button>
</div>
</form>
<!-- <div class="mt-10">
<div class="flex flex-col text-white">
<a href="/company/about" class="hover:underline">О компании</a>
<a href="/company/offer" class="hover:underline">Оферта</a>
<a href="/company/terms-payment" class="hover:underline">Условия оплаты</a>
<a href="/company/terms-service" class="hover:underline">Условия предоставления услуг</a>
<a href="/company/cookies" class="hover:underline">Cookies</a>
<a href="/company/privacy-policy" class="hover:underline">Privacy Policy</a>
</div>
</div> -->
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import TextInput from '@/Shared/Form/TextInput.vue'
import { Inertia } from '@inertiajs/inertia'
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import Layout from '@/Shared/Layout.vue'
import SettingsMenu from '@/Shared/LayoutParts/SettingsMenu.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import TextareaInput from '@/Shared/Form/TextareaInput.vue'
export default {
components: {
MetaHead,
SettingsMenu,
LoadingButton,
TextInput,
TextareaInput,
},
layout: Layout,
props: {
},
setup(props) {
const form = useForm({
title: null,
body: null,
})
const submit = () => {
form.post(route('common.write-to-us'), {
onSuccess: () => form.reset(),
})
}
return {
form,
submit
}
}
}
</script>

144
resources/js/Pages/Tag/Feed.vue Executable file
View File

@@ -0,0 +1,144 @@
<template>
<meta-head title="Тег"></meta-head>
<div class="mb-6 py-3 banner relative bg-center bg-no-repeat bg-cover"
style="background-image: url('/image/bg-home.jpg');"
>
<div class="h-52 flex justify-center items-center text-center">
<div class="max-w-4xl text-gray px-3">
<h1 class="text-xl md:text-2xl lg:text-3xl xl:text-5xl text-white font-semibold xl:leading-relaxed">
#{{ tag.name }}
</h1>
</div>
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 buttons-filter-line">
<div class=" relative mb-5">
<div class="absolute inset-y-0 left-3 flex items-center z-[1]">
<svg class="flex-shrink-0 h-5 w-5 text-gray-light" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" fill="currentColor"
>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11 4a7 7 0 100 14 7 7 0 000-14zm-9 7a9 9 0 1118 0 9 9 0 01-18 0z"
/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M15.943 15.943a1 1 0 011.414 0l4.35 4.35a1 1 0 01-1.414 1.414l-4.35-4.35a1 1 0 010-1.414z"
/>
</svg>
</div>
<input
v-model="form.search"
class="relative w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light !pl-10 h-14" placeholder="Поиск"
type="search"
>
</div>
<div class="grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<inertia-link :href="local_route"
:class="[active_filter == 'new' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center rounded-md text-white focus:outline-none']"
>
Новинки
</inertia-link>
<inertia-link :href="local_route"
:data="{ filter: 'hot' }"
:class="[active_filter == 'hot' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center shadow-classic rounded-md text-white focus:outline-none']"
>
<svg class="-ml-1 mr-2 h-4 w-4 md:h-5 md:w-5 flex-shrink-0" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
>
<path
d="M14.422 6.243c-3.57-2.172-1.895-5.238-1.824-5.365A.586.586 0 0012.09 0c-1.837 0-3.276.522-4.276 1.552-1.71 1.76-1.63 4.498-1.597 5.667.004.13.007.242.007.325 0 .868.14 1.67.263 2.377.079.456.147.85.16 1.159.012.331-.048.407-.05.41-.01.01-.081.049-.276.049a.657.657 0 01-.53-.235c-.565-.634-.604-2.365-.496-3.307a.586.586 0 00-.582-.654c-1.521 0-2.658 2.488-2.658 4.712 0 1.046.21 2.07.625 3.045a8.081 8.081 0 001.7 2.527C5.894 19.157 7.89 20 10 20c2.119 0 4.114-.83 5.618-2.34a7.892 7.892 0 002.327-5.605c0-2.692-2.107-4.95-3.523-5.812zM10 18.828c-3.671 0-6.773-3.101-6.773-6.773 0-.893.213-1.83.585-2.571.086-.173.175-.322.263-.447.01.967.163 2.284.841 3.046.366.41.852.627 1.405.627.518 0 .912-.156 1.172-.466.486-.578.337-1.436.148-2.524-.115-.66-.245-1.409-.245-2.176 0-.1-.003-.22-.007-.359-.032-1.107-.099-3.412 1.265-4.817.613-.63 1.467-1.014 2.55-1.145-.123.406-.228.92-.232 1.497-.01 1.27.476 3.085 2.841 4.524 1.145.696 2.96 2.618 2.96 4.811A6.78 6.78 0 0110 18.828z"
/>
</svg>
Популярные
</inertia-link>
</div>
<div class="mt-4 grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<feed-tags-buttons :slug="tag.slug" />
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 my-6">
<button class="button-default text-gray text-lg" @click="resetFilter">
Очистить фильтры
</button>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div v-if="feeds.length" class="grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 cards-block rounded-md bg-indigo-200 shadow-classic grid gap-2 lg:gap-4 grid-cards p-2 lg:p-5">
<feed
:next-cursor="nextCursor"
:feeds="feeds"
/>
</div>
<div v-else class="font-bold text-xl lg:text-3xl text-gray text-center">
Записи не найдены
</div>
</div>
</template>
<script>
import throttle from 'lodash/throttle'
import pickBy from 'lodash/pickBy'
import { Inertia } from '@inertiajs/inertia'
import Feed from '@/Shared/Feed/Feed.vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import FeedTagsButtons from '@/Shared/Misc/FeedTagsButtons.vue'
export default {
components: {
MetaHead,
FeedTagsButtons,
Feed,
},
layout: Layout,
props: {
feeds: Array,
local_route: String,
active_filter: String,
tag: Object,
searchFilters: Object,
nextCursor: String,
},
data() {
return {
form: {
search: this.searchFilters.search,
},
}
},
watch: {
form: {
deep: true,
handler: throttle(function () {
Inertia.get(this.local_route, pickBy(this.form), {
preserveScroll: true, preserveState: true
})
}, 500),
},
},
methods: {
resetFilter() {
Inertia.get(
route('feed.tags', this.tag.slug),
{},
{
preserveScroll: true,
}
)
},
},
}
</script>

175
resources/js/Pages/User/Index.vue Executable file
View File

@@ -0,0 +1,175 @@
<template>
<meta-head title="Пользователи"></meta-head>
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<div class="mb-5">
<search-filter v-model="form.search" class="w-full max-w-3xl mr-4"
@reset="reset"
>
<label class="text-xs md:text-base block text-gray mb-2">Пол:</label>
<div class="flex space-x-6">
<div class="flex items-center">
<input id="user-sex-1" v-model="form.sex"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent cursor-pointer"
>
<label for="user-sex-1" class="cursor-pointer select-none ml-3 text-gray text-xs md:text-base">Женский</label>
</div>
<div class="flex items-center">
<input id="user-sex-2" v-model="form.sex"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent cursor-pointer"
>
<label for="user-sex-2" class="cursor-pointer select-none ml-3 text-gray text-xs md:text-base">Мужской</label>
</div>
</div>
</search-filter>
</div>
<div v-show="users.length" class="grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 cards-block rounded-md bg-indigo-200 shadow-classic grid gap-2 lg:gap-4 grid-cards p-2 lg:p-5">
<InfinityScroll :node-element="lastNodeLement" :next-cursor="nextCursor"
@fromPagination="putFromPagination"
>
<div
v-for="user in userLists"
:key="user.id"
:ref="el => { if (el && user.id === lastElementID) lastNodeLement = el }"
class="user-card relative"
>
<div v-if="!user.private" class="absolute inset-x-0 top-4 z-10 flex justify-center">
<toggle
:user_id="user.id"
:enabled="user.is_sub"
textin="Подписаться"
textout="Отписаться" @clicked="susbscribe"
/>
</div>
<div class="absolute inset-x-0 bottom-4 z-10 flex justify-center">
<div class="flex flex-col items-center">
<inertia-link :href="route('profile.user', user.username)" class="block flex-shrink-0">
<user-avatar :user="user" size="small"
class="border border-white shadow-classic h-20 w-20 text-lg"
/>
</inertia-link>
<inertia-link :href="route('profile.user', user.username)" class="mt-2 block text-white text-sm text-center">
<p class="">
{{ user.name }}
</p>
<p class="text-xs">
{{ user.username }}
</p>
</inertia-link>
</div>
</div>
<div class="gradient-profile relative overflow-hidden">
<user-banner class="h-72 bg-indigo-300" :user="user"
size="banner"
/>
</div>
</div>
</InfinityScroll>
</div>
<div v-show="users.length == 0">
<p class="text-center md:text-2xl text-gray-light">
Пользователи не найдены
</p>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import { Inertia } from '@inertiajs/inertia'
import throttle from 'lodash/throttle'
import pickBy from 'lodash/pickBy'
import mapValues from 'lodash/mapValues'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import Toggle from '@/Shared/Form/Toggle.vue'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
import UserBanner from '@/Shared/Misc/UserBanner.vue'
import SearchFilter from '@/Shared/Form/SearchFilter.vue'
import InfinityScroll from '@/Shared/Misc/InfinityScroll.vue'
export default {
components: {
MetaHead,
UserAvatar,
UserBanner,
Toggle,
InfinityScroll,
SearchFilter,
},
layout: Layout,
props: {
nextCursor: String,
users: Array,
filters: Object,
per_page: Number,
},
setup() {
const containerRef = ref(null)
return {
lastNodeLement: containerRef,
}
},
data() {
return {
userLists: [],
form: {
search: this.filters.search,
sex: this.filters.sex,
},
}
},
computed: {
lastElementID() {
return this.userLists[this.userLists.length - 1]?.id
},
},
watch: {
users() {
this.updateRequest()
},
form: {
deep: true,
handler: throttle(function () {
Inertia.get(this.route('users.index'), pickBy(this.form), {
preserveState: true,
})
}, 500),
},
},
mounted() {
this.updateRequest()
},
methods: {
putFromPagination(lists) {
for (let list of lists) {
this.userLists.push(list)
}
},
susbscribe(user_id) {
Inertia.post(
route('users.subs', user_id),
{},
{ preserveScroll: true, preserveState: true }
)
},
updateRequest() {
this.userLists = this.users
},
reset() {
this.form = mapValues(this.form, () => null)
},
},
}
</script>

View File

@@ -0,0 +1,180 @@
<template>
<meta-head title="Загрузить видео"></meta-head>
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
<form class=" bg-indigo-200 shadow-classic rounded-md p-5" @submit.prevent="submit">
<div class="mb-4 flex items-center text-gray-light text-lg font-medium">
<link-back class="default block hover:underline">
Вернуться
</link-back>
<span class="px-3">/</span>
<h1 class="text-gray">
Загрузка видео
</h1>
</div>
<div class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.title" :error="form.errors.title"
type="text" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Название"
/>
<warning-text />
</div>
<div class="flex flex-col">
<textarea-input v-model="form.body" :error="form.errors.body"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md" cols="30"
rows="4" label="Описание"
/>
</div>
<div class="flex flex-col">
<file-input
v-model="form.preview"
accept="image/png, image/jpeg, image/jpg"
:error="form.errors.preview"
label="Загрузить превью"
/>
</div>
<div class="flex flex-col">
<file-input-multiple
v-model="form.videos"
accept=".mp4"
:error="form.errors.videos"
label="Выбрать видео"
/>
</div>
<div class="text-gray-light">
<TagInput v-model="form.tags" />
</div>
<div>
<div class="text-gray-light text-lg mb-2">
Тип контента
</div>
<div class="flex flex-wrap gap-4">
<div class="flex items-center">
<input id="feed-paid-1" v-model="form.is_paid"
value="0" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-1" class="select-none ml-3 text-gray">Бесплатный</label>
</div>
<div class="flex items-center">
<input id="feed-paid-2" v-model="form.is_paid"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-2" class="select-none ml-3 text-gray">Эксклюзивный</label>
</div>
<div v-if="authUser.private" class="flex items-center">
<input id="feed-paid-3" v-model="form.is_paid"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-3" class="select-none ml-3 text-gray">Доступен по личной подписке</label>
</div>
</div>
</div>
<!-- <div class="flex items-center">
<input id="is_adult_feed" v-model="form.is_adult"
type="checkbox"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="is_adult_feed" class="select-none ml-3 text-gray">Контент для взрослых</label>
</div> -->
<div v-if="form.is_paid == 1" class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.price" :error="form.errors.price"
type="number" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Цена"
/>
</div>
<div class="flex flex-col">
<file-input-multiple
v-model="form.videos_paid"
accept=".mp4"
:error="form.errors.videos_paid"
label="Выбрать видео"
/>
</div>
</div>
</div>
<div class="mt-12 flex flex-wrap -my-1 -mx-3">
<progress
v-if="form.progress"
class="mx-3 my-1 w-full"
:value="form.progress.percentage"
max="100"
>
{{ form.progress.percentage }}%
</progress>
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Создать
</loading-button>
<link-back class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none">
Отменить
</link-back>
</div>
</form>
</div>
</template>
<script>
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import { ref, computed } from 'vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import FileInputMultiple from '@/Shared/Form/FileInputMultiple.vue'
import FileInput from '@/Shared/Form/FileInput.vue'
import TextareaInput from '@/Shared/Form/TextareaInput.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import LinkBack from '@/Shared/Misc/LinkBack.vue'
import TagInput from '@/Shared/Form/TagInput.vue'
import WarningText from '@/Shared/Misc/WarningText.vue'
export default {
components: {
MetaHead,
TextInput,
FileInput,
FileInputMultiple,
LoadingButton,
TextareaInput,
LinkBack,
TagInput,
WarningText,
},
layout: Layout,
setup() {
// let isFeedPaid = ref(0)
// let openPaidBlock = ref(0)
const form = useForm({
title: null,
body: null,
preview: null,
videos: null,
price: null,
videos_paid: null,
tags: [],
is_paid: 0,
})
// watch(isFeedPaid, (value) => {
// openPaidBlock.value = value
// form.is_paid = value
// })
const authUser = computed(() => usePage().props.value.auth.user)
const submit = () => {
form.post(route('videos.store'))
}
return { form, submit, authUser }
},
}
</script>

356
resources/js/Pages/Video/Edit.vue Executable file
View File

@@ -0,0 +1,356 @@
<template>
<meta-head title="Обновить видео"></meta-head>
<modal-warning
:feed_id="feed.id"
:open="modalShow"
@action="deleteActionModal"
/>
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 buttons-filter-line">
{{ feed.status }}
<div v-if="feed.status != 1" class="bg-indigo-200 shadow-classic rounded-md p-5 mb-10">
<p class="text-lg font-medium text-gray">
Контент запрещен к публикации, причина:
</p>
<p v-if="feed.status_note " class="text-gray-light">
<ul>
<li v-for="textBreak in textsBreak" :key="textBreak">
{{ textBreak }}
</li>
</ul>
</p>
<p v-else class="text-gray-light">
модерация созданного\обновленного контента
</p>
<p v-if="feed.status == 3" class="pt-3 text-green">
Обновления отправлены, ожидайте!
</p>
</div>
<form class="bg-indigo-200 shadow-classic rounded-md p-5" @submit.prevent="submit">
<div class="mb-4 flex items-center text-gray-light text-lg font-medium">
<link-back class="default block hover:underline">
Вернуться
</link-back>
<span class="px-3">/</span>
<h1 class="text-gray">
Редактирование видео
</h1>
</div>
<div class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.title" :error="form.errors.title"
type="text" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Название"
/>
<warning-text />
</div>
<div class="flex flex-col">
<textarea-input v-model="form.body" :error="form.errors.body"
class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md" cols="30"
rows="4" label="Описание"
/>
</div>
<div class="flex flex-col">
<file-input
v-model="form.preview"
accept="image/png, image/jpeg, image/jpg"
:error="form.errors.preview"
:label="textPreview()"
/>
</div>
<div v-if="mediaPreview" class="border border-indigo-300 p-4 rounded-md">
<button type="button"
class="flex-shrink-0 px-6 py-2 bg-pink focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:outline-none focus:border-transparent rounded-sm text-sm text-white" @click="removePreview(mediaPreview.id)"
>
Удалить старое превью
</button>
<div class="mt-4 flex-shrink-0 self-start overflow-hidden">
<img class="object-contain h-48" :src="mediaPreview.url"
alt=""
>
</div>
</div>
<div class="border border-indigo-300 flex flex-col p-4 rounded-md">
<file-input-multiple
v-model="form.videos"
accept=".mp4"
:error="form.errors.videos"
label="Загрузить новое видео"
/>
<div>
<h2 class="mt-3 mb-2 text-lg text-gray-light select-none">
Загруженные видео:
</h2>
<div class="grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
<created-media-item
v-for="mediaCommon in mediasCommon"
:key="mediaCommon.id" :type="feed.type"
:media="mediaCommon"
@addRemoveId="stateToggle"
/>
</div>
</div>
</div>
<div class="text-gray-light">
<TagInput v-model="form.tags" />
</div>
<div v-if="isFreeFeed">
<div class="text-gray-light text-lg mb-2">
Тип контента
</div>
<div class="flex flex-wrap gap-4">
<div class="flex items-center">
<input id="feed-paid-1" v-model="form.is_paid"
value="0" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-1" class="select-none ml-3 text-gray">Бесплатный</label>
</div>
<div class="flex items-center">
<input id="feed-paid-2" v-model="form.is_paid"
value="1" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-2" class="select-none ml-3 text-gray">Эксклюзивный</label>
</div>
<div v-if="authUser.private" class="flex items-center">
<input id="feed-paid-3" v-model="form.is_paid"
value="2" type="radio"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="feed-paid-3" class="select-none ml-3 text-gray">Доступен по личной подписке</label>
</div>
</div>
</div>
<!-- <div class="flex items-center">
<input id="is_adult_feed" v-model="form.is_adult"
type="checkbox"
class="h-5 w-5 text-orange border-gray-light focus:ring-transparent focus:ring-offset-transparent"
>
<label for="is_adult_feed" class="select-none ml-3 text-gray">Контент для взрослых</label>
</div> -->
<div v-if="form.is_paid == 1" class="space-y-5">
<div class="flex flex-col">
<text-input v-model="form.price" :error="form.errors.price"
type="number" class="focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
label="Цена"
/>
</div>
<div class="border border-indigo-300 flex flex-col p-4 rounded-md space-y-3">
<div v-if="isFreeFeed">
<file-input-multiple
v-model="form.videos_paid"
:error="form.errors.videos_paid"
accept=".mp4"
label="Выбрать видео"
/>
</div>
<div v-if="mediasPaid.length">
<h2 class="mb-2 text-lg text-gray-light select-none">
Загруженные платные видео:
</h2>
<div class="grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
<created-media-item
v-for="mediaPaid in mediasPaid" :key="mediaPaid.id"
:edit="false"
:type="feed.type"
:media="mediaPaid"
/>
</div>
</div>
</div>
</div>
</div>
<div class="mt-12 flex flex-wrap -my-1 -mx-3">
<progress
v-if="form.progress"
class="mx-3 my-1 w-full"
:value="form.progress.percentage"
max="100"
>
{{ form.progress.percentage }}%
</progress>
<loading-button :loading="form.processing" class="mx-3 my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-orange focus:outline-none"
type="submit"
>
Обновить
</loading-button>
<link-back class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none">
Отменить
</link-back>
<button type="button" class="mx-3 my-1 default text-lg text-pink"
@click="openModal"
>
Удалить
</button>
</div>
</form>
</div>
</template>
<script>
import find from 'lodash/find'
import { toRefs, watch, ref, computed } from 'vue'
import { useForm, usePage } from '@inertiajs/inertia-vue3'
import { Inertia } from '@inertiajs/inertia'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import FileInput from '@/Shared/Form/FileInput.vue'
import TextInput from '@/Shared/Form/TextInput.vue'
import FileInputMultiple from '@/Shared/Form/FileInputMultiple.vue'
import TextareaInput from '@/Shared/Form/TextareaInput.vue'
import TagInput from '@/Shared/Form/TagInput.vue'
import LoadingButton from '@/Shared/Form/LoadingButton.vue'
import LinkBack from '@/Shared/Misc/LinkBack.vue'
import WarningText from '@/Shared/Misc/WarningText.vue'
import CreatedMediaItem from '@/Shared/Edit/CreatedMediaItem.vue'
import ModalWarning from '@/Shared/Overlay/ModalWarning.vue'
export default {
components: {
MetaHead,
CreatedMediaItem,
TextInput,
FileInput,
FileInputMultiple,
LoadingButton,
WarningText,
TextareaInput,
LinkBack,
TagInput,
ModalWarning,
},
layout: Layout,
props: {
feed: Object,
tags: Array,
mediasCommon: Array,
mediasPaid: Array,
mediasCount: Number,
mediaPreview: Object,
},
setup(props) {
const { feed, tags, mediasCount, mediaPreview } = toRefs(props)
const textsBreak = computed(() => feed.value.status_note.split('\n'))
const modalShow = ref(false)
// let isFeedPaid, openPaidBlock
let form_fields = {
id: feed.value.id,
title: feed.value.title,
body: feed.value.body,
price: feed.value.price,
tags: tags.value,
is_paid: feed.value.is_paid,
preview: null,
videos: null,
videos_paid: null,
removedItems: [],
totalItems: mediasCount,
}
if (feed.value.is_paid) {
// isFeedPaid = ref(1)
// openPaidBlock = ref(1)
delete form_fields.videos_paid
}
// else {
// isFeedPaid = ref(0)
// openPaidBlock = ref(0)
// }
const form = useForm(form_fields)
// watch(isFeedPaid, (value) => {
// openPaidBlock.value = value
// form.is_paid = value
// })
const submit = () => {
form.post(route('videos.update', feed.value.id), {
onSuccess: () => {
form.reset('videos')
form.reset('videos_paid')
form.reset('preview')
},
})
}
const stateToggle = (id) => {
let existID = find(form.removedItems, (itemID) => itemID === id)
if (existID) {
const index = form.removedItems.indexOf(existID)
if (index > -1) {
form.removedItems.splice(index, 1)
}
} else {
form.removedItems.push(id)
}
}
const deleteActionModal = (remove) => {
if (remove) {
Inertia.delete(route('feed.destroy', feed.value.id), {
preserveScroll: true,
preserveState: true,
})
}
modalShow.value = false
}
const openModal = () => (modalShow.value = true)
const isFreeFeed = computed(() => feed.value.is_paid === 0)
const authUser = computed(() => usePage().props.value.auth.user)
const textPreview = () => {
if (mediaPreview) {
return 'Обновить превью'
}
return 'Загрузить превью'
}
const removePreview = (id) => {
Inertia.delete(route('feed.preview.destroy', id))
}
return {
form,
submit,
stateToggle,
authUser,
// isFeedPaid,
// openPaidBlock,
isFreeFeed,
modalShow,
deleteActionModal,
openModal,
textPreview,
removePreview,
textsBreak
}
},
updated() {
if (this.feed.is_paid) {
delete this.form.videos_paid
}
},
}
</script>

152
resources/js/Pages/Video/Feed.vue Executable file
View File

@@ -0,0 +1,152 @@
<template>
<meta-head title="Видео"></meta-head>
<div class="mb-6 py-3 banner relative bg-center bg-no-repeat bg-cover"
style="background-image: url('/image/bg-home.jpg');"
>
<div class="h-52 flex justify-center items-center text-center">
<div class="max-w-4xl text-gray px-3">
<h1
class=" text-xl md:text-2xl lg:text-3xl xl:text-5xl text-white font-semibold xl:leading-relaxed"
>
Видео
</h1>
<h2 v-if="tag" class="text-lg md:text-3xl text-gray-light">
#{{ tag.name }}
</h2>
<inertia-link :href="route('videos.create')"
class="mt-8 inline-flex tracking-wide items-center px-8 md:px-12 py-3 border border-white text-sm lg:text-lg text-white rounded-full bg-transparent hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2"
>
Загрузить
</inertia-link>
</div>
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 buttons-filter-line">
<div class="relative mb-5">
<div class="absolute inset-y-0 left-3 flex items-center z-[1]">
<svg class="flex-shrink-0 h-5 w-5 text-gray-light" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" fill="currentColor"
>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11 4a7 7 0 100 14 7 7 0 000-14zm-9 7a9 9 0 1118 0 9 9 0 01-18 0z"
/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M15.943 15.943a1 1 0 011.414 0l4.35 4.35a1 1 0 01-1.414 1.414l-4.35-4.35a1 1 0 010-1.414z"
/>
</svg>
</div>
<input
v-model="form.search"
class="relative w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light !pl-10 h-14" placeholder="Поиск"
type="search"
>
</div>
<div class="grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<inertia-link :href="local_route"
:class="[active_filter == 'new' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center rounded-md text-white focus:outline-none']"
>
Новинки
</inertia-link>
<inertia-link :href="local_route"
:data="{ filter: 'hot' }"
:class="[active_filter == 'hot' ? 'bg-orange shadow-classic2' : 'shadow-classic bg-indigo-200 hover:bg-orange' , 'transition inline-flex items-center px-3 py-3 lg:px-6 lg:py-6 xl:px-10 text-sm lg:text-lg justify-center shadow-classic rounded-md text-white focus:outline-none']"
>
<svg class="-ml-1 mr-2 h-4 w-4 md:h-5 md:w-5 flex-shrink-0" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
>
<path
d="M14.422 6.243c-3.57-2.172-1.895-5.238-1.824-5.365A.586.586 0 0012.09 0c-1.837 0-3.276.522-4.276 1.552-1.71 1.76-1.63 4.498-1.597 5.667.004.13.007.242.007.325 0 .868.14 1.67.263 2.377.079.456.147.85.16 1.159.012.331-.048.407-.05.41-.01.01-.081.049-.276.049a.657.657 0 01-.53-.235c-.565-.634-.604-2.365-.496-3.307a.586.586 0 00-.582-.654c-1.521 0-2.658 2.488-2.658 4.712 0 1.046.21 2.07.625 3.045a8.081 8.081 0 001.7 2.527C5.894 19.157 7.89 20 10 20c2.119 0 4.114-.83 5.618-2.34a7.892 7.892 0 002.327-5.605c0-2.692-2.107-4.95-3.523-5.812zM10 18.828c-3.671 0-6.773-3.101-6.773-6.773 0-.893.213-1.83.585-2.571.086-.173.175-.322.263-.447.01.967.163 2.284.841 3.046.366.41.852.627 1.405.627.518 0 .912-.156 1.172-.466.486-.578.337-1.436.148-2.524-.115-.66-.245-1.409-.245-2.176 0-.1-.003-.22-.007-.359-.032-1.107-.099-3.412 1.265-4.817.613-.63 1.467-1.014 2.55-1.145-.123.406-.228.92-.232 1.497-.01 1.27.476 3.085 2.841 4.524 1.145.696 2.96 2.618 2.96 4.811A6.78 6.78 0 0110 18.828z"
/>
</svg>
Популярные
</inertia-link>
</div>
<div v-if="tag" class="mt-4 grid gap-2 md:gap-5 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
<feed-tags-buttons :slug="tag.slug" />
</div>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3 my-6">
<button class="button-default text-gray text-lg" @click="resetFilter">
Очистить фильтры
</button>
</div>
<div class="xl:container xl:mx-auto px-2 md:px-3">
<div v-if="feeds.length" class="grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 cards-block rounded-md bg-indigo-200 shadow-classic grid gap-2 lg:gap-4 grid-cards p-2 lg:p-5">
<feed
:next-cursor="nextCursor"
:feeds="feeds"
/>
</div>
<div v-else class="font-bold text-xl lg:text-3xl text-gray text-center">
Записи не найдены
</div>
</div>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import throttle from 'lodash/throttle'
import pickBy from 'lodash/pickBy'
import Feed from '@/Shared/Feed/Feed.vue'
import Layout from '@/Shared/Layout.vue'
import MetaHead from '@/Shared/MetaHead.vue'
import FeedTagsButtons from '@/Shared/Misc/FeedTagsButtons.vue'
export default {
components: {
MetaHead,
FeedTagsButtons,
Feed,
},
layout: Layout,
props: {
feeds: Array,
local_route: String,
active_filter: String,
tag: { type: [Object, Boolean], default: false },
searchFilters: Object,
nextCursor: String,
},
data() {
return {
form: {
search: this.searchFilters.search,
},
}
},
watch: {
form: {
deep: true,
handler: throttle(function () {
Inertia.get(this.local_route, pickBy(this.form), {
preserveScroll: true, preserveState: true
})
}, 500),
},
},
methods: {
resetFilter() {
Inertia.get(
this.local_route,
{},
{
preserveScroll: true,
}
)
},
},
}
</script>

View File

@@ -0,0 +1,79 @@
<template>
<Head>
<title>{{feed.entity.title}}</title>
<meta name="description" :content="feed.entity.body">
<meta property="og:url" :content="route(`${feed.entity.type}.show`, feed.entity.slug)">
<meta property="og:type" content="website">
<meta property="og:title" :content="feed.entity.title">
<meta property="og:description" :content="feed.entity.body">
<meta property="og:image" :content="feed.entity.preview">
<meta name="twitter:card" content="summary_large_image">
<!-- <meta name="twitter:title" :content="feed.entity.title">
<meta name="twitter:description" :content="feed.entity.body">
<meta name="twitter:image" :content="feed.entity.preview">
<meta name="twitter:card" content="summary_large_image"> -->
</Head>
<header-auth v-if="!$page.props.auth.user" />
<modal-share :entity='feed.entity' ref="shareModalNode" />
<div class="mt-16 container mx-auto px-2 md:px-6 2xl:px-28 md:grid grid-cols-12 ">
<modal-feed-media
:type='feed.type'
:feed_id='feed.id'
:title='feed.entity.title'
:preview='feed.entity.preview'
:medias='feed.entity.collection_medias'
class="border-l border-r md:border-r-0 border-t border-b border-indigo-100"
/>
<modal-feed-body
@openShare='showShareModal'
:user='user'
:feed_id='feed.id'
:entity='feed.entity'
class="border-l border-r md:border-t border-b border-indigo-100"
/>
</div>
</template>
<script>
import { Head } from "@inertiajs/inertia-vue3";
import Layout from "@/Shared/Layout.vue";
import ModalFeedMedia from "@/Shared/Overlay/ModalFeedMedia.vue";
import ModalFeedBody from "@/Shared/Overlay/ModalFeedBody.vue";
import ModalShare from "@/Shared/Overlay/ModalShare.vue";
import HeaderAuth from "@/Shared/LayoutParts/HeaderAuth.vue";
export default {
layout: Layout,
components: {
Head,
ModalShare,
ModalFeedMedia,
ModalFeedBody,
HeaderAuth,
},
provide() {
return {
is_exist_menu: false
}
},
props: {
feed: Object,
user: Object,
},
methods: {
showShareModal() {
this.$refs.shareModalNode.openModal();
},
},
};
</script>