docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Application\Campaign;
use App\Framework\Attributes\Route;
use App\Framework\Http\Method;
use App\Framework\Router\Result\ViewResult;
use App\Framework\Http\Session\Session;
use App\Framework\Meta\MetaData;
/**
* Show Campaign Landing Page
*
* Displays public campaign landing page with pre-save functionality
*/
final readonly class ShowCampaign
{
public function __construct(
private CampaignService $campaignService
) {}
#[Route(path: '/campaign/{slug}', method: Method::GET)]
public function __invoke(string $slug, Session $session): ViewResult
{
// Fetch campaign data
$campaign = $this->campaignService->findBySlug($slug);
if (!$campaign) {
throw new \RuntimeException("Campaign not found: {$slug}");
}
// Check if user has already saved
$userHasSaved = $this->campaignService->hasUserSaved(
$campaign->id,
$session->getId()
);
// Prepare campaign data for view
$campaignData = [
'slug' => $campaign->slug,
'artist_name' => $campaign->artist_name,
'album_title' => $campaign->album_title,
'description' => $campaign->description,
'artwork_url' => $campaign->artwork_url,
'release_date' => $campaign->release_date?->format('F j, Y'),
'total_saves' => $campaign->total_saves,
'track_count' => $campaign->track_count,
'spotify_enabled' => (bool) $campaign->spotify_enabled,
'apple_music_enabled' => (bool) $campaign->apple_music_enabled,
'tracks' => $campaign->tracks ? array_map(
fn($track) => [
'position' => $track->position,
'title' => $track->title,
'duration' => $track->duration ? $this->formatDuration($track->duration) : null,
'preview_url' => $track->preview_url,
],
$campaign->tracks
) : null,
];
return new ViewResult(
'campaign-landing',
new MetaData(
title: $campaign->artist_name . ' - ' . $campaign->album_title,
description: $campaign->description ?? 'Pre-save this album on Spotify',
),
data: [
'campaign' => $campaignData,
'user_has_saved' => $userHasSaved,
'csrf_token' => $session->csrf->generateToken('campaign-landing')->toString(),
]
);
}
private function formatDuration(int $seconds): string
{
$minutes = floor($seconds / 60);
$remainingSeconds = $seconds % 60;
return sprintf('%d:%02d', $minutes, $remainingSeconds);
}
}