mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 17:01:16 +00:00
Update forum forntend
This commit is contained in:
@@ -23,7 +23,8 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'preset' => 'livewire-tailwind',
|
||||
//'preset' => 'livewire-tailwind',
|
||||
'preset' => 'blade-tailwind',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
+10
-5
@@ -11,20 +11,25 @@
|
||||
"@tailwindcss/forms": "^0.5.8",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.7",
|
||||
"axios": "^1.13.2",
|
||||
"lodash": "^4.17.21",
|
||||
"npm-check-updates": "^17.1.1",
|
||||
"postcss": "^8.4.44",
|
||||
"prettier": "^3.3.3",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"tailwindcss": "^3.4.18",
|
||||
"vite": "^7.0",
|
||||
"vue": "^3.4.38"
|
||||
"vue": "^3.5.24"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@melloware/coloris": "^0.24.0",
|
||||
"@melloware/coloris": "^0.24.2",
|
||||
"@simonwep/pickr": "^1.9.1",
|
||||
"alpinejs": "^3.15.2",
|
||||
"date-fns": "^3.6.0",
|
||||
"feather-icons": "^4.29.2",
|
||||
"laravel-echo": "^2.2.6",
|
||||
"laravel-vite-plugin": "^2.0",
|
||||
"nested-sort": "^5.2.0"
|
||||
"nested-sort": "^5.2.0",
|
||||
"vuedraggable": "^4.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
.forum {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-variation-settings: "wdth" 100;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-4xl my-6;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-xl;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-blue-500;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
@apply text-blue-800;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import feather from 'feather-icons';
|
||||
import { createApp, ref, reactive, watch } from 'vue/dist/vue.esm-bundler.js';
|
||||
import axios from 'axios';
|
||||
import Pickr from '@simonwep/pickr';
|
||||
import draggable from 'vuedraggable/src/vuedraggable';
|
||||
|
||||
import '@simonwep/pickr/dist/themes/classic.min.css';
|
||||
|
||||
window.axios = axios;
|
||||
window.Vue = { createApp, ref, reactive, watch };
|
||||
window.VueDraggable = draggable;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
createApp({
|
||||
setup() {
|
||||
const isCollapsed = ref(true);
|
||||
const isUserDropdownCollapsed = ref(true);
|
||||
|
||||
window.addEventListener('click', event => {
|
||||
const ignore = ['navbar-toggler', 'navbar-toggler-icon', 'dropdown-toggle'];
|
||||
if (ignore.some(className => event.target.classList.contains(className))) return;
|
||||
if (!isCollapsed.value) isCollapsed.value = true;
|
||||
if (!isUserDropdownCollapsed.value) isUserDropdownCollapsed.value = true;
|
||||
});
|
||||
|
||||
return {
|
||||
isCollapsed,
|
||||
isUserDropdownCollapsed,
|
||||
};
|
||||
}
|
||||
}).mount('.v-navbar');
|
||||
|
||||
function findModal(key)
|
||||
{
|
||||
const modal = document.querySelector(`[data-modal=${key}]`);
|
||||
|
||||
if (!modal) throw `Attempted to open modal '${key}' but no such modal found.`;
|
||||
|
||||
return modal;
|
||||
}
|
||||
|
||||
function openModal(modal)
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
}, 200);
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-open-modal]').forEach(item =>
|
||||
{
|
||||
item.addEventListener('click', event =>
|
||||
{
|
||||
event.preventDefault();
|
||||
|
||||
openModal(findModal(event.currentTarget.dataset.openModal));
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-close-modal]').forEach(modalClose =>
|
||||
{
|
||||
modalClose.addEventListener('click', event =>
|
||||
{
|
||||
event.preventDefault
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
modalClose.closest('[data-modal]').classList.remove('flex');
|
||||
modalClose.closest('[data-modal]').classList.add('hidden');
|
||||
}, 200);
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-dismiss]').forEach(item =>
|
||||
{
|
||||
item.addEventListener('click', event => event.currentTarget.parentElement.style.display = 'none');
|
||||
});
|
||||
|
||||
const hash = window.location.hash.substr(1);
|
||||
if (hash.startsWith('modal='))
|
||||
{
|
||||
openModal(findModal(hash.replace('modal=','')));
|
||||
}
|
||||
|
||||
feather.replace();
|
||||
|
||||
const input = document.querySelector('input[name=color_light_mode]');
|
||||
|
||||
if (!input) return;
|
||||
|
||||
const pickr = Pickr.create({
|
||||
el: '.pickr',
|
||||
theme: 'classic',
|
||||
default: input.value || null,
|
||||
|
||||
swatches: [
|
||||
window.defaultCategoryColor,
|
||||
'#f44336',
|
||||
'#e91e63',
|
||||
'#9c27b0',
|
||||
'#673ab7',
|
||||
'#3f51b5',
|
||||
'#2196f3',
|
||||
'#03a9f4',
|
||||
'#00bcd4',
|
||||
'#009688',
|
||||
'#4caf50',
|
||||
'#8bc34a',
|
||||
'#cddc39',
|
||||
'#ffeb3b',
|
||||
'#ffc107'
|
||||
],
|
||||
|
||||
components: {
|
||||
preview: true,
|
||||
hue: true,
|
||||
interaction: {
|
||||
input: true,
|
||||
save: true
|
||||
}
|
||||
},
|
||||
|
||||
strings: {
|
||||
save: 'Apply'
|
||||
}
|
||||
});
|
||||
|
||||
pickr.on('save', instance => pickr.hide())
|
||||
.on('clear', instance =>
|
||||
{
|
||||
input.value = '';
|
||||
input.dispatchEvent(new Event('change'));
|
||||
})
|
||||
.on('cancel', instance =>
|
||||
{
|
||||
const selectedColor = instance
|
||||
.getSelectedColor()
|
||||
.toHEXA()
|
||||
.toString();
|
||||
|
||||
input.value = selectedColor;
|
||||
input.dispatchEvent(new Event('change'));
|
||||
})
|
||||
.on('change', (color, instance) =>
|
||||
{
|
||||
const selectedColor = color
|
||||
.toHEXA()
|
||||
.toString();
|
||||
|
||||
input.value = selectedColor;
|
||||
input.dispatchEvent(new Event('change'));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
{{-- $category is passed as NULL to the master layout view to prevent it from showing in the breadcrumbs --}}
|
||||
@extends ('forum::layouts.main', ['category' => null])
|
||||
|
||||
@section ('content')
|
||||
<div class="flex flex-row justify-between my-3">
|
||||
<h2 class="grow text-3xl font-medium">{{ trans('forum::general.index') }}</h2>
|
||||
|
||||
@can ('createCategories')
|
||||
<x-forum::button type="button" data-open-modal="create-category">
|
||||
{{ trans('forum::categories.create') }}
|
||||
</x-forum::button>
|
||||
|
||||
@include ('forum::category.modals.create')
|
||||
@endcan
|
||||
</div>
|
||||
|
||||
@foreach ($categories as $category)
|
||||
@include ('forum::category.partials.list', ['titleClass' => 'lead'])
|
||||
@endforeach
|
||||
@stop
|
||||
@@ -0,0 +1,120 @@
|
||||
@extends ('forum::layouts.main', ['category' => null, 'thread' => null, 'breadcrumbs_append' => [trans('forum::general.manage')]])
|
||||
|
||||
@section ('content')
|
||||
<div class="flex flex-row justify-between mb-2">
|
||||
<h2 class="text-3xl font-medium my-3">{{ trans('forum::general.manage') }}</h2>
|
||||
|
||||
@can ('createCategories')
|
||||
<x-forum::button type="button" data-open-modal="create-category" class="px-6">
|
||||
{{ trans('forum::categories.create') }}
|
||||
</x-forum::button>
|
||||
|
||||
@include ('forum::category.modals.create')
|
||||
@endcan
|
||||
</div>
|
||||
|
||||
<div id="manage-categories">
|
||||
<draggable-category-list :categories="state.categories"></draggable-category-list>
|
||||
|
||||
<transition name="fade">
|
||||
<div v-show="state.changesApplied" class="bg-green-100 mb-4 text-green-700 mt-3 px-4 py-3" role="alert">
|
||||
{{ trans('forum::general.changes_applied') }}
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<div class="flex justify-end py-3">
|
||||
<button type="button" class="bg-blue-500 text-white rounded py-2 px-8 hover:cursor-pointer disabled:opacity-50" :disabled="state.isSavingDisabled" @click="onSave">
|
||||
{{ trans('forum::general.save') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/x-template" id="draggable-category-list-template">
|
||||
<draggable
|
||||
:list="categories"
|
||||
tag="ul"
|
||||
class="cursor-move"
|
||||
@start="drag=true"
|
||||
@end="drag=false"
|
||||
:group="{ name: 'categories' }"
|
||||
:empty-insert-threshold="50"
|
||||
item-key="id">
|
||||
<template #item="{element}">
|
||||
<li class="bg-white p-4 my-2 rounded-md border" :data-id="element.id">
|
||||
<span class="flex">
|
||||
<span class="grow">
|
||||
<strong :style="{ color: element.color }">@{{ element.title }}</strong>
|
||||
<div class="text-muted">@{{ element.description }}</div>
|
||||
</span>
|
||||
<span>
|
||||
<a class="text-blue-500 hover:text-blue-800 px-3 py-2 text-sm ml-2" :href="element.route + '#modal=edit-category'">{{ trans('forum::general.edit') }}</a>
|
||||
<a class="bg-red-500 hover:bg-red-400 text-white hover:text-white px-3 py-2 text-sm rounded ml-2" :href="element.route + '#modal=delete-category'">{{ trans('forum::general.delete') }}</a>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span v-if="element.children.length == 0" class="block min-h-5 border border-dashed border-slate-400 rounded">
|
||||
<draggable-category-list :categories="element.children" />
|
||||
</span>
|
||||
<draggable-category-list v-else :categories="element.children" />
|
||||
</li>
|
||||
</template>
|
||||
</draggable>
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
const app = Vue.createApp({
|
||||
setup() {
|
||||
const state = Vue.reactive({
|
||||
categories: @json($categories),
|
||||
isSavingDisabled: true,
|
||||
changesApplied: false,
|
||||
});
|
||||
|
||||
Vue.watch(
|
||||
() => state.categories,
|
||||
async (newValue, oldValue) => {
|
||||
state.isSavingDisabled = false;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
function onSave()
|
||||
{
|
||||
state.isSavingDisabled = true;
|
||||
state.changesApplied = false;
|
||||
|
||||
var payload = { categories: state.categories };
|
||||
axios.post('{{ route('forum.bulk.category.manage') }}', payload)
|
||||
.then(response => {
|
||||
state.changesApplied = true;
|
||||
setTimeout(() => state.changesApplied = false, 3000);
|
||||
})
|
||||
.catch(error => {
|
||||
state.isSavingDisabled = false;
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
onSave
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
app.component(
|
||||
'Draggable',
|
||||
VueDraggable
|
||||
);
|
||||
|
||||
app.component(
|
||||
'DraggableCategoryList',
|
||||
{
|
||||
props: ['categories'],
|
||||
template: '#draggable-category-list-template',
|
||||
}
|
||||
);
|
||||
|
||||
app.mount('#manage-categories');
|
||||
</script>
|
||||
@stop
|
||||
@@ -0,0 +1,31 @@
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'create-category')
|
||||
@slot('title', trans('forum::categories.create'))
|
||||
@slot('route', Forum::route('category.store'))
|
||||
|
||||
<div class="mb-3">
|
||||
<x-forum::label for="title">{{ trans('forum::general.title') }}</x-forum::label>
|
||||
<x-forum::input type="text" name="title" value="{{ old('title') }}" class="w-full" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<x-forum::label for="description">{{ trans('forum::general.description') }}</x-forum::label>
|
||||
<x-forum::input type="text" name="description" value="{{ old('description') }}" class="w-full" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="accepts_threads" id="accepts-threads" value="1" {{ old('accepts_threads') ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="accepts-threads">{{ trans('forum::categories.enable_threads') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div>
|
||||
<input type="checkbox" name="is_private" id="is-private" value="1" {{ old('is_private') ? 'checked' : '' }}>
|
||||
<label for="is-private">{{ trans('forum::categories.make_private') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@include ('forum::category.partials.inputs.color')
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.create') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@@ -0,0 +1,21 @@
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'delete-category')
|
||||
@slot('title', trans('forum::general.delete'))
|
||||
@slot('route', Forum::route('category.delete', $category))
|
||||
@slot('method', 'DELETE')
|
||||
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
|
||||
@if(!$category->isEmpty())
|
||||
<div class="form-check mt-3">
|
||||
<input class="form-check-input" type="checkbox" value="1" name="force" id="forceDelete">
|
||||
<label class="form-check-label" for="forceDelete">
|
||||
{{ trans('forum::categories.confirm_nonempty_delete') }}
|
||||
</label>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit" class="bg-red-500 hover:bg-red-400">{{ trans('forum::general.delete') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@@ -0,0 +1,40 @@
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'edit-category')
|
||||
@slot('title', trans('forum::general.edit'))
|
||||
@slot('route', Forum::route('category.update', $category))
|
||||
@slot('method', 'PATCH')
|
||||
|
||||
<div class="mb-3">
|
||||
<x-forum::label for="title">{{ trans('forum::general.title') }}</x-forum::label>
|
||||
<x-forum::input type="text" name="title" value="{{ old('title') ?? $category->title }}" class="w-full" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<x-forum::label for="description">{{ trans('forum::general.description') }}</x-forum::label>
|
||||
<x-forum::input type="text" name="description" value="{{ old('description') ?? $category->description }}" class="w-full" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input type="hidden" name="accepts_threads" value="0" />
|
||||
<input class="form-check-input" type="checkbox" name="accepts_threads" id="accepts-threads" value="1" {{ $category->accepts_threads ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="accepts-threads">{{ trans('forum::categories.enable_threads') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input type="hidden" name="is_private" value="0" />
|
||||
<input class="form-check-input" type="checkbox" name="is_private" id="is-private" value="1" {{ $category->is_private ? 'checked' : '' }} {{ $privateAncestor != null ? 'disabled' : '' }}>
|
||||
<label class="form-check-label" for="is-private">{{ trans('forum::categories.make_private') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@if ($privateAncestor != null)
|
||||
<div class="alert alert-primary" role="alert">
|
||||
{!! trans('forum::categories.access_controlled_by_private_ancestor', ['category' => "<a href=\"{$privateAncestor->route}\">{$privateAncestor->title}</a>"]) !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@include ('forum::category.partials.inputs.color')
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.save') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@@ -0,0 +1,16 @@
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'mark-threads-as-read')
|
||||
@slot('title', trans('forum::categories.mark_read'))
|
||||
@slot('route', Forum::route('unread.mark-as-read'))
|
||||
@slot('method', 'PATCH')
|
||||
|
||||
<input type="hidden" name="category_id" value="{{ $category->id }}" />
|
||||
|
||||
<p>{{ trans('forum::general.generic_confirm') }}</p>
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">
|
||||
{{ trans('forum::general.mark_read') }}
|
||||
</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="color_light_mode">{{ trans('forum::general.color') }}</label>
|
||||
<div class="pickr"></div>
|
||||
<input type="hidden"
|
||||
value="{{ isset($category->color_light_mode) ? $category->color_light_mode : (old('color_light_mode') ?? config('forum.frontend.default_category_color')) }}"
|
||||
name="color_light_mode" />
|
||||
</div>
|
||||
@@ -0,0 +1,75 @@
|
||||
<div class="my-4">
|
||||
<div class="bg-white shadow rounded-md relative">
|
||||
<div class="flex flex-col md:items-start md:flex-row md:justify-between md:gap-4 p-6">
|
||||
<div class="md:w-3/6 text-center md:text-left">
|
||||
<h5 class="text-lg">
|
||||
<a href="{{ Forum::route('category.show', $category) }}" style="color: {{ $category->color_light_mode }};">{{ $category->title }}</a>
|
||||
</h5>
|
||||
<p class="text-gray-500">{{ $category->description }}</p>
|
||||
</div>
|
||||
<div class="md:w-1/6 flex flex-col items-center gap-1 mt-2 md:mt-0">
|
||||
@if ($category->accepts_threads)
|
||||
<x-forum::badge style="background: {{ $category->color_light_mode }};">
|
||||
{{ trans_choice('forum::threads.thread', 2) }}: {{ $category->thread_count }}
|
||||
</x-forum::badge>
|
||||
<x-forum::badge style="background: {{ $category->color_light_mode }};">
|
||||
{{ trans_choice('forum::posts.post', 2) }}: {{ $category->post_count }}
|
||||
</x-forum::badge>
|
||||
@endif
|
||||
</div>
|
||||
<div class="md:w-2/6 text-gray-500 text-center md:text-right mt-2 md:mt-0">
|
||||
@if ($category->accepts_threads)
|
||||
@if ($category->newestThread)
|
||||
<div>
|
||||
<a href="{{ Forum::route('thread.show', $category->newestThread) }}" class="mr-1">{{ $category->newestThread->title }}</a>
|
||||
@include ('forum::partials.timestamp', ['carbon' => $category->newestThread->created_at])
|
||||
</div>
|
||||
@endif
|
||||
@if ($category->latestActiveThread && $category->latestActiveThread->post_count > 1)
|
||||
<div>
|
||||
<a href="{{ Forum::route('thread.show', $category->latestActiveThread->lastPost) }}" class="mr-1">Re: {{ $category->latestActiveThread->title }}</a>
|
||||
@include ('forum::partials.timestamp', ['carbon' => $category->latestActiveThread->lastPost->created_at])
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($category->children->count() > 0)
|
||||
<div class="subcategories">
|
||||
@foreach ($category->children as $subcategory)
|
||||
<div class="bg-white -mt-1 shadow rounded-b-md">
|
||||
<div class="flex flex-col md:items-start md:flex-row md:justify-between md:gap-4 p-6">
|
||||
<div class="md:w-3/6 text-center md:text-left">
|
||||
<a href="{{ Forum::route('category.show', $subcategory) }}" style="color: {{ $subcategory->color_light_mode }};">{{ $subcategory->title }}</a>
|
||||
<div class="text-muted">{{ $subcategory->description }}</div>
|
||||
</div>
|
||||
<div class="md:w-1/6 flex flex-col items-center gap-1 mt-2 md:mt-0">
|
||||
<x-forum::badge style="background: {{ $subcategory->color_light_mode }};">
|
||||
{{ trans_choice('forum::threads.thread', 2) }}: {{ $subcategory->thread_count }}
|
||||
</x-forum::badge>
|
||||
<x-forum::badge style="background: {{ $subcategory->color_light_mode }};">
|
||||
{{ trans_choice('forum::posts.post', 2) }}: {{ $subcategory->post_count }}
|
||||
</x-forum::badge>
|
||||
</div>
|
||||
<div class="md:w-2/6 text-gray-500 md:items-end text-center md:text-right mt-2 md:mt-0">
|
||||
@if ($subcategory->newestThread)
|
||||
<div>
|
||||
<a href="{{ Forum::route('thread.show', $subcategory->newestThread) }}" class="mr-1">{{ $subcategory->newestThread->title }}</a>
|
||||
@include ('forum::partials.timestamp', ['carbon' => $subcategory->newestThread->created_at])
|
||||
</div>
|
||||
@endif
|
||||
@if ($subcategory->latestActiveThread && $subcategory->latestActiveThread->post_count > 1)
|
||||
<div>
|
||||
<a href="{{ Forum::route('thread.show', $subcategory->latestActiveThread->lastPost) }}" class="mr-1">Re: {{ $subcategory->latestActiveThread->title }}</a>
|
||||
@include ('forum::partials.timestamp', ['carbon' => $subcategory->latestActiveThread->lastPost->created_at])
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
@foreach ($categories as $cat)
|
||||
@if (!isset($hide) || (isset($hide) && $cat->id != $hide->id))
|
||||
<option value="{{ $cat->id }}" {{ (isset($category) && $cat->id == $category->id) ? 'selected' : '' }}>
|
||||
@for ($i = 0; $i < $cat->depth; $i++)- @endfor
|
||||
{{ $cat->title }}
|
||||
</option>
|
||||
@endif
|
||||
|
||||
@if ($cat->children)
|
||||
@include ('forum::category.partials.options', ['categories' => $cat->children])
|
||||
@endif
|
||||
@endforeach
|
||||
@@ -0,0 +1,248 @@
|
||||
{{-- $thread is passed as NULL to the master layout view to prevent it from showing in the breadcrumbs --}}
|
||||
@extends('forum::layouts.main', ['thread' => null])
|
||||
|
||||
@section('content')
|
||||
<div class="flex flex-row justify-between mb-2">
|
||||
<h2 class="text-3xl" style="color: {{ $category->color_light_mode }};">
|
||||
{{ $category->title }}
|
||||
@if ($category->description)
|
||||
<small>{{ $category->description }}</small>
|
||||
@endif
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div id="category">
|
||||
<div class="flex justify-between flex-row-reverse">
|
||||
@if ($category->accepts_threads)
|
||||
@can ('createThreads', $category)
|
||||
<x-forum::button-link href="{{ Forum::route('thread.create', $category) }}" class="float-end">{{ trans('forum::threads.new_thread') }}</x-forum::button-link>
|
||||
@endcan
|
||||
@endif
|
||||
|
||||
<x-forum::button-group>
|
||||
@can ('editCategories')
|
||||
@can ('edit', $category)
|
||||
<x-forum::button-secondary type="button" data-open-modal="edit-category">
|
||||
{{ trans('forum::general.edit') }}
|
||||
</x-forum::button-secondary>
|
||||
@endcan
|
||||
@endcan
|
||||
</x-forum::button-group>
|
||||
</div>
|
||||
|
||||
@if (!$category->children->isEmpty())
|
||||
@foreach ($category->children as $subcategory)
|
||||
@include ('forum::category.partials.list', ['category' => $subcategory])
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
@if ($category->accepts_threads)
|
||||
@if (!$threads->isEmpty())
|
||||
<div class="mt-4">
|
||||
{{ $threads->links('forum::pagination') }}
|
||||
</div>
|
||||
|
||||
@if (count($selectableThreadIds) > 0)
|
||||
@can ('manageThreads', $category)
|
||||
<form :action="actions[state.selectedAction]" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" name="_method" :value="actionMethods[state.selectedAction]" />
|
||||
|
||||
<div class="text-end mt-2">
|
||||
<div class="form-check">
|
||||
<label for="selectAllThreads">
|
||||
{{ trans('forum::threads.select_all') }}
|
||||
</label>
|
||||
<input type="checkbox" value="" id="selectAllThreads" class="align-middle" @click="toggleAll" :checked="state.selectedThreads.length == selectableThreadIds.length">
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
@endif
|
||||
|
||||
<div class="threads list-group my-3 shadow-sm">
|
||||
@foreach ($threads as $thread)
|
||||
@include ('forum::thread.partials.list')
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if (count($selectableThreadIds) > 0)
|
||||
@can ('manageThreads', $category)
|
||||
<div class="fixed bottom-0 right-0 m-2" style="z-index: 1000;" v-if="state.selectedThreads.length">
|
||||
<div class="bg-white shadow-sm rounded-md min-w-96 max-w-full">
|
||||
<div class="border-b text-center py-4 px-6">
|
||||
{{ trans('forum::general.with_selection') }}
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="mb-3">
|
||||
<div>
|
||||
<x-forum::label for="bulk-actions">{{ trans_choice('forum::general.actions', 1) }}</x-forum::label>
|
||||
</div>
|
||||
|
||||
<x-forum::select id="bulk-actions" v-model="state.selectedAction">
|
||||
@can ('deleteThreads', $category)
|
||||
<option value="delete">{{ trans('forum::general.delete') }}</option>
|
||||
@endcan
|
||||
@can ('restoreThreads', $category)
|
||||
<option value="restore">{{ trans('forum::general.restore') }}</option>
|
||||
@endcan
|
||||
@can ('moveThreadsFrom', $category)
|
||||
<option value="move">{{ trans('forum::general.move') }}</option>
|
||||
@endcan
|
||||
@can ('lockThreads', $category)
|
||||
<option value="lock">{{ trans('forum::threads.lock') }}</option>
|
||||
<option value="unlock">{{ trans('forum::threads.unlock') }}</option>
|
||||
@endcan
|
||||
@can ('pinThreads', $category)
|
||||
<option value="pin">{{ trans('forum::threads.pin') }}</option>
|
||||
<option value="unpin">{{ trans('forum::threads.unpin') }}</option>
|
||||
@endcan
|
||||
</x-forum::select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" v-if="state.selectedAction == 'move'">
|
||||
<div>
|
||||
<label for="category-id">{{ trans_choice('forum::categories.category', 1) }}</label>
|
||||
</div>
|
||||
<x-forum::select name="category_id" id="category-id">
|
||||
@include ('forum::category.partials.options', [
|
||||
'categories' => $threadDestinationCategories,
|
||||
'hide' => $category
|
||||
])
|
||||
</x-forum::select>
|
||||
</div>
|
||||
|
||||
@if (config('forum.general.soft_deletes'))
|
||||
<div class="mb-3" v-if="state.selectedAction == 'delete'">
|
||||
<input class="form-check-input" type="checkbox" name="permadelete" value="1" id="permadelete">
|
||||
<label class="form-check-label" for="permadelete">
|
||||
{{ trans('forum::general.perma_delete') }}
|
||||
</label>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="text-end">
|
||||
<button type="submit" class="bg-blue-500 text-white rounded-md px-3 py-1" @click="submit" :disabled="state.selectedAction == null">{{ trans('forum::general.proceed') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endcan
|
||||
@endif
|
||||
@else
|
||||
<div class="card my-3">
|
||||
<div class="card-body">
|
||||
{{ trans('forum::threads.none_found') }}
|
||||
@can ('createThreads', $category)
|
||||
<br>
|
||||
<a href="{{ Forum::route('thread.create', $category) }}">{{ trans('forum::threads.post_the_first') }}</a>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col col-xs-8">
|
||||
{{ $threads->links('forum::pagination') }}
|
||||
</div>
|
||||
<div class="col col-xs-4 text-end">
|
||||
@if ($category->accepts_threads)
|
||||
@can ('createThreads', $category)
|
||||
<x-forum::button-link href="{{ Forum::route('thread.create', $category) }}">{{ trans('forum::threads.new_thread') }}</x-forum::button-link>
|
||||
@endcan
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if (!$threads->isEmpty())
|
||||
@can ('markThreadsAsRead')
|
||||
<div class="text-center mt-3">
|
||||
<x-forum::button class="inline-flex px-6 items-center gap-2" data-open-modal="mark-threads-as-read">
|
||||
<i data-feather="book"></i> {{ trans('forum::general.mark_read') }}
|
||||
</x-forum::button>
|
||||
</div>
|
||||
|
||||
@include ('forum::category.modals.mark-threads-as-read')
|
||||
@endcan
|
||||
@endif
|
||||
|
||||
@can ('editCategories')
|
||||
@can ('edit', $category)
|
||||
@include ('forum::category.modals.edit')
|
||||
@endcan
|
||||
@endcan
|
||||
@can ('deleteCategories')
|
||||
@can ('delete', $category)
|
||||
@include ('forum::category.modals.delete')
|
||||
@endcan
|
||||
@endcan
|
||||
|
||||
<script type="module">
|
||||
Vue.createApp({
|
||||
setup() {
|
||||
const selectableThreadIds = @json($selectableThreadIds);
|
||||
|
||||
const actions = {
|
||||
delete: "{{ Forum::route('bulk.thread.delete') }}",
|
||||
restore: "{{ Forum::route('bulk.thread.restore') }}",
|
||||
lock: "{{ Forum::route('bulk.thread.lock') }}",
|
||||
unlock: "{{ Forum::route('bulk.thread.unlock') }}",
|
||||
pin: "{{ Forum::route('bulk.thread.pin') }}",
|
||||
unpin: "{{ Forum::route('bulk.thread.unpin') }}",
|
||||
move: "{{ Forum::route('bulk.thread.move') }}"
|
||||
};
|
||||
|
||||
const actionMethods = {
|
||||
delete: 'DELETE',
|
||||
restore: 'POST',
|
||||
lock: 'POST',
|
||||
unlock: 'POST',
|
||||
pin: 'POST',
|
||||
unpin: 'POST',
|
||||
move: 'POST'
|
||||
};
|
||||
|
||||
const state = Vue.reactive({
|
||||
selectedAction: null,
|
||||
selectedThreads: [],
|
||||
isEditModalOpen: false,
|
||||
isDeleteModalOpen: false
|
||||
});
|
||||
|
||||
function toggleAll()
|
||||
{
|
||||
state.selectedThreads = (state.selectedThreads.length < selectableThreadIds.length) ? selectableThreadIds : [];
|
||||
}
|
||||
|
||||
function submit(event)
|
||||
{
|
||||
if (actionMethods[state.selectedAction] === 'DELETE' && !confirm("{{ trans('forum::general.generic_confirm') }}"))
|
||||
{
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function onClickModal(event)
|
||||
{
|
||||
if (event.target.classList.contains('modal'))
|
||||
{
|
||||
state.isEditModalOpen = false;
|
||||
state.isDeleteModalOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
selectableThreadIds,
|
||||
actions,
|
||||
actionMethods,
|
||||
state,
|
||||
toggleAll,
|
||||
submit,
|
||||
onClickModal,
|
||||
};
|
||||
}
|
||||
}).mount('#category');
|
||||
</script>
|
||||
@stop
|
||||
@@ -0,0 +1,27 @@
|
||||
@props([
|
||||
'type' => 'default'
|
||||
])
|
||||
|
||||
@php
|
||||
switch($type) {
|
||||
case('info'):
|
||||
$color = 'bg-blue-500';
|
||||
break;
|
||||
|
||||
case('danger'):
|
||||
$color = 'bg-red-500';
|
||||
break;
|
||||
|
||||
case('warning'):
|
||||
$color = 'bg-orange-500';
|
||||
break;
|
||||
|
||||
default:
|
||||
$color = 'bg-gray-400';
|
||||
break;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<span {{ $attributes->merge(['class' => "$color rounded-full px-2 py-1 text-white text-xs font-semibold"]) }}>
|
||||
{{ $slot }}
|
||||
</span>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="flex flex-wrap items-center [&_a:first-child]:rounded-r-none [&_a:not(:first-child):not(:last-child)]:rounded-none [&_a:last-child]:rounded-l-none" role="group">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<a {{ $attributes->merge(['class' => 'bg-blue-500 hover:bg-blue-400 text-white font-semibold hover:text-white px-3 py-2 rounded-md inline-block']) }}>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
@@ -0,0 +1,3 @@
|
||||
<button {{ $attributes->merge(['class' => 'bg-gray-300 hover:bg-gray-200 text-gray-600 font-semibold px-3 py-2 rounded-md inline-block']) }}>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@@ -0,0 +1,3 @@
|
||||
<button {{ $attributes->merge(['class' => 'bg-blue-500 hover:bg-blue-400 text-white font-semibold px-3 py-2 rounded-md inline-block']) }}>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@@ -0,0 +1 @@
|
||||
<input {{ $attributes->merge(['type' => 'text', 'class' => 'px-3 py-1 border-gray-300 focus:border-blue-500 focus:ring-blue-500 rounded-md border shadow-sm']) }}>
|
||||
@@ -0,0 +1,3 @@
|
||||
<label {{ $attributes->merge(['class' => 'block w-full mb-1']) }}>
|
||||
{{ $slot }}
|
||||
</label>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="inline-block relative w-64">
|
||||
<select {{ $attributes->merge(['class' => 'block appearance-none w-full bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline']) }}>
|
||||
{{ $slot }}
|
||||
</select>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
<textarea {{ $attributes->merge(['type' => 'text', 'class' => 'px-3 py-1 border-gray-300 focus:border-blue-500 focus:ring-blue-500 rounded-md border shadow-sm']) }}>{{ $slot }}</textarea>
|
||||
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>
|
||||
@if (isset($thread_title))
|
||||
{{ $thread_title }} —
|
||||
@endif
|
||||
@if (isset($category))
|
||||
{{ $category->title }} —
|
||||
@endif
|
||||
{{ trans('forum::general.home_title') }}
|
||||
</title>
|
||||
|
||||
@vite(['resources/forum/blade-tailwind/css/forum.css', 'resources/forum/blade-tailwind/js/forum.js'])
|
||||
</head>
|
||||
<body class="forum bg-gray-100">
|
||||
<nav class="v-navbar bg-white shadow py-4">
|
||||
<div class="container mx-auto px-4 md:flex md:items-center md:gap-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<a class="text-lg font-semibold" href="{{ url(config('forum.frontend.router.prefix')) }}">Laravel Forum</a>
|
||||
<button class="navbar-toggler block md:hidden border rounded-md px-2 py-1" type="button" :class="{ collapsed: isCollapsed }" @click="isCollapsed = !isCollapsed">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="navbar-toggler-icon w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grow justify-between navbar-collapse" :class="{ 'flex flex-col': !isCollapsed, 'hidden md:flex': isCollapsed }">
|
||||
<ul class="flex flex-col md:flex-row gap-3 mb-4 md:mb-0">
|
||||
<li>
|
||||
<a class="text-gray-500 hover:text-gray-800" href="{{ route('home') }}">Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="text-gray-500 hover:text-gray-800" href="{{ url(config('forum.frontend.router.prefix')) }}">{{ trans('forum::general.index') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="text-gray-500 hover:text-gray-800" href="{{ route('forum.recent') }}">{{ trans('forum::threads.recent') }}</a>
|
||||
</li>
|
||||
@auth
|
||||
<li>
|
||||
<a class="text-gray-500 hover:text-gray-800" href="{{ route('forum.unread') }}">{{ trans('forum::threads.unread_updated') }}</a>
|
||||
</li>
|
||||
@endauth
|
||||
@can ('moveCategories')
|
||||
<li>
|
||||
<a class="text-gray-500 hover:text-gray-800" href="{{ route('forum.category.manage') }}">{{ trans('forum::general.manage') }}</a>
|
||||
</li>
|
||||
@endcan
|
||||
</ul>
|
||||
<ul class="navbar-nav flex gap-4 flex-col md:flex-row">
|
||||
@if (Auth::check())
|
||||
<li class="nav-item dropdown relative">
|
||||
<a class="dropdown-toggle text-gray-500 flex items-center gap-1" href="#" id="navbarDropdownMenuLink" @click="isUserDropdownCollapsed = !isUserDropdownCollapsed">
|
||||
{{ $username }}
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-3 h-3">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
||||
</svg>
|
||||
</a>
|
||||
<div class="border absolute left-0 bg-white rounded-md w-44 divide-y" :class="{ hidden: isUserDropdownCollapsed }" aria-labelledby="navbarDropdownMenuLink">
|
||||
<a class="block px-4 py-2" href="{{ url('/logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
|
||||
Log out
|
||||
</a>
|
||||
<form id="logout-form" action="{{ url('/logout') }}" method="POST" style="display: none;">
|
||||
@csrf
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
@else
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url('/login') }}">Log in</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url('/register') }}">Register</a>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="main" class="container mx-auto p-4">
|
||||
@include ('forum::partials.breadcrumbs')
|
||||
@include ('forum::partials.alerts')
|
||||
|
||||
@yield('content')
|
||||
</div>
|
||||
|
||||
@yield('footer')
|
||||
|
||||
<script>
|
||||
window.defaultCategoryColor = '{{ config('forum.frontend.default_category_color') }}';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
<div tabindex="-1" role="dialog" data-modal="{{ $key }}" class="fixed top-0 left-0 w-full h-full hidden z-10 items-center justify-center">
|
||||
<div class="fixed top-0 left-0 w-full h-full bg-black/25" data-close-modal></div>
|
||||
<div class="relative bg-white rounded-md max-w-screen-sm w-full m-2" role="document">
|
||||
<div class="">
|
||||
<div class="border-b px-6 py-4 flex justify-between">
|
||||
<h5 class="text-xl font-medium flex items-center gap-1">{!!$title !!}</h5>
|
||||
<button type="button" aria-label="Close" data-close-modal>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<form action="{{ $route }}" method="POST">
|
||||
@csrf
|
||||
@if (isset($method))
|
||||
@method($method)
|
||||
@endif
|
||||
|
||||
<div class="p-6">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
<div class="border-t px-6 py-4 flex justify-end gap-4">
|
||||
<x-forum::button-secondary type="button" class="btn btn-secondary" data-close-modal>{{ trans('forum::general.cancel') }}</x-forum::button-secondary>
|
||||
{{ $actions }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,46 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||
<span class="page-link" aria-hidden="true">‹</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||
@else
|
||||
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||
<span class="page-link" aria-hidden="true">›</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
@@ -0,0 +1,18 @@
|
||||
@php
|
||||
$colorClasses = match ($type) {
|
||||
'primary', '', null => 'bg-blue-100 text-blue-700',
|
||||
'success', '', null => 'bg-green-100 text-green-700',
|
||||
'danger' => 'bg-orange-100 text-orange-700'
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div class="alert alert-{{ $type }} alert-dismissable rounded-md my-4 p-4 flex justify-between gap-4 {{ $colorClasses }}">
|
||||
<div class="message">
|
||||
{!! $message !!}
|
||||
</div>
|
||||
<button type="button" data-dismiss="alert" aria-hidden="true">
|
||||
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
@if (Session::has('alerts'))
|
||||
@foreach (Session::get('alerts') as $alert)
|
||||
@include ('forum::partials.alert', $alert)
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
@if (isset($errors) && !$errors->isEmpty())
|
||||
@foreach ($errors->all() as $error)
|
||||
@include ('forum::partials.alert', ['type' => 'danger', 'message' => $error])
|
||||
@endforeach
|
||||
@endif
|
||||
@@ -0,0 +1,4 @@
|
||||
@if ($category->parent !== null)
|
||||
@include ('forum::partials.breadcrumb-categories', ['category' => $category->parent])
|
||||
@endif
|
||||
<li class=""><a href="{{ Forum::route('category.show', $category) }}" class="text-blue-500">{{ $category->title }}</a></li>
|
||||
@@ -0,0 +1,16 @@
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="flex flex-wrap [&_li]:after:content-['/'] [&_li]:after:px-2 [&_li]:after:text-gray-500 [&_li:last-child]:after:content-['']">
|
||||
<li class=""><a href="{{ url(config('forum.frontend.router.prefix')) }}" class="text-blue-500">{{ trans('forum::general.index') }}</a></li>
|
||||
@if (isset($category) && $category)
|
||||
@include ('forum::partials.breadcrumb-categories', ['category' => $category])
|
||||
@endif
|
||||
@if (isset($thread) && $thread)
|
||||
<li class=""><a href="{{ Forum::route('thread.show', $thread) }}" class="text-blue-500">{{ $thread->title }}</a></li>
|
||||
@endif
|
||||
@if (isset($breadcrumbs_append) && count($breadcrumbs_append) > 0)
|
||||
@foreach ($breadcrumbs_append as $breadcrumb)
|
||||
<li class="">{{ $breadcrumb }}</li>
|
||||
@endforeach
|
||||
@endif
|
||||
</ol>
|
||||
</nav>
|
||||
@@ -0,0 +1 @@
|
||||
<time class="timestamp" datetime="{{ $carbon }}" title="{{ $carbon->toDayDateTimeString() }}">{{ $carbon->diffForHumans() }}</time>
|
||||
@@ -0,0 +1,37 @@
|
||||
@extends ('forum::layouts.main', ['breadcrumbs_append' => [trans_choice('forum::posts.delete', 1)]])
|
||||
|
||||
@section ('content')
|
||||
<div id="delete-post">
|
||||
<h2 class="text-3xl">{{ trans_choice('forum::posts.delete', 1) }}</h2>
|
||||
|
||||
<hr>
|
||||
|
||||
@include ('forum::post.partials.list', ['post' => $post, 'single' => true])
|
||||
|
||||
<form method="POST" action="{{ Forum::route('post.delete', $post) }}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
|
||||
<div class="bg-white border rounded-md mb-3">
|
||||
<div class="p-4">
|
||||
|
||||
@if (config('forum.general.soft_deletes'))
|
||||
<div class="form-check" v-if="state.selectedPostAction == 'delete'">
|
||||
<input class="form-check-input" type="checkbox" name="permadelete" value="1" id="permadelete">
|
||||
<label class="form-check-label" for="permadelete">
|
||||
{{ trans('forum::general.perma_delete') }}
|
||||
</label>
|
||||
</div>
|
||||
@else
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end items-center gap-2">
|
||||
<x-forum::button-link href="{{ URL::previous() }}">{{ trans('forum::general.cancel') }}</x-forum::button-link>
|
||||
<x-forum::button type="submit" class="bg-red-500 px-5">{{ trans('forum::general.delete') }}</x-forum::button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@stop
|
||||
@@ -0,0 +1,29 @@
|
||||
@extends ('forum::layouts.main', ['breadcrumbs_append' => [trans_choice('forum::posts.restore', 1)]])
|
||||
|
||||
@section ('content')
|
||||
<div id="delete-post">
|
||||
<h2 class="flex-grow-1">{{ trans_choice('forum::posts.restore', 1) }}</h2>
|
||||
|
||||
<hr>
|
||||
|
||||
@include ('forum::post.partials.list', ['post' => $post, 'single' => true])
|
||||
|
||||
<form method="POST" action="{{ Forum::route('post.restore', $post) }}">
|
||||
@csrf
|
||||
@method('POST')
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-end">
|
||||
<a href="{{ URL::previous() }}" class="btn btn-link">{{ trans('forum::general.cancel') }}</a>
|
||||
<x-forum::button type="submit" class="px-5">
|
||||
{{ trans('forum::general.restore') }}
|
||||
</x-forum::button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@stop
|
||||
@@ -0,0 +1,32 @@
|
||||
@extends ('forum::layouts.main', ['breadcrumbs_append' => [trans('forum::general.new_reply')]])
|
||||
|
||||
@section ('content')
|
||||
<div id="create-post">
|
||||
<h2 class="text-3xl font-medium my-3">{{ trans('forum::general.new_reply') }} ({{ $thread->title }})</h2>
|
||||
|
||||
@if ($post !== null && !$post->trashed())
|
||||
<div class="mb-2">{{ trans('forum::general.replying_to', ['item' => $post->authorName]) }}:</div>
|
||||
|
||||
@include ('forum::post.partials.quote')
|
||||
@endif
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<form method="POST" action="{{ Forum::route('post.store', $thread) }}">
|
||||
{!! csrf_field() !!}
|
||||
|
||||
@if ($post !== null)
|
||||
<input type="hidden" name="post" value="{{ $post->id }}" />
|
||||
@endif
|
||||
|
||||
<div class="mb-3">
|
||||
<x-forum::textarea name="content" class="w-full min-h-48">{{ old('content') }}</x-forum::textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end items-center gap-4">
|
||||
<a href="{{ URL::previous() }}" class="text-blue-500 underline">{{ trans('forum::general.cancel') }}</a>
|
||||
<x-forum::button type="submit" class="">{{ trans('forum::general.reply') }}</x-forum::button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@stop
|
||||
@@ -0,0 +1,29 @@
|
||||
@extends ('forum::layouts.main', ['breadcrumbs_append' => [trans('forum::posts.edit')]])
|
||||
|
||||
@section ('content')
|
||||
<div id="edit-post">
|
||||
<h2 class="text-3xl font-medium my-3">{{ trans('forum::posts.edit') }} ({{ $thread->title }})</h2>
|
||||
|
||||
<hr class="mb-4">
|
||||
|
||||
@if ($post->parent)
|
||||
<h3>{{ trans('forum::general.response_to', ['item' => $post->parent->authorName]) }}...</h3>
|
||||
|
||||
@include ('forum::post.partials.list', ['post' => $post->parent, 'single' => true])
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ Forum::route('post.update', $post) }}">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
|
||||
<div class="mb-3">
|
||||
<x-forum::textarea name="content" class="w-full">{{ old('content') !== null ? old('content') : $post->content }}</x-forum::textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 justify-end">
|
||||
<a href="{{ URL::previous() }}" class="underline text-blue-500">{{ trans('forum::general.cancel') }}</a>
|
||||
<x-forum::button type="submit">{{ trans('forum::general.save') }}</x-forum::button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@stop
|
||||
@@ -0,0 +1,23 @@
|
||||
<div class="panel panel-default" data-actions>
|
||||
<div class="panel-heading">
|
||||
<span class="glyphicon glyphicon-option-vertical"></span>
|
||||
<a href="#" data-toggle="collapse" data-target=".collapse.post-actions">{{ trans('forum::posts.actions') }}</a>
|
||||
</div>
|
||||
<div class="collapse post-actions">
|
||||
<div class="panel-body">
|
||||
<div class="mb-3">
|
||||
<label for="action">{{ trans_choice('forum::general.actions', 1) }}</label>
|
||||
<select name="action" id="action" class="form-control">
|
||||
@can ('deletePosts', $post->thread)
|
||||
@can ('delete', $post)
|
||||
<option value="delete" data-confirm="true" data-method="delete">{{ trans('forum::general.delete') }}</option>
|
||||
@endcan
|
||||
@endcan
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-footer clearfix">
|
||||
<button type="submit" class="btn btn-default pull-right">{{ trans('forum::general.proceed') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,70 @@
|
||||
<div @if (!$post->trashed())id="post-{{ $post->sequence }}"@endif
|
||||
class="bg-white border mb-2 rounded-md {{ $post->trashed() || $thread->trashed() ? 'opacity-50' : '' }}"
|
||||
:class="{ 'border-blue-500': state.selectedPosts.includes({{ $post->id }}) }">
|
||||
<div class="bg-gray-100 border-b px-6 py-4 flex justify-between flex-row-reverse rounded-t-md">
|
||||
@if (!isset($single) || !$single)
|
||||
<span class="float-end">
|
||||
<a href="{{ Forum::route('thread.show', $post) }}" class="text-blue-500">#{{ $post->sequence }}</a>
|
||||
@if ($post->sequence != 1)
|
||||
@can ('deletePosts', $post->thread)
|
||||
@can ('delete', $post)
|
||||
<input type="checkbox" name="posts[]" :value="{{ $post->id }}" v-model="state.selectedPosts" class="ml-2" />
|
||||
@endcan
|
||||
@endcan
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<div>
|
||||
{{ $post->authorName }}
|
||||
<span class="text-gray-500">
|
||||
@include ('forum::partials.timestamp', ['carbon' => $post->created_at])
|
||||
@if ($post->hasBeenUpdated())
|
||||
({{ trans('forum::general.last_updated') }} @include ('forum::partials.timestamp', ['carbon' => $post->updated_at]))
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
@if ($post->parent !== null)
|
||||
@include ('forum::post.partials.quote', ['post' => $post->parent])
|
||||
@endif
|
||||
|
||||
@if ($post->trashed())
|
||||
@can ('viewTrashedPosts')
|
||||
{!! Forum::render($post->content) !!}
|
||||
<br>
|
||||
@endcan
|
||||
<x-forum::badge type="danger">{{ trans('forum::general.deleted') }}</x-forum::badge>
|
||||
@else
|
||||
{!! Forum::render($post->content) !!}
|
||||
@endif
|
||||
|
||||
@if (!isset($single) || !$single)
|
||||
<div class="flex items-center gap-4 justify-end mt-4">
|
||||
@if (!$post->trashed())
|
||||
<a href="{{ Forum::route('post.show', $post) }}" class="text-gray-500 hover:text-gray-800">{{ trans('forum::general.permalink') }}</a>
|
||||
@if ($post->sequence != 1)
|
||||
@can ('deletePosts', $post->thread)
|
||||
@can ('delete', $post)
|
||||
<a href="{{ Forum::route('post.confirm-delete', $post) }}" class="text-red-500 hover:text-red-800">{{ trans('forum::general.delete') }}</a>
|
||||
@endcan
|
||||
@endcan
|
||||
@endif
|
||||
@can ('edit', $post)
|
||||
<a href="{{ Forum::route('post.edit', $post) }}" class="text-blue-500">{{ trans('forum::general.edit') }}</a>
|
||||
@endcan
|
||||
@can ('reply', $post->thread)
|
||||
<a href="{{ Forum::route('post.create', $post) }}" class="text-blue-500">{{ trans('forum::general.reply') }}</a>
|
||||
@endcan
|
||||
@else
|
||||
@can ('restorePosts', $post->thread)
|
||||
@can ('restore', $post)
|
||||
<a href="{{ Forum::route('post.confirm-restore', $post) }}" class="card-link">{{ trans('forum::general.restore') }}</a>
|
||||
@endcan
|
||||
@endcan
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
<div class="bg-white rounded-md border mb-2">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between flex-row-reverse mb-2">
|
||||
<span>
|
||||
<a href="{{ Forum::route('thread.show', $post) }}" class="text-gray-500">#{{ $post->sequence }}</a>
|
||||
</span>
|
||||
<div>
|
||||
<strong>{{ $post->authorName }}</strong> <span class="text-gray-500">{{ $post->posted }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{!! \Illuminate\Support\Str::limit(Forum::render($post->content)) !!}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
@extends ('forum::layouts.main', ['breadcrumbs_append' => [trans('forum::posts.view')]])
|
||||
|
||||
@section ('content')
|
||||
<div id="post">
|
||||
<div class="flex flex-row justify-between mb-3">
|
||||
<h2 class="grow">{{ trans('forum::posts.view') }} ({{ $thread->title }})</h2>
|
||||
<x-forum::button-link href="{{ Forum::route('thread.show', $thread) }}">{{ trans('forum::threads.view') }}</x-forum::button-link>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
@include ('forum::post.partials.list', ['post' => $post, 'single' => true])
|
||||
</div>
|
||||
@stop
|
||||
@@ -0,0 +1,25 @@
|
||||
@extends ('forum::layouts.main', ['breadcrumbs_append' => [trans('forum::threads.new_thread')]])
|
||||
|
||||
@section ('content')
|
||||
<div id="create-thread">
|
||||
<h2 class="text-3xl">{{ trans('forum::threads.new_thread') }} ({{ $category->title }})</h2>
|
||||
|
||||
<form method="POST" action="{{ Forum::route('thread.store', $category) }}">
|
||||
@csrf
|
||||
|
||||
<div class="mb-3">
|
||||
<x-forum::label for="title">{{ trans('forum::general.title') }}</x-forum::label>
|
||||
<x-forum::input type="text" name="title" value="{{ old('title') }}" class="w-full" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<x-forum::textarea name="content" class="w-full">{{ old('content') }}</x-forum::textarea>
|
||||
</div>
|
||||
|
||||
<div class="text-end">
|
||||
<x-forum::button-link href="{{ URL::previous() }}" class="bg-gray-500">{{ trans('forum::general.cancel') }}</x-forum::button-link>
|
||||
<x-forum::button type="submit">{{ trans('forum::general.create') }}</x-forum::button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@stop
|
||||
@@ -0,0 +1,14 @@
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'mark-as-read')
|
||||
@slot('title', trans('forum::general.mark_read'))
|
||||
@slot('route', Forum::route('unread.mark-as-read'))
|
||||
@slot('method', 'PATCH')
|
||||
|
||||
<p>{{ trans('forum::general.generic_confirm') }}</p>
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">
|
||||
{{ trans('forum::general.mark_read') }}
|
||||
</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@@ -0,0 +1,52 @@
|
||||
<div class="bg-white" :class="{ 'ring-1 ring-blue-500': state.selectedThreads.includes({{ $thread->id }}) }">
|
||||
<div class="flex flex-col md:items-start md:flex-row md:justify-between md:gap-4 p-6">
|
||||
<div class="md:w-3/6 text-center md:text-left">
|
||||
<span class="lead">
|
||||
<a href="{{ Forum::route('thread.show', $thread) }}" @if (isset($category))style="color: {{ $category->color_light_mode }};"@endif class="text-lg">{{ $thread->title }}</a>
|
||||
</span>
|
||||
<br>
|
||||
{{ $thread->authorName }}
|
||||
<span class="text-gray-500"> @include ('forum::partials.timestamp', ['carbon' => $thread->created_at])</span>
|
||||
|
||||
@if (!isset($category))
|
||||
<br>
|
||||
<a href="{{ Forum::route('category.show', $thread->category) }}" style="color: {{ $thread->category->color_light_mode }};">{{ $thread->category->title }}</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="md:w-2/6 flex flex-wrap justify-center items-center md:justify-end gap-1">
|
||||
@if ($thread->pinned)
|
||||
<x-forum::badge type="info">{{ trans('forum::threads.pinned') }}</x-forum::badge>
|
||||
@endif
|
||||
@if ($thread->locked)
|
||||
<x-forum::badge type="warning">{{ trans('forum::threads.locked') }}</x-forum::badge>
|
||||
@endif
|
||||
@if ($thread->userReadStatus !== null && !$thread->trashed())
|
||||
<x-forum::badge type="success">{{ trans($thread->userReadStatus) }}</x-forum::badge>
|
||||
@endif
|
||||
@if ($thread->trashed())
|
||||
<x-forum::badge type="danger">{{ trans('forum::general.deleted') }}</x-forum::badge>
|
||||
@endif
|
||||
<x-forum::badge :style="(isset($category) && $category->color_light_mode) ? 'background: '.$category->color_light_mode .';' : null">
|
||||
{{ trans('forum::general.replies') }}:
|
||||
{{ $thread->reply_count }}
|
||||
</x-forum::badge>
|
||||
</div>
|
||||
|
||||
@if ($thread->lastPost)
|
||||
<div class="md:w-1/6 flex justify-center md:flex-col md:items-end">
|
||||
<a href="{{ Forum::route('thread.show', $thread->lastPost) }}" class="text-blue-500">{{ trans('forum::posts.view') }} »</a>
|
||||
<div>
|
||||
{{ $thread->lastPost->authorName }}
|
||||
<span class="text-gray-500"> @include ('forum::partials.timestamp', ['carbon' => $thread->lastPost->created_at])</span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (isset($category) && isset($selectableThreadIds) && in_array($thread->id, $selectableThreadIds))
|
||||
<div class="" style="flex: 0;">
|
||||
<input type="checkbox" name="threads[]" :value="{{ $thread->id }}" v-model="state.selectedThreads">
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
@extends ('forum::layouts.main', ['thread' => null, 'breadcrumbs_append' => [trans('forum::threads.recent')]])
|
||||
|
||||
@section ('content')
|
||||
<div id="new-posts">
|
||||
<h2 class="text-3xl font-medium my-3">{{ trans('forum::threads.recent') }}</h2>
|
||||
|
||||
@if (!$threads->isEmpty())
|
||||
<div class="my-3">
|
||||
@foreach ($threads as $thread)
|
||||
@include ('forum::thread.partials.list')
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-white my-3">
|
||||
<div class="card-body text-center text-muted">
|
||||
{{ trans('forum::threads.none_found') }}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@stop
|
||||
@@ -0,0 +1,391 @@
|
||||
@extends ('forum::layouts.main', ['thread' => null, 'breadcrumbs_append' => [$thread->title], 'thread_title' => $thread->title])
|
||||
|
||||
@section ('content')
|
||||
<div id="thread">
|
||||
<div class="flex flex-col md:flex-row justify-between my-4">
|
||||
<h2 class="grow text-3xl font-semibold">{{ $thread->title }}</h2>
|
||||
|
||||
<div class="flex flex-col md:flex-row items-center gap-2">
|
||||
@if (Gate::allows('deleteThreads', $thread->category) && Gate::allows('delete', $thread))
|
||||
@if ($thread->trashed())
|
||||
<x-forum::button-link href="#" class="bg-red-500 hover:bg-red-400" data-open-modal="perma-delete-thread">
|
||||
<i data-feather="trash"></i> {{ trans('forum::general.perma_delete') }}
|
||||
</x-forum::button-link>
|
||||
@else
|
||||
<x-forum::button-link href="#" class="bg-red-500 hover:bg-red-400 inline-flex items-center gap-2" data-open-modal="delete-thread">
|
||||
<i data-feather="trash" class="w-4"></i> {{ trans('forum::general.delete') }}
|
||||
</x-forum::button-link>
|
||||
@endif
|
||||
@endif
|
||||
@if ($thread->trashed() && Gate::allows('restoreThreads', $thread->category) && Gate::allows('restore', $thread))
|
||||
<x-forum::button-link href="#" data-open-modal="restore-thread" class="inline-flex items-center gap-2">
|
||||
<i data-feather="refresh-cw" class="w-4"></i> {{ trans('forum::general.restore') }}
|
||||
</x-forum::button-link>
|
||||
@endif
|
||||
|
||||
@if (Gate::allows('lockThreads', $category)
|
||||
|| Gate::allows('pinThreads', $category)
|
||||
|| Gate::allows('rename', $thread)
|
||||
|| Gate::allows('moveThreadsFrom', $category))
|
||||
<x-forum::button-group>
|
||||
@if (!$thread->trashed())
|
||||
@can ('lockThreads', $category)
|
||||
@if ($thread->locked)
|
||||
<x-forum::button-link href="#" data-open-modal="unlock-thread" class="inline-flex items-center gap-2 bg-gray-500 hover:bg-gray-400">
|
||||
<i data-feather="unlock" class="w-4"></i> {{ trans('forum::threads.unlock') }}
|
||||
</x-forum::button-link>
|
||||
@else
|
||||
<x-forum::button-link href="#" data-open-modal="lock-thread" class="inline-flex items-center gap-2 bg-gray-500 hover:bg-gray-400">
|
||||
<i data-feather="lock" class="w-4"></i> {{ trans('forum::threads.lock') }}
|
||||
</x-forum::button-link>
|
||||
@endif
|
||||
@endcan
|
||||
@can ('pinThreads', $category)
|
||||
@if ($thread->pinned)
|
||||
<x-forum::button-link href="#" data-open-modal="unpin-thread" class="inline-flex items-center gap-2 bg-gray-500 hover:bg-gray-400">
|
||||
<i data-feather="arrow-down"></i> {{ trans('forum::threads.unpin') }}
|
||||
</x-forum::button-link>
|
||||
@else
|
||||
<x-forum::button-link href="#" data-open-modal="pin-thread" class="inline-flex items-center gap-2 bg-gray-500 hover:bg-gray-400">
|
||||
<i data-feather="arrow-up" class="w-4"></i> {{ trans('forum::threads.pin') }}
|
||||
</x-forum::button-link>
|
||||
@endif
|
||||
@endcan
|
||||
@can ('rename', $thread)
|
||||
<x-forum::button-link href="#" data-open-modal="rename-thread" class="inline-flex items-center gap-2 bg-gray-500 hover:bg-gray-400">
|
||||
<i data-feather="edit-2" class="w-4"></i> {{ trans('forum::general.rename') }}
|
||||
</x-forum::button-link>
|
||||
@endcan
|
||||
@can ('moveThreadsFrom', $category)
|
||||
<x-forum::button-link href="#" data-open-modal="move-thread" class="inline-flex items-center gap-2 bg-gray-500 hover:bg-gray-400">
|
||||
<i data-feather="corner-up-right" class="w-4"></i> {{ trans('forum::general.move') }}
|
||||
</x-forum::button-link>
|
||||
@endcan
|
||||
@endif
|
||||
</x-forum::button-group>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-x-1">
|
||||
@if ($thread->trashed())
|
||||
<x-forum::badge type="danger">{{ trans('forum::general.deleted') }}</x-forum::badge>
|
||||
@endif
|
||||
@if ($thread->pinned)
|
||||
<x-forum::badge type="info">{{ trans('forum::threads.pinned') }}</x-forum::badge>
|
||||
@endif
|
||||
@if ($thread->locked)
|
||||
<x-forum::badge type="warning">{{ trans('forum::threads.locked') }}</x-forum::badge>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ((count($posts) > 1 || $posts->currentPage() > 1) && (Gate::allows('deletePosts', $thread) || Gate::allows('restorePosts', $thread)) && count($selectablePosts) > 0)
|
||||
<form :action="postActions[state.selectedPostAction]" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" name="_method" :value="postActionMethods[state.selectedPostAction]" />
|
||||
@endif
|
||||
|
||||
<div class="my-4">
|
||||
<div>
|
||||
{{ $posts->links('forum::pagination') }}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
@if (!$thread->trashed())
|
||||
@can ('reply', $thread)
|
||||
<x-forum::button-group>
|
||||
<x-forum::button-link href="{{ Forum::route('post.create', $thread) }}">
|
||||
{{ trans('forum::general.new_reply') }}
|
||||
</x-forum::button-link>
|
||||
<x-forum::button-link href="#quick-reply">
|
||||
{{ trans('forum::general.quick_reply') }}
|
||||
</x-forum::button-link>
|
||||
</x-forum::button-group>
|
||||
@endcan
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ((count($posts) > 1 || $posts->currentPage() > 1) && (Gate::allows('deletePosts', $thread) || Gate::allows('restorePosts', $thread)) && count($selectablePosts) > 0)
|
||||
<div class="text-end mb-2">
|
||||
<div class="form-check">
|
||||
<label for="selectAllPosts">
|
||||
{{ trans('forum::posts.select_all') }}
|
||||
</label>
|
||||
<input type="checkbox" value="" id="selectAllPosts" class="align-middle" @click="toggleAll" :checked="state.selectedPosts.length == posts.data.length">
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@foreach ($posts as $post)
|
||||
@include ('forum::post.partials.list', compact('post'))
|
||||
@endforeach
|
||||
|
||||
@if ((count($posts) > 1 || $posts->currentPage() > 1) && (Gate::allows('deletePosts', $thread) || Gate::allows('restorePosts', $thread)) && count($selectablePosts) > 0)
|
||||
<div class="fixed bottom-0 right-0 m-2" style="z-index: 1000;" v-if="state.selectedPosts.length">
|
||||
<div class="bg-white shadow-sm rounded-md min-w-96 max-w-full">
|
||||
<div class="border-b text-center py-4 px-6">
|
||||
{{ trans('forum::general.with_selection') }}
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="mb-3">
|
||||
<div>
|
||||
<x-forum::label for="bulk-actions">{{ trans_choice('forum::general.actions', 1) }}</x-forum::label>
|
||||
</div>
|
||||
|
||||
<x-forum::select id="bulk-actions" v-model="state.selectedPostAction">
|
||||
<option value="delete">{{ trans('forum::general.delete') }}</option>
|
||||
<option value="restore">{{ trans('forum::general.restore') }}</option>
|
||||
</x-forum::select>
|
||||
</div>
|
||||
|
||||
@if (config('forum.general.soft_deletes'))
|
||||
<div class="form-check mb-3" v-if="state.selectedPostAction == 'delete'">
|
||||
<input class="form-check-input" type="checkbox" name="permadelete" value="1" id="permadelete">
|
||||
<label class="form-check-label" for="permadelete">
|
||||
{{ trans('forum::general.perma_delete') }}
|
||||
</label>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="text-end">
|
||||
<x-forum::button type="submit" class="px-5" @click="submitPosts">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
{{ $posts->links('forum::pagination') }}
|
||||
|
||||
@if (!$thread->trashed())
|
||||
@can ('reply', $thread)
|
||||
<h2>{{ trans('forum::general.quick_reply') }}</h2>
|
||||
<div id="quick-reply" class="mt-4">
|
||||
<form method="POST" action="{{ Forum::route('post.store', $thread) }}">
|
||||
@csrf
|
||||
|
||||
<div class="mb-3">
|
||||
<x-forum::textarea name="content" class="w-full min-h-48">{{ old('content') }}</x-forum::textarea>
|
||||
</div>
|
||||
|
||||
<div class="text-end">
|
||||
<x-forum::button type="submit" class="px-5">{{ trans('forum::general.reply') }}</x-forum::button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endcan
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($thread->trashed() && Gate::allows('restoreThreads', $thread->category) && Gate::allows('restore', $thread))
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'restore-thread')
|
||||
@slot('title', '<i data-feather="refresh-cw" class="text-gray-500"></i>' . trans('forum::general.restore'))
|
||||
@slot('route', Forum::route('thread.restore', $thread))
|
||||
@slot('method', 'POST')
|
||||
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@endif
|
||||
|
||||
@if (Gate::allows('deleteThreads', $thread->category) && Gate::allows('delete', $thread))
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'delete-thread')
|
||||
@slot('title', '<i data-feather="trash" class="text-gray-500"></i>' . trans('forum::threads.delete'))
|
||||
@slot('route', Forum::route('thread.delete', $thread))
|
||||
@slot('method', 'DELETE')
|
||||
|
||||
@if (config('forum.general.soft_deletes'))
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="permadelete" value="1" id="permadelete">
|
||||
<label class="form-check-label" for="permadelete">
|
||||
{{ trans('forum::general.perma_delete') }}
|
||||
</label>
|
||||
</div>
|
||||
@else
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
@endif
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
|
||||
@if (config('forum.general.soft_deletes'))
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'perma-delete-thread')
|
||||
@slot('title', '<i data-feather="trash" class="text-gray-500"></i>' . trans_choice('forum::threads.perma_delete', 1))
|
||||
@slot('route', Forum::route('thread.delete', $thread))
|
||||
@slot('method', 'DELETE')
|
||||
|
||||
<input type="hidden" name="permadelete" value="1" />
|
||||
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if (!$thread->trashed())
|
||||
@can ('lockThreads', $category)
|
||||
@if ($thread->locked)
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'unlock-thread')
|
||||
@slot('title', '<i data-feather="unlock" class="text-gray-500"></i> ' . trans('forum::threads.unlock'))
|
||||
@slot('route', Forum::route('thread.unlock', $thread))
|
||||
@slot('method', 'POST')
|
||||
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@else
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'lock-thread')
|
||||
@slot('title', '<i data-feather="lock" class="text-gray-500"></i> ' . trans('forum::threads.lock'))
|
||||
@slot('route', Forum::route('thread.lock', $thread))
|
||||
@slot('method', 'POST')
|
||||
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@endif
|
||||
@endcan
|
||||
|
||||
@can ('pinThreads', $category)
|
||||
@if ($thread->pinned)
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'unpin-thread')
|
||||
@slot('title', '<i data-feather="arrow-down" class="text-gray-500"></i> ' . trans('forum::threads.unpin'))
|
||||
@slot('route', Forum::route('thread.unpin', $thread))
|
||||
@slot('method', 'POST')
|
||||
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@else
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'pin-thread')
|
||||
@slot('title', '<i data-feather="arrow-up" class="text-gray-500"></i> ' . trans('forum::threads.pin'))
|
||||
@slot('route', Forum::route('thread.pin', $thread))
|
||||
@slot('method', 'POST')
|
||||
|
||||
{{ trans('forum::general.generic_confirm') }}
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@endif
|
||||
@endcan
|
||||
|
||||
@can ('rename', $thread)
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'rename-thread')
|
||||
@slot('title', '<i data-feather="edit-2" class="text-gray-500"></i> ' . trans('forum::general.rename'))
|
||||
@slot('route', Forum::route('thread.rename', $thread))
|
||||
@slot('method', 'POST')
|
||||
|
||||
<div>
|
||||
<x-forum::label for="new-title">{{ trans('forum::general.title') }}</x-forum::label>
|
||||
<x-forum::input type="text" name="title" value="{{ $thread->title }}" class="w-full" />
|
||||
</div>
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@endcan
|
||||
|
||||
@can ('moveThreadsFrom', $category)
|
||||
@component('forum::modal-form')
|
||||
@slot('key', 'move-thread')
|
||||
@slot('title', '<i data-feather="corner-up-right" class="text-gray-500"></i> ' . trans('forum::general.move'))
|
||||
@slot('route', Forum::route('thread.move', $thread))
|
||||
@slot('method', 'POST')
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<label class="input-group-text" for="category-id">{{ trans_choice('forum::categories.category', 1) }}</label>
|
||||
</div>
|
||||
<select name="category_id" id="category-id" class="form-select">
|
||||
@include ('forum::category.partials.options', ['hide' => $thread->category])
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@slot('actions')
|
||||
<x-forum::button type="submit">{{ trans('forum::general.proceed') }}</x-forum::button>
|
||||
@endslot
|
||||
@endcomponent
|
||||
@endcan
|
||||
@endif
|
||||
|
||||
<script type="module">
|
||||
Vue.createApp({
|
||||
setup() {
|
||||
let posts = @json($posts);
|
||||
posts.data = posts.data.filter(post => post.sequence > 1);
|
||||
|
||||
const selectablePosts = @json($selectablePosts);
|
||||
const postActions = {
|
||||
delete: "{{ Forum::route('bulk.post.delete') }}",
|
||||
restore: "{{ Forum::route('bulk.post.restore') }}"
|
||||
};
|
||||
const postActionMethods = {
|
||||
delete: 'DELETE',
|
||||
restore: 'POST',
|
||||
};
|
||||
|
||||
const state = Vue.reactive({
|
||||
selectedPostAction: 'delete',
|
||||
selectedPosts: [],
|
||||
selectedThreadAction: null,
|
||||
});
|
||||
|
||||
function toggleAll() {
|
||||
state.selectedPosts = (state.selectedPosts.length < selectablePosts.length) ? selectablePosts : [];
|
||||
}
|
||||
|
||||
function submitThread(event) {
|
||||
if (threadActionMethods[state.selectedThreadAction] === 'DELETE' && !confirm("{{ trans('forum::general.generic_confirm') }}"))
|
||||
{
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function submitPosts(event) {
|
||||
if (postActionMethods[state.selectedPostAction] === 'DELETE' && !confirm("{{ trans('forum::general.generic_confirm') }}")) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
posts,
|
||||
selectablePosts,
|
||||
postActions,
|
||||
postActionMethods,
|
||||
state,
|
||||
toggleAll,
|
||||
submitThread,
|
||||
submitPosts,
|
||||
};
|
||||
}
|
||||
}).mount('#thread');
|
||||
</script>
|
||||
@stop
|
||||
@@ -0,0 +1,31 @@
|
||||
@extends ('forum::layouts.main', ['thread' => null, 'breadcrumbs_append' => [trans('forum::threads.unread_updated')]])
|
||||
|
||||
@section ('content')
|
||||
<div id="new-posts">
|
||||
<h2 class="text-3xl text-medium my-4">{{ trans('forum::threads.unread_updated') }}</h2>
|
||||
|
||||
@if (!$threads->isEmpty())
|
||||
<div class="">
|
||||
@foreach ($threads as $thread)
|
||||
@include ('forum::thread.partials.list')
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-white shadow rounded text-gray-500 text-center py-4">
|
||||
{{ trans('forum::threads.none_found') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if (!$threads->isEmpty())
|
||||
@can ('markThreadsAsRead')
|
||||
<div class="flex justify-center mt-4">
|
||||
<x-forum::button class="px-5 flex items-center gap-2" data-open-modal="mark-as-read">
|
||||
<i data-feather="book"></i> {{ trans('forum::general.mark_read') }}
|
||||
</x-forum::button>
|
||||
</div>
|
||||
|
||||
@include ('forum::thread.modals.mark-as-read')
|
||||
@endcan
|
||||
@endif
|
||||
@stop
|
||||
@@ -19,13 +19,13 @@
|
||||
wire:model="description" />
|
||||
|
||||
<x-forum::form.input-text
|
||||
id="color"
|
||||
id="color-light-mode"
|
||||
:label="trans('forum::general.color_light_mode')"
|
||||
wire:model="color_light_mode"
|
||||
data-coloris />
|
||||
|
||||
<x-forum::form.input-text
|
||||
id="color"
|
||||
id="color-dark-mode"
|
||||
:label="trans('forum::general.color_dark_mode')"
|
||||
wire:model="color_dark_mode"
|
||||
data-coloris />
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
wire:model="description" />
|
||||
|
||||
<x-forum::form.input-text
|
||||
id="color"
|
||||
id="color-light-mode"
|
||||
:label="trans('forum::general.color_light_mode')"
|
||||
wire:model="color_light_mode"
|
||||
data-coloris />
|
||||
|
||||
<x-forum::form.input-text
|
||||
id="color"
|
||||
id="color-dark-mode"
|
||||
:label="trans('forum::general.color_dark_mode')"
|
||||
wire:model="color_dark_mode"
|
||||
data-coloris />
|
||||
|
||||
@@ -9,6 +9,8 @@ export default defineConfig({
|
||||
'resources/js/app.js',
|
||||
'resources/forum/livewire-tailwind/css/forum.css',
|
||||
'resources/forum/livewire-tailwind/js/forum.js',
|
||||
'resources/forum/blade-tailwind/css/forum.css',
|
||||
'resources/forum/blade-tailwind/js/forum.js',
|
||||
],
|
||||
refresh: true,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user