Save workspace changes

This commit is contained in:
2026-04-18 17:02:56 +02:00
parent f02ea9a711
commit 87d60af5a9
4220 changed files with 1388603 additions and 1554 deletions

View File

@@ -0,0 +1,100 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Http\Requests\Dashboard\ArtworkEditRequest;
use App\Http\Requests\Dashboard\ArtworkDestroyRequest;
use App\Http\Requests\Dashboard\UpdateArtworkRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
class ArtworkController extends Controller
{
public function index(Request $request): View
{
$artworks = $request->user()
->artworks()
->latest()
->paginate(20);
return view('artworks.index', [
'artworks' => $artworks,
'page_title' => 'My Artworks',
]);
}
public function edit(ArtworkEditRequest $request, int $id): View
{
$artwork = $request->artwork();
return view('artworks.edit', [
'artwork' => $artwork,
'page_title' => 'Edit Artwork',
]);
}
public function update(UpdateArtworkRequest $request, int $id): RedirectResponse
{
$artwork = $request->artwork();
$data = $request->validated();
$artwork->title = $data['title'];
$artwork->description = $data['description'] ?? null;
if ($request->hasFile('file')) {
$file = $request->file('file');
// Remove prior stored file if it's on the public disk.
if (! empty($artwork->file_path) && Storage::disk('public')->exists($artwork->file_path)) {
Storage::disk('public')->delete($artwork->file_path);
}
$size = $file->getSize() ?? 0;
$mime = $file->getMimeType() ?? 'application/octet-stream';
$dimensions = @getimagesize($file->getRealPath());
$width = is_array($dimensions) ? (int) ($dimensions[0] ?? 0) : 0;
$height = is_array($dimensions) ? (int) ($dimensions[1] ?? 0) : 0;
$path = $file->storePublicly('artworks', 'public');
$artwork->file_name = $file->getClientOriginalName();
$artwork->file_path = $path;
$artwork->file_size = (int) $size;
$artwork->mime_type = (string) $mime;
$artwork->width = max(1, $width);
$artwork->height = max(1, $height);
// If a file is replaced, clear CDN-derived fields unless a separate pipeline repopulates them.
$artwork->hash = null;
$artwork->thumb_ext = null;
$artwork->file_ext = $file->guessExtension() ?: $file->getClientOriginalExtension() ?: null;
}
$artwork->save();
return redirect()
->route('dashboard.artworks.edit', $artwork->id)
->with('status', 'Artwork updated.');
}
public function destroy(ArtworkDestroyRequest $request, int $id): RedirectResponse
{
$artwork = $request->artwork();
// Best-effort remove stored file.
if (! empty($artwork->file_path) && Storage::disk('public')->exists($artwork->file_path)) {
Storage::disk('public')->delete($artwork->file_path);
}
$artwork->delete();
return redirect()
->route('dashboard.artworks.index')
->with('status', 'Artwork deleted.');
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Services\ReceivedCommentsInboxService;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
class CommentController extends Controller
{
public function __construct(private readonly ReceivedCommentsInboxService $inbox) {}
public function received(Request $request): View
{
$user = $request->user();
$search = trim((string) $request->query('q', ''));
$sort = strtolower((string) $request->query('sort', 'newest'));
if (! in_array($sort, ['newest', 'oldest'], true)) {
$sort = 'newest';
}
$baseQuery = $this->inbox->queryForUser($user)
->with(['user.profile', 'artwork']);
if ($search !== '') {
$baseQuery->where(function ($query) use ($search): void {
$query->where('content', 'like', '%' . $search . '%')
->orWhere('raw_content', 'like', '%' . $search . '%')
->orWhereHas('artwork', function ($artworkQuery) use ($search): void {
$artworkQuery->where('title', 'like', '%' . $search . '%')
->orWhere('slug', 'like', '%' . $search . '%');
})
->orWhereHas('user', function ($userQuery) use ($search): void {
$userQuery->where('username', 'like', '%' . $search . '%')
->orWhere('name', 'like', '%' . $search . '%');
});
});
}
$orderedQuery = (clone $baseQuery)
->orderBy('created_at', $sort === 'oldest' ? 'asc' : 'desc');
$comments = $orderedQuery->paginate(12)->withQueryString();
$statsBaseQuery = clone $baseQuery;
$freshlyClearedCount = $this->inbox->unreadCountForUser($user);
$totalComments = (clone $statsBaseQuery)->count();
$recentComments = (clone $statsBaseQuery)->where('created_at', '>=', now()->subDays(7))->count();
$uniqueCommenters = (clone $statsBaseQuery)->distinct('user_id')->count('user_id');
$activeArtworks = (clone $statsBaseQuery)->distinct('artwork_id')->count('artwork_id');
$this->inbox->markInboxRead($user);
return view('dashboard.comments', [
'comments' => $comments,
'search' => $search,
'sort' => $sort,
'freshlyClearedCount' => $freshlyClearedCount,
'stats' => [
'total' => $totalComments,
'recent' => $recentComments,
'commenters' => $uniqueCommenters,
'artworks' => $activeArtworks,
],
]);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Models\Artwork;
use Illuminate\Http\Request;
use Illuminate\View\View;
class DashboardAwardsController extends Controller
{
public function index(Request $request): View
{
$user = $request->user();
$artworks = Artwork::query()
->where('user_id', (int) $user->id)
->whereHas('awards')
->with(['awardStat', 'stats', 'categories.contentType'])
->orderByDesc(
\App\Models\ArtworkAwardStat::select('score_total')
->whereColumn('artwork_id', 'artworks.id')
->limit(1)
)
->paginate(24)
->withQueryString();
return view('dashboard.awards', [
'artworks' => $artworks,
'page_title' => 'My Awards SkinBase',
]);
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Models\Artwork;
use App\Models\ContentType;
use App\Support\AvatarUrl;
use App\Services\ThumbnailPresenter;
use Illuminate\Http\Request;
use Illuminate\View\View;
class DashboardGalleryController extends Controller
{
public function index(Request $request): View
{
$user = $request->user();
$perPage = 24;
$query = Artwork::query()
->with([
'user:id,name,username',
'user.profile:user_id,avatar_hash',
'categories:id,name,slug,content_type_id,parent_id,sort_order',
'categories.contentType:id,name,slug',
])
->where('user_id', (int) $user->id)
->orderBy('published_at', 'desc');
$artworks = $query->paginate($perPage)->withQueryString();
$artworks->setCollection(
$artworks->getCollection()->map(fn (Artwork $artwork) => $this->presentArtwork($artwork))
);
$mainCategories = ContentType::ordered()
->get(['name', 'slug'])
->map(function (ContentType $type) {
return (object) [
'id' => $type->id,
'name' => $type->name,
'slug' => $type->slug,
'url' => '/' . strtolower($type->slug),
];
});
return view('gallery.index', [
'gallery_type' => 'dashboard',
'mainCategories' => $mainCategories,
'subcategories' => $mainCategories,
'contentType' => null,
'category' => null,
'artworks' => $artworks,
'hero_title' => 'My Gallery',
'hero_description' => 'Your uploaded artworks.',
'breadcrumbs' => collect(),
'page_title' => 'My Gallery - SkinBase',
'page_meta_description' => 'My uploaded artworks on SkinBase',
'page_meta_keywords' => 'my gallery, uploads, skinbase',
'page_canonical' => url('/dashboard/gallery'),
]);
}
private function presentArtwork(Artwork $artwork): object
{
$primary = $artwork->categories->sortBy('sort_order')->first();
$present = ThumbnailPresenter::present($artwork, 'md');
$group = $artwork->group;
$isGroupPublisher = $group !== null;
$displayName = $isGroupPublisher ? ($group->name ?? 'Skinbase') : ($artwork->user?->name ?? 'Skinbase');
$username = $isGroupPublisher ? '' : ($artwork->user?->username ?? '');
$avatarUrl = $isGroupPublisher
? $group->avatarUrl()
: AvatarUrl::forUser(
(int) ($artwork->user_id ?? 0),
$artwork->user?->profile?->avatar_hash ?? null,
64
);
$profileUrl = $isGroupPublisher ? $group->publicUrl() : ($username !== '' ? '/@' . $username : null);
return (object) [
'id' => $artwork->id,
'name' => $artwork->title,
'content_type_name' => $primary?->contentType?->name ?? '',
'content_type_slug' => $primary?->contentType?->slug ?? '',
'category_name' => $primary?->name ?? '',
'category_slug' => $primary?->slug ?? '',
'thumb_url' => $present['url'],
'thumb_srcset' => $present['srcset'] ?? $present['url'],
'uname' => $displayName,
'username' => $username,
'avatar_url' => $avatarUrl,
'profile_url' => $profileUrl,
'published_as_type' => $isGroupPublisher ? 'group' : 'user',
'publisher' => [
'type' => $isGroupPublisher ? 'group' : 'user',
'name' => $displayName,
'username' => $username,
'avatar_url' => $avatarUrl,
'profile_url' => $profileUrl,
],
'published_at' => $artwork->published_at,
'slug' => $artwork->slug ?? '',
'width' => $artwork->width ?? null,
'height' => $artwork->height ?? null,
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Models\DashboardPreference;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DashboardPreferenceController extends Controller
{
public function updateShortcuts(Request $request): JsonResponse
{
$validated = $request->validate([
'pinned_spaces' => ['present', 'array', 'max:' . DashboardPreference::MAX_PINNED_SPACES],
'pinned_spaces.*' => ['string'],
]);
$pinnedSpaces = DashboardPreference::sanitizePinnedSpaces($validated['pinned_spaces'] ?? []);
DashboardPreference::query()->updateOrCreate(
['user_id' => $request->user()->id],
['pinned_spaces' => $pinnedSpaces]
);
return response()->json([
'data' => [
'pinned_spaces' => $pinnedSpaces,
],
]);
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Models\Artwork;
use App\Services\UserStatsService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\View\View;
use App\Support\AvatarUrl;
class FavoriteController extends Controller
{
public function index(Request $request): View
{
$user = $request->user();
$perPage = 20;
$favTable = 'artwork_favourites';
$sort = $request->query('sort', 'newest');
$order = $sort === 'oldest' ? 'asc' : 'desc';
$orderColumn = 'created_at';
$query = DB::table($favTable)->where('user_id', (int) $user->id);
if ($orderColumn) {
$query = $query->orderBy($orderColumn, $order);
}
// Collect artwork ids in the correct order using the favourites table
$artworkIds = $query->pluck('artwork_id')->values()->all();
$page = max(1, (int) $request->query('page', 1));
$slice = array_slice($artworkIds, ($page - 1) * $perPage, $perPage);
$artworks = collect();
if ($slice !== []) {
$arts = Artwork::query()
->whereIn('id', $slice)
->with(['user.profile', 'categories'])
->withCount(['favourites', 'comments'])
->get()
->keyBy('id');
foreach ($slice as $id) {
$a = $arts->get($id);
if (! $a) continue;
$primaryCategory = $a->categories->sortBy('sort_order')->first();
$username = $a->user?->username ?? $a->user?->name ?? '';
$artworks->push((object) [
'id' => $a->id,
'name' => $a->title,
'title' => $a->title,
'thumb' => $a->thumbUrl('md') ?? $a->thumbnail_url ?? null,
'thumb_url' => $a->thumbUrl('md') ?? $a->thumbnail_url ?? null,
'slug' => $a->slug,
'author' => $username,
'uname' => $username,
'username' => $a->user?->username ?? '',
'avatar_url' => AvatarUrl::forUser(
(int) ($a->user_id ?? 0),
$a->user?->profile?->avatar_hash ?? null,
64
),
'content_type_name' => $primaryCategory?->contentType?->name ?? '',
'content_type_slug' => $primaryCategory?->contentType?->slug ?? '',
'category_name' => $primaryCategory->name ?? '',
'category_slug' => $primaryCategory->slug ?? '',
'width' => $a->width,
'height' => $a->height,
'likes' => (int) ($a->favourites_count ?? $a->likes ?? 0),
'comments_count' => (int) ($a->comments_count ?? 0),
'published_at' => $a->published_at,
]);
}
}
$paginator = new LengthAwarePaginator($artworks->toArray(), count($artworkIds), $perPage, $page, ['path' => $request->url(), 'query' => $request->query()]);
return view('dashboard.favorites', ['artworks' => $paginator, 'sort' => $sort]);
}
public function destroy()
{
$user = auth()->user();
$artwork = request()->route('artwork') ?? request()->input('artwork');
if (! $artwork) {
$last = collect(request()->segments())->last();
if (is_numeric($last)) {
$artwork = (int) $last;
}
}
$artworkId = is_object($artwork) ? (int) $artwork->id : (int) $artwork;
Log::info('FavoriteController::destroy', ['user_id' => $user->id ?? null, 'artworkId' => $artworkId]);
// Look up creator before deleting so we can decrement their counter
$creatorId = (int) DB::table('artworks')->where('id', $artworkId)->value('user_id');
DB::table('artwork_favourites')
->where('user_id', (int) $user->id)
->where('artwork_id', $artworkId)
->delete();
if ($creatorId) {
app(UserStatsService::class)->decrementFavoritesReceived($creatorId);
}
return redirect()->route('dashboard.favorites')->with('status', 'favourite-removed');
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Support\AvatarUrl;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class FollowerController extends Controller
{
public function index(Request $request)
{
$user = $request->user();
$perPage = 30;
$search = trim((string) $request->query('q', ''));
$sort = (string) $request->query('sort', 'recent');
$relationship = (string) $request->query('relationship', 'all');
$allowedSorts = ['recent', 'oldest', 'name', 'uploads', 'followers'];
$allowedRelationships = ['all', 'following-back', 'not-followed'];
if (! in_array($sort, $allowedSorts, true)) {
$sort = 'recent';
}
if (! in_array($relationship, $allowedRelationships, true)) {
$relationship = 'all';
}
// People who follow $user (user_id = $user being followed)
$baseQuery = DB::table('user_followers as uf')
->join('users as u', 'u.id', '=', 'uf.follower_id')
->leftJoin('user_profiles as up', 'up.user_id', '=', 'u.id')
->leftJoin('user_statistics as us', 'us.user_id', '=', 'u.id')
->leftJoin('user_followers as mutual', function ($join) use ($user): void {
$join->on('mutual.user_id', '=', 'uf.follower_id')
->where('mutual.follower_id', '=', $user->id);
})
->where('uf.user_id', $user->id)
->whereNull('u.deleted_at')
->when($search !== '', function ($query) use ($search): void {
$query->where(function ($inner) use ($search): void {
$inner->where('u.username', 'like', '%' . $search . '%')
->orWhere('u.name', 'like', '%' . $search . '%');
});
})
->when($relationship === 'following-back', function ($query): void {
$query->whereNotNull('mutual.created_at');
})
->when($relationship === 'not-followed', function ($query): void {
$query->whereNull('mutual.created_at');
});
$summaryBaseQuery = clone $baseQuery;
$followers = $baseQuery
->when($sort === 'recent', fn ($query) => $query->orderByDesc('uf.created_at'))
->when($sort === 'oldest', fn ($query) => $query->orderBy('uf.created_at'))
->when($sort === 'name', fn ($query) => $query->orderByRaw('COALESCE(u.username, u.name) asc'))
->when($sort === 'uploads', fn ($query) => $query->orderByDesc('us.uploads_count')->orderByRaw('COALESCE(u.username, u.name) asc'))
->when($sort === 'followers', fn ($query) => $query->orderByDesc('us.followers_count')->orderByRaw('COALESCE(u.username, u.name) asc'))
->select([
'u.id', 'u.username', 'u.name',
'up.avatar_hash',
'us.uploads_count',
'us.followers_count',
'uf.created_at as followed_at',
'mutual.created_at as followed_back_at',
])
->paginate($perPage)
->withQueryString()
->through(fn ($row) => (object) [
'id' => $row->id,
'name' => $row->name,
'username' => $row->username,
'uname' => $row->username ?? $row->name,
'avatar_url' => AvatarUrl::forUser((int) $row->id, $row->avatar_hash, 64),
'profile_url' => '/@' . strtolower((string) ($row->username ?? $row->id)),
'uploads' => $row->uploads_count ?? 0,
'followers_count' => $row->followers_count ?? 0,
'is_following_back' => $row->followed_back_at !== null,
'followed_back_at' => $row->followed_back_at,
'followed_at' => $row->followed_at,
]);
$summary = [
'total_followers' => (clone $summaryBaseQuery)->count(),
'following_back' => (clone $summaryBaseQuery)->whereNotNull('mutual.created_at')->count(),
'not_followed' => (clone $summaryBaseQuery)->whereNull('mutual.created_at')->count(),
];
return view('dashboard.followers', [
'followers' => $followers,
'filters' => [
'q' => $search,
'sort' => $sort,
'relationship' => $relationship,
],
'summary' => $summary,
'page_title' => 'My Followers',
]);
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Support\AvatarUrl;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class FollowingController extends Controller
{
public function index(Request $request)
{
$user = $request->user();
$perPage = 30;
$search = trim((string) $request->query('q', ''));
$sort = (string) $request->query('sort', 'recent');
$relationship = (string) $request->query('relationship', 'all');
$allowedSorts = ['recent', 'oldest', 'name', 'uploads', 'followers'];
$allowedRelationships = ['all', 'mutual', 'one-way'];
if (! in_array($sort, $allowedSorts, true)) {
$sort = 'recent';
}
if (! in_array($relationship, $allowedRelationships, true)) {
$relationship = 'all';
}
// People that $user follows (follower_id = $user)
$baseQuery = DB::table('user_followers as uf')
->join('users as u', 'u.id', '=', 'uf.user_id')
->leftJoin('user_profiles as up', 'up.user_id', '=', 'u.id')
->leftJoin('user_statistics as us', 'us.user_id', '=', 'u.id')
->leftJoin('user_followers as mutual', function ($join) use ($user): void {
$join->on('mutual.follower_id', '=', 'uf.user_id')
->where('mutual.user_id', '=', $user->id);
})
->where('uf.follower_id', $user->id)
->whereNull('u.deleted_at')
->when($search !== '', function ($query) use ($search): void {
$query->where(function ($inner) use ($search): void {
$inner->where('u.username', 'like', '%' . $search . '%')
->orWhere('u.name', 'like', '%' . $search . '%');
});
})
->when($relationship === 'mutual', function ($query): void {
$query->whereNotNull('mutual.created_at');
})
->when($relationship === 'one-way', function ($query): void {
$query->whereNull('mutual.created_at');
});
$summaryBaseQuery = clone $baseQuery;
$following = $baseQuery
->when($sort === 'recent', fn ($query) => $query->orderByDesc('uf.created_at'))
->when($sort === 'oldest', fn ($query) => $query->orderBy('uf.created_at'))
->when($sort === 'name', fn ($query) => $query->orderByRaw('COALESCE(u.username, u.name) asc'))
->when($sort === 'uploads', fn ($query) => $query->orderByDesc('us.uploads_count')->orderByRaw('COALESCE(u.username, u.name) asc'))
->when($sort === 'followers', fn ($query) => $query->orderByDesc('us.followers_count')->orderByRaw('COALESCE(u.username, u.name) asc'))
->select([
'u.id', 'u.username', 'u.name',
'up.avatar_hash',
'us.uploads_count',
'us.followers_count',
'uf.created_at as followed_at',
'mutual.created_at as follows_you_at',
])
->paginate($perPage)
->withQueryString()
->through(fn ($row) => (object) [
'id' => $row->id,
'username' => $row->username,
'name' => $row->name,
'uname' => $row->username ?? $row->name,
'avatar_url' => AvatarUrl::forUser((int) $row->id, $row->avatar_hash, 64),
'profile_url' => '/@' . strtolower((string) ($row->username ?? $row->id)),
'uploads' => $row->uploads_count ?? 0,
'followers_count' => $row->followers_count ?? 0,
'follows_you' => $row->follows_you_at !== null,
'follows_you_at' => $row->follows_you_at,
'followed_at' => $row->followed_at,
]);
$summary = [
'total_following' => (clone $summaryBaseQuery)->count(),
'mutual' => (clone $summaryBaseQuery)->whereNotNull('mutual.created_at')->count(),
'one_way' => (clone $summaryBaseQuery)->whereNull('mutual.created_at')->count(),
];
return view('dashboard.following', [
'following' => $following,
'filters' => [
'q' => $search,
'sort' => $sort,
'relationship' => $relationship,
],
'summary' => $summary,
'page_title' => 'People I Follow',
]);
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Http\Requests\Manage\ManageArtworkEditRequest;
use App\Http\Requests\Manage\ManageArtworkUpdateRequest;
use App\Http\Requests\Manage\ManageArtworkDestroyRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;
class ManageController extends Controller
{
public function index(Request $request)
{
$userId = $request->user()->id;
$perPage = 50;
$categorySub = DB::table('artwork_category as ac')
->join('categories as c', 'ac.category_id', '=', 'c.id')
->select('ac.artwork_id', DB::raw('MIN(c.name) as category_name'))
->groupBy('ac.artwork_id');
$query = DB::table('artworks as a')
->leftJoinSub($categorySub, 'cat', function ($join) {
$join->on('a.id', '=', 'cat.artwork_id');
})
->leftJoin('artwork_stats as s', 'a.id', '=', 's.artwork_id')
->where('a.user_id', $userId)
->select(
'a.*',
DB::raw('cat.category_name as category_name'),
DB::raw('COALESCE(s.rating_count, 0) as rating_num'),
DB::raw('COALESCE(s.rating_avg, 0) as rating'),
DB::raw('COALESCE(s.downloads, 0) as dls'),
DB::raw('COALESCE(s.favorites, 0) as zoom'),
DB::raw('COALESCE(s.views, 0) as views')
)
->orderByDesc('a.published_at')
->orderByDesc('a.id');
$artworks = $query->paginate($perPage);
return view('manage.index', [
'artworks' => $artworks,
'page_title' => 'Artwork Manager',
]);
}
public function edit(ManageArtworkEditRequest $request, $id)
{
$artwork = $request->artwork();
$selectedCategory = DB::table('artwork_category')->where('artwork_id', (int)$id)->value('category_id');
$artwork->category = $selectedCategory;
$categories = DB::table('categories')
->where('content_type_id', 0)
->orderBy('id')
->select(DB::raw('id as category_id'), DB::raw('name as category_name'))
->get();
return view('manage.edit', [
'artwork' => $artwork,
'categories' => $categories,
'page_title' => 'Edit Artwork: ' . ($artwork->title ?? ''),
]);
}
public function update(ManageArtworkUpdateRequest $request, $id)
{
$existing = $request->artwork();
$data = $request->validated();
$update = [
'title' => $data['title'],
'description' => $data['description'] ?? $existing->description,
'updated' => now(),
];
if ($request->hasFile('artwork')) {
$file = $request->file('artwork');
$path = $file->store('public/uploads/artworks');
$filename = basename($path);
$update['picture'] = $filename;
}
if ($request->hasFile('attachment')) {
$att = $request->file('attachment');
$attPath = $att->store('public/uploads/attachments');
$update['fname'] = basename($attPath);
}
DB::table('artworks')->where('id', (int)$id)->update($update);
if (isset($data['section'])) {
DB::table('artwork_category')->where('artwork_id', (int)$id)->delete();
DB::table('artwork_category')->insert([
'artwork_id' => (int)$id,
'category_id' => (int)$data['section'],
]);
}
return redirect()->route('manage')->with('status', 'Artwork was successfully updated.');
}
public function destroy(ManageArtworkDestroyRequest $request, $id)
{
$artwork = $request->artwork();
if (!empty($artwork->fname)) {
Storage::delete('public/uploads/attachments/' . $artwork->fname);
}
if (!empty($artwork->picture)) {
Storage::delete('public/uploads/artworks/' . $artwork->picture);
}
DB::table('artworks')->where('id', (int)$id)->delete();
return redirect()->route('manage')->with('status', 'Artwork deleted.');
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use App\Services\NotificationService;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
class NotificationController extends Controller
{
public function __construct(private readonly NotificationService $notifications) {}
public function index(Request $request): View
{
$page = max(1, (int) $request->query('page', 1));
$payload = $this->notifications->listForUser($request->user(), $page, 15);
return view('dashboard.notifications', [
'notifications' => collect($payload['data'] ?? []),
'notificationsMeta' => $payload['meta'] ?? [],
'unreadCount' => (int) ($payload['unread_count'] ?? 0),
]);
}
}