Implement creator studio and upload updates
This commit is contained in:
224
app/Services/Studio/CreatorStudioCalendarService.php
Normal file
224
app/Services/Studio/CreatorStudioCalendarService.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Studio;
|
||||
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
final class CreatorStudioCalendarService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CreatorStudioContentService $content,
|
||||
private readonly CreatorStudioScheduledService $scheduled,
|
||||
) {
|
||||
}
|
||||
|
||||
public function build(User $user, array $filters = []): array
|
||||
{
|
||||
$view = $this->normalizeView((string) ($filters['view'] ?? 'month'));
|
||||
$module = $this->normalizeModule((string) ($filters['module'] ?? 'all'));
|
||||
$status = $this->normalizeStatus((string) ($filters['status'] ?? 'scheduled'));
|
||||
$query = trim((string) ($filters['q'] ?? ''));
|
||||
$focusDate = $this->normalizeFocusDate((string) ($filters['focus_date'] ?? ''));
|
||||
|
||||
$scheduledItems = $this->scheduledItems($user, $module, $query);
|
||||
$unscheduledItems = $this->unscheduledItems($user, $module, $query);
|
||||
|
||||
return [
|
||||
'filters' => [
|
||||
'view' => $view,
|
||||
'module' => $module,
|
||||
'status' => $status,
|
||||
'q' => $query,
|
||||
'focus_date' => $focusDate->toDateString(),
|
||||
],
|
||||
'view_options' => [
|
||||
['value' => 'month', 'label' => 'Month'],
|
||||
['value' => 'week', 'label' => 'Week'],
|
||||
['value' => 'agenda', 'label' => 'Agenda'],
|
||||
],
|
||||
'status_options' => [
|
||||
['value' => 'scheduled', 'label' => 'Scheduled only'],
|
||||
['value' => 'unscheduled', 'label' => 'Unscheduled queue'],
|
||||
['value' => 'all', 'label' => 'Everything'],
|
||||
],
|
||||
'module_options' => array_merge([
|
||||
['value' => 'all', 'label' => 'All content'],
|
||||
], collect($this->content->moduleSummaries($user))->map(fn (array $summary): array => [
|
||||
'value' => $summary['key'],
|
||||
'label' => $summary['label'],
|
||||
])->all()),
|
||||
'summary' => $this->summary($scheduledItems, $unscheduledItems),
|
||||
'month' => $this->monthGrid($scheduledItems, $focusDate),
|
||||
'week' => $this->weekGrid($scheduledItems, $focusDate),
|
||||
'agenda' => $this->agenda($scheduledItems),
|
||||
'scheduled_items' => $status === 'unscheduled' ? [] : $scheduledItems->take(18)->values()->all(),
|
||||
'unscheduled_items' => $status === 'scheduled' ? [] : $unscheduledItems->take(12)->values()->all(),
|
||||
'gaps' => $this->gaps($scheduledItems, $focusDate),
|
||||
];
|
||||
}
|
||||
|
||||
private function scheduledItems(User $user, string $module, string $query): Collection
|
||||
{
|
||||
$items = $module === 'all'
|
||||
? collect($this->content->providers())->flatMap(fn ($provider) => $provider->scheduledItems($user, 320))
|
||||
: ($this->content->provider($module)?->scheduledItems($user, 320) ?? collect());
|
||||
|
||||
if ($query !== '') {
|
||||
$needle = mb_strtolower($query);
|
||||
$items = $items->filter(fn (array $item): bool => str_contains(mb_strtolower((string) ($item['title'] ?? '')), $needle));
|
||||
}
|
||||
|
||||
return $items
|
||||
->sortBy(fn (array $item): int => strtotime((string) ($item['scheduled_at'] ?? $item['published_at'] ?? '')) ?: PHP_INT_MAX)
|
||||
->values();
|
||||
}
|
||||
|
||||
private function unscheduledItems(User $user, string $module, string $query): Collection
|
||||
{
|
||||
$items = $module === 'all'
|
||||
? collect($this->content->providers())->flatMap(fn ($provider) => $provider->items($user, 'all', 240))
|
||||
: ($this->content->provider($module)?->items($user, 'all', 240) ?? collect());
|
||||
|
||||
return $items
|
||||
->filter(function (array $item) use ($query): bool {
|
||||
if (filled($item['scheduled_at'] ?? null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_array((string) ($item['status'] ?? ''), ['archived', 'hidden', 'rejected'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($query === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return str_contains(mb_strtolower((string) ($item['title'] ?? '')), mb_strtolower($query));
|
||||
})
|
||||
->sortByDesc(fn (array $item): int => strtotime((string) ($item['updated_at'] ?? $item['created_at'] ?? '')) ?: 0)
|
||||
->values();
|
||||
}
|
||||
|
||||
private function summary(Collection $scheduledItems, Collection $unscheduledItems): array
|
||||
{
|
||||
$days = $scheduledItems
|
||||
->groupBy(fn (array $item): string => date('Y-m-d', strtotime((string) ($item['scheduled_at'] ?? $item['published_at'] ?? now()->toIso8601String()))))
|
||||
->map(fn (Collection $items): int => $items->count());
|
||||
|
||||
return [
|
||||
'scheduled_total' => $scheduledItems->count(),
|
||||
'unscheduled_total' => $unscheduledItems->count(),
|
||||
'overloaded_days' => $days->filter(fn (int $count): bool => $count >= 3)->count(),
|
||||
'next_publish_at' => $scheduledItems->first()['scheduled_at'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
private function monthGrid(Collection $scheduledItems, Carbon $focusDate): array
|
||||
{
|
||||
$start = $focusDate->copy()->startOfMonth()->startOfWeek();
|
||||
$end = $focusDate->copy()->endOfMonth()->endOfWeek();
|
||||
$days = [];
|
||||
|
||||
for ($date = $start->copy(); $date->lte($end); $date->addDay()) {
|
||||
$key = $date->toDateString();
|
||||
$items = $scheduledItems->filter(fn (array $item): bool => str_starts_with((string) ($item['scheduled_at'] ?? ''), $key))->values();
|
||||
$days[] = [
|
||||
'date' => $key,
|
||||
'day' => $date->day,
|
||||
'is_current_month' => $date->month === $focusDate->month,
|
||||
'count' => $items->count(),
|
||||
'items' => $items->take(3)->all(),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'label' => $focusDate->format('F Y'),
|
||||
'days' => $days,
|
||||
];
|
||||
}
|
||||
|
||||
private function weekGrid(Collection $scheduledItems, Carbon $focusDate): array
|
||||
{
|
||||
$start = $focusDate->copy()->startOfWeek();
|
||||
|
||||
return [
|
||||
'label' => $start->format('M j') . ' - ' . $start->copy()->endOfWeek()->format('M j'),
|
||||
'days' => collect(range(0, 6))->map(function (int $offset) use ($start, $scheduledItems): array {
|
||||
$date = $start->copy()->addDays($offset);
|
||||
$key = $date->toDateString();
|
||||
$items = $scheduledItems->filter(fn (array $item): bool => str_starts_with((string) ($item['scheduled_at'] ?? ''), $key))->values();
|
||||
|
||||
return [
|
||||
'date' => $key,
|
||||
'label' => $date->format('D j'),
|
||||
'items' => $items->all(),
|
||||
];
|
||||
})->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function agenda(Collection $scheduledItems): array
|
||||
{
|
||||
return $scheduledItems
|
||||
->groupBy(fn (array $item): string => date('Y-m-d', strtotime((string) ($item['scheduled_at'] ?? $item['published_at'] ?? now()->toIso8601String()))))
|
||||
->map(fn (Collection $items, string $date): array => [
|
||||
'date' => $date,
|
||||
'label' => Carbon::parse($date)->format('M j'),
|
||||
'count' => $items->count(),
|
||||
'items' => $items->values()->all(),
|
||||
])
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
private function gaps(Collection $scheduledItems, Carbon $focusDate): array
|
||||
{
|
||||
return collect(range(0, 13))
|
||||
->map(function (int $offset) use ($focusDate, $scheduledItems): ?array {
|
||||
$date = $focusDate->copy()->startOfDay()->addDays($offset);
|
||||
$key = $date->toDateString();
|
||||
$count = $scheduledItems->filter(fn (array $item): bool => str_starts_with((string) ($item['scheduled_at'] ?? ''), $key))->count();
|
||||
|
||||
if ($count > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'date' => $key,
|
||||
'label' => $date->format('D, M j'),
|
||||
];
|
||||
})
|
||||
->filter()
|
||||
->take(6)
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
private function normalizeView(string $view): string
|
||||
{
|
||||
return in_array($view, ['month', 'week', 'agenda'], true) ? $view : 'month';
|
||||
}
|
||||
|
||||
private function normalizeStatus(string $status): string
|
||||
{
|
||||
return in_array($status, ['scheduled', 'unscheduled', 'all'], true) ? $status : 'scheduled';
|
||||
}
|
||||
|
||||
private function normalizeModule(string $module): string
|
||||
{
|
||||
return in_array($module, ['all', 'artworks', 'cards', 'collections', 'stories'], true) ? $module : 'all';
|
||||
}
|
||||
|
||||
private function normalizeFocusDate(string $value): Carbon
|
||||
{
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) === 1) {
|
||||
return Carbon::parse($value);
|
||||
}
|
||||
|
||||
return now();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user