mirror of
https://github.com/NyaaStudios/nyaabooru.git
synced 2025-12-10 05:42:58 +00:00
Add tag & tag group creation
This commit is contained in:
parent
f64afa649a
commit
f2950ec7eb
15 changed files with 330 additions and 22 deletions
|
|
@ -4,6 +4,8 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Models\Comment;
|
||||
use App\Models\Post;
|
||||
use App\Models\Tag;
|
||||
use App\Models\TagGroup;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
|
@ -28,4 +30,20 @@ class DeletionController extends Controller
|
|||
$post->delete();
|
||||
return redirect()->route('posts.home');
|
||||
}
|
||||
|
||||
public function deleteTag(Tag $tag)
|
||||
{
|
||||
$tag->delete();
|
||||
return redirect()->route('tags.home');
|
||||
}
|
||||
|
||||
public function deleteTagGroup(TagGroup $tagGroup)
|
||||
{
|
||||
foreach ($tagGroup->tags as $tag)
|
||||
{
|
||||
$tag->delete();
|
||||
}
|
||||
$tagGroup->delete();
|
||||
return redirect()->route('tags.groups');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
app/Livewire/App/DataCard.php
Normal file
17
app/Livewire/App/DataCard.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\App;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class DataCard extends Component
|
||||
{
|
||||
public string $icon = 'question-mark';
|
||||
public string $title = '';
|
||||
public int $value = 0;
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.app.data-card');
|
||||
}
|
||||
}
|
||||
39
app/Livewire/Tags/Groups.php
Normal file
39
app/Livewire/Tags/Groups.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\Tags;
|
||||
|
||||
use App\Models\TagGroup;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Groups extends Component
|
||||
{
|
||||
#[Validate('required|string|min:2|max:100|unique:tag_groups,name')]
|
||||
public string $name = '';
|
||||
|
||||
#[Validate('required')]
|
||||
public string $color = '';
|
||||
|
||||
#[Validate('nullable|string|max:240')]
|
||||
public string $description = '';
|
||||
|
||||
#[Title("Tag groups")]
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.tags.groups', ['groups' => TagGroup::all()]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
TagGroup::create([
|
||||
'name' => $this->name,
|
||||
'color' => $this->color,
|
||||
'description' => $this->description,
|
||||
]);
|
||||
|
||||
return $this->redirectRoute('tags.groups');
|
||||
}
|
||||
}
|
||||
53
app/Livewire/Tags/Index.php
Normal file
53
app/Livewire/Tags/Index.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\Tags;
|
||||
|
||||
use App\Models\Post;
|
||||
use App\Models\Tag;
|
||||
use App\Models\TagGroup;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
use MongoDB\Collection;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
#[Validate('required|string|min:2|max:100|unique:tags,name')]
|
||||
public string $name = '';
|
||||
|
||||
#[Validate('required|exists:tag_groups,id')]
|
||||
public string $group = '';
|
||||
|
||||
#[Validate('nullable')]
|
||||
public $implies = [];
|
||||
|
||||
public $untaggedPosts = [];
|
||||
|
||||
#[Title("Tags")]
|
||||
public function render()
|
||||
{
|
||||
$this->untaggedPosts = Post::doesntHave('tags')->get();
|
||||
|
||||
return view('livewire.tags.index', [
|
||||
'tags' => Tag::all(),
|
||||
'tagGroups' => TagGroup::all(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$tag = Tag::create([
|
||||
'name' => $this->name,
|
||||
'slug' => Str::slug($this->name, '_'),
|
||||
'implies' => $this->implies,
|
||||
]);
|
||||
|
||||
$group = TagGroup::find($this->group);
|
||||
$group->tags()->save($tag);
|
||||
|
||||
return $this->redirect('/tags');
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,10 @@
|
|||
namespace App\Models;
|
||||
|
||||
use MongoDB\Laravel\Eloquent\Model;
|
||||
use MongoDB\Laravel\Relations\BelongsTo;
|
||||
use MongoDB\Laravel\Relations\BelongsToMany;
|
||||
use MongoDB\Laravel\Relations\HasMany;
|
||||
use MongoDB\Laravel\Relations\MorphToMany;
|
||||
|
||||
class Tag extends Model
|
||||
{
|
||||
|
|
@ -13,4 +16,9 @@ class Tag extends Model
|
|||
{
|
||||
return $this->belongsToMany(Post::class);
|
||||
}
|
||||
|
||||
public function tagGroup(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(TagGroup::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
app/Models/TagGroup.php
Normal file
16
app/Models/TagGroup.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use MongoDB\Laravel\Eloquent\Model;
|
||||
use MongoDB\Laravel\Relations\HasMany;
|
||||
|
||||
class TagGroup extends Model
|
||||
{
|
||||
protected $fillable = ['name', 'color', 'description'];
|
||||
|
||||
public function tags(): HasMany
|
||||
{
|
||||
return $this->hasMany(Tag::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ return new class extends Migration
|
|||
{
|
||||
Schema::create('tags', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tag_groups', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('color');
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tag_groups');
|
||||
}
|
||||
};
|
||||
11
resources/views/livewire/app/data-card.blade.php
Normal file
11
resources/views/livewire/app/data-card.blade.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<wa-card>
|
||||
<div class="wa-flank wa-align-items-start">
|
||||
<wa-avatar shape="rounded">
|
||||
<wa-icon slot="icon" name="{{ $icon }}"></wa-icon>
|
||||
</wa-avatar>
|
||||
<div class="wa-stack wa-gap-2xs">
|
||||
<h3 class="wa-caption-m">{{ $title }}</h3>
|
||||
<wa-format-number class="wa-heading-l" :$value></wa-format-number>
|
||||
</div>
|
||||
</div>
|
||||
</wa-card>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
Upload
|
||||
</wa-button>
|
||||
|
||||
<wa-button appearance="plain">
|
||||
<wa-button appearance="plain" href="{{ route('tags.home') }}" wire:navigate.hover>
|
||||
<wa-icon slot="prefix" name="tags"></wa-icon>
|
||||
Tags
|
||||
</wa-button>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,8 @@
|
|||
<div class="wa-stack">
|
||||
<h1>{{ $user->name }}</h1>
|
||||
|
||||
<div class="wa-grid">
|
||||
|
||||
<wa-card>
|
||||
<span class="wa-heading-m">
|
||||
<wa-format-number value="{{ $user->posts->count() }}"></wa-format-number> {{ Str::plural('post', $user->posts->count()) }}
|
||||
</span>
|
||||
</wa-card>
|
||||
|
||||
<wa-card>
|
||||
<span class="wa-heading-m">
|
||||
<wa-format-number value="{{ $user->comments->count() }}"></wa-format-number> {{ Str::plural('comment', $user->comments->count()) }}
|
||||
</span>
|
||||
</wa-card>
|
||||
|
||||
<wa-card>
|
||||
<span class="wa-heading-m">
|
||||
Last seen {{ $user->updated_at->diffForHumans() }}
|
||||
</span>
|
||||
</wa-card>
|
||||
|
||||
<div class="wa-grid" style="--min-column-size: 30ch;">
|
||||
<livewire:app.data-card icon="images" title="Posts" value="{{ $user->posts->count() }}" />
|
||||
<livewire:app.data-card icon="comments" title="Comments" value="{{ $user->comments->count() }}" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<span>Changes are automatically saved.</span>
|
||||
</wa-callout>
|
||||
|
||||
<form wire:submit class="wa-stack">
|
||||
<form wire:submit class="wa-stack wa-gap-xl">
|
||||
<wa-select wire:model.live="rating" label="Rating" value="{{ $post->rating }}" wire:loading.attr="disabled" @error('rating') hint="{{ $message }}" @enderror>
|
||||
<wa-option value="safe">Safe</wa-option>
|
||||
<wa-option value="suggestive">Suggestive</wa-option>
|
||||
|
|
|
|||
52
resources/views/livewire/tags/groups.blade.php
Normal file
52
resources/views/livewire/tags/groups.blade.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<div class="wa-stack">
|
||||
<h1 class="wa-cluster wa-gap-3xl" style="--wa-link-decoration-default: none; --wa-color-text-link: var(--wa-color-text-quiet)">
|
||||
<a href="{{ route('tags.home') }}" wire:navigate.hover>Tags</a>
|
||||
<span>Tag groups</span>
|
||||
</h1>
|
||||
|
||||
<div class="wa-flank:end wa-align-items-start">
|
||||
|
||||
{{-- List tag groups --}}
|
||||
<table class="wa-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>Description</td>
|
||||
<td>Tag count</td>
|
||||
<td>Actions</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($groups as $group)
|
||||
<tr>
|
||||
<td style="color: {{ $group->color }};">{{ $group->name }}</td>
|
||||
<td>{{ $group->description }}</td>
|
||||
<td><wa-format-number value="{{ $group->tags->count() }}"></wa-format-number> {{ Str::plural('tag', $group->tags->count()) }}</td>
|
||||
<td>
|
||||
<wa-icon-button name="times" style="color: var(--wa-color-danger-on-normal);" href="{{ url("/delete/group/$group->id") }}" wire:navigate></wa-icon-button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{-- Create tag group --}}
|
||||
<div class="wa-stack">
|
||||
<span class="wa-heading-m">Create a tag group</span>
|
||||
<form wire:submit="create" class="wa-stack wa-gap-2xl">
|
||||
<wa-input wire:model="name" type="text" label="Group name"></wa-input>
|
||||
<wa-color-picker
|
||||
wire:model="color"
|
||||
label="Group color"
|
||||
swatches="#d0021b; #f5a623; #f8e71c; #8b572a; #7ed321; #417505; #bd10e0; #9013fe; #4a90e2; #50e3c2; #b8e986; #000; #444; #888; #ccc; #fff;">
|
||||
</wa-color-picker>
|
||||
<wa-input wire:model="description" type="text" label="Group description (optional)"></wa-input>
|
||||
<wa-button type="submit" appearance="outlined" variant="brand">
|
||||
<wa-icon name="plus" slot="prefix"></wa-icon>
|
||||
Create tag group
|
||||
</wa-button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
69
resources/views/livewire/tags/index.blade.php
Normal file
69
resources/views/livewire/tags/index.blade.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
@php use App\Models\Tag; @endphp
|
||||
|
||||
<div class="wa-stack">
|
||||
<h1 class="wa-cluster wa-gap-3xl" style="--wa-link-decoration-default: none; --wa-color-text-link: var(--wa-color-text-quiet)">
|
||||
<span>Tags</span>
|
||||
<a href="{{ route('tags.groups') }}" wire:navigate.hover>Tag groups</a>
|
||||
</h1>
|
||||
|
||||
<div class="wa-flank:end wa-align-items-start">
|
||||
<table class="wa-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>Implies</td>
|
||||
<td>Post count</td>
|
||||
<td>Actions</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{-- Untagged posts --}}
|
||||
<tr>
|
||||
<td class="wa-caption-m">-- Untagged --</td>
|
||||
<td></td>
|
||||
<td><wa-format-number value="{{ $untaggedPosts->count() }}"></wa-format-number> {{ Str::plural('post', $untaggedPosts->count()) }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
@foreach ($tags as $tag)
|
||||
<tr>
|
||||
<td style="color: {{ $tag->tagGroup->color }}">{{ $tag->name }}</td>
|
||||
<td>
|
||||
@if ($tag->implies)
|
||||
@foreach ($tag->implies as $impliesTagId)
|
||||
@php $impliedTag = Tag::find($impliesTagId); @endphp
|
||||
<span style="color: {{ $impliedTag->tagGroup->color }}">{{ $impliedTag->name }}</span>
|
||||
@endforeach
|
||||
@endif
|
||||
</td>
|
||||
<td><wa-format-number value="{{ $tag->posts->count() }}"></wa-format-number> {{ Str::plural('post', $tag->posts->count()) }}</td>
|
||||
<td>
|
||||
<wa-icon-button name="times" style="color: var(--wa-color-danger-on-normal);" href="{{ url("/delete/tag/$tag->id") }}" wire:navigate></wa-icon-button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="wa-stack">
|
||||
<span class="wa-heading-m">Create a tag</span>
|
||||
<form wire:submit="create" class="wa-stack wa-gap-2xl">
|
||||
<wa-input wire:model="name" type="text" label="Tag name" @error('name') hint="{{ $message }}" @enderror></wa-input>
|
||||
<wa-select wire:model="group" label="Tag group" @error('group') hint="{{ $message }}" @enderror>
|
||||
@foreach ($tagGroups as $tagGroup)
|
||||
<wa-option value="{{ $tagGroup->id }}" style="color: {{ $tagGroup->color }};">{{ $tagGroup->name }}</wa-option>
|
||||
@endforeach
|
||||
</wa-select>
|
||||
<wa-select wire:model="implies" label="Implied tags" multiple clearable @error('implies') hint="{{ $message }}" @enderror>
|
||||
@foreach ($tags->all() as $tagToImply)
|
||||
<wa-option value="{{ $tagToImply->id }}" style="color: {{ $tagToImply->tagGroup->color }};">{{ $tagToImply->name }}</wa-option>
|
||||
@endforeach
|
||||
</wa-select>
|
||||
<wa-button type="submit" variant="brand" appearance="outlined">
|
||||
<wa-icon name="plus" slot="prefix"></wa-icon>
|
||||
Create tag
|
||||
</wa-button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -7,6 +7,8 @@ use App\Livewire\Pages\Upload as UploadPage;
|
|||
use App\Livewire\Posts\Index as PostsPage;
|
||||
use App\Livewire\Posts\Edit as EditPost;
|
||||
use App\Livewire\Posts\View as ViewPost;
|
||||
use App\Livewire\Tags\Index as TagsIndexPage;
|
||||
use App\Livewire\Tags\Groups as TagGroupsPage;
|
||||
use App\Models\Post;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
|
@ -29,8 +31,16 @@ Route::middleware('auth')->prefix('posts')->group(function () {
|
|||
});
|
||||
});
|
||||
|
||||
// Tag routes
|
||||
Route::middleware('auth')->prefix('tags')->group(function () {
|
||||
Route::get('/', TagsIndexPage::class)->name('tags.home');
|
||||
Route::get('/groups', TagGroupsPage::class)->name('tags.groups');
|
||||
});
|
||||
|
||||
// Object deletion routes
|
||||
Route::middleware('auth')->prefix('delete')->controller(DeletionController::class)->group(function () {
|
||||
Route::get('comment/{comment}', 'deleteComment');
|
||||
Route::get('post/{post}', 'deletePost');
|
||||
Route::get('tag/{tag}', 'deleteTag');
|
||||
Route::get('group/{tagGroup}', 'deleteTagGroup');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue