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

@@ -1,353 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Application\Analytics;
use App\Application\Analytics\Service\AnalyticsDashboardService;
use App\Application\Analytics\Service\AnalyticsRealTimeService;
use App\Application\Analytics\Service\AnalyticsReportService;
use App\Framework\Analytics\AnalyticsCategory;
use App\Framework\Analytics\AnalyticsCollector;
use App\Framework\Analytics\Storage\AnalyticsStorage;
use App\Framework\Attributes\Route;
use App\Framework\Http\Method;
use App\Framework\Http\Status;
use App\Framework\Meta\StaticPageMetaResolver;
use App\Framework\Router\Result\JsonResult;
use App\Framework\Router\Result\ViewResult;
final class AnalyticsController
{
public function __construct(
private AnalyticsCollector $analyticsCollector,
private AnalyticsStorage $storage,
private AnalyticsDashboardService $dashboardService,
private AnalyticsReportService $reportService,
private AnalyticsRealTimeService $realTimeService,
) {
}
#[Route(path: '/admin/analytics', method: Method::GET)]
public function dashboard(): ViewResult
{
return new ViewResult(
'analytics-dashboard',
new StaticPageMetaResolver(
'Analytics Dashboard',
'View website analytics and user behavior data'
)(),
[
'title' => 'Analytics Dashboard',
'description' => 'Monitor your website performance and user engagement',
]
);
}
#[Route(path: '/admin/analytics/api/overview', method: Method::GET)]
public function getOverview(): JsonResult
{
try {
$endDate = $_GET['end_date'] ?? date('Y-m-d');
$startDate = $_GET['start_date'] ?? date('Y-m-d', strtotime('-30 days'));
// Calculate days between dates
$days = max(1, (strtotime($endDate) - strtotime($startDate)) / 86400 + 1);
$overview = $this->dashboardService->getOverview($startDate, $endDate);
return new JsonResult([
'success' => true,
'data' => $overview,
'period' => [
'start' => $startDate,
'end' => $endDate,
'days' => (int)$days,
],
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/timeseries', method: Method::GET)]
public function getTimeSeries(): JsonResult
{
try {
$metric = $_GET['metric'] ?? 'page_views';
$period = $_GET['period'] ?? 'day';
$days = (int) ($_GET['days'] ?? 30);
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime("-{$days} days"));
$timeSeries = $this->storage->getTimeSeries($metric, $startDate, $endDate, $period);
return new JsonResult([
'success' => true,
'data' => $timeSeries,
'metric' => $metric,
'period' => $period,
'range' => [
'start' => $startDate,
'end' => $endDate,
'days' => $days,
],
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/top-pages', method: Method::GET)]
public function getTopPages(): JsonResult
{
try {
$limit = (int) ($_GET['limit'] ?? 10);
$days = (int) ($_GET['days'] ?? 30);
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime("-{$days} days"));
$topPages = $this->dashboardService->getTopPages($startDate, $endDate, $limit);
return new JsonResult([
'success' => true,
'data' => $topPages,
'limit' => $limit,
'period' => [
'start' => $startDate,
'end' => $endDate,
'days' => $days,
],
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/traffic-sources', method: Method::GET)]
public function getTrafficSources(): JsonResult
{
try {
$days = (int) ($_GET['days'] ?? 30);
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime("-{$days} days"));
$sources = $this->dashboardService->getTrafficSources($startDate, $endDate);
return new JsonResult([
'success' => true,
'data' => $sources,
'period' => [
'start' => $startDate,
'end' => $endDate,
'days' => $days,
],
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/user-behavior', method: Method::GET)]
public function getUserBehavior(): JsonResult
{
try {
$days = (int) ($_GET['days'] ?? 30);
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime("-{$days} days"));
$behavior = $this->reportService->getUserBehavior($startDate, $endDate);
return new JsonResult([
'success' => true,
'data' => $behavior,
'period' => [
'start' => $startDate,
'end' => $endDate,
'days' => $days,
],
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/business-metrics', method: Method::GET)]
public function getBusinessMetrics(): JsonResult
{
try {
$days = (int) ($_GET['days'] ?? 30);
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime("-{$days} days"));
$metrics = $this->reportService->getBusinessMetrics($startDate, $endDate);
return new JsonResult([
'success' => true,
'data' => $metrics,
'period' => [
'start' => $startDate,
'end' => $endDate,
'days' => $days,
],
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/real-time', method: Method::GET)]
public function getRealTimeData(): JsonResult
{
try {
$realTime = $this->realTimeService->getRealTimeData();
return new JsonResult([
'success' => true,
'data' => $realTime,
'timestamp' => time(),
'updated_at' => date('Y-m-d H:i:s'),
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/events', method: Method::GET)]
public function getEvents(): JsonResult
{
try {
$category = $_GET['category'] ?? null;
$limit = (int) ($_GET['limit'] ?? 100);
$offset = (int) ($_GET['offset'] ?? 0);
$days = (int) ($_GET['days'] ?? 7);
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime("-{$days} days"));
$categoryFilter = $category ? AnalyticsCategory::tryFrom($category) : null;
$events = $this->reportService->getEvents($startDate, $endDate, $categoryFilter, $limit, $offset);
$total = $this->reportService->getEventsCount($startDate, $endDate, $categoryFilter);
return new JsonResult([
'success' => true,
'data' => $events,
'pagination' => [
'limit' => $limit,
'offset' => $offset,
'total' => $total,
'has_more' => ($offset + $limit) < $total,
],
'filter' => [
'category' => $category,
'start' => $startDate,
'end' => $endDate,
],
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/export', method: Method::GET)]
public function exportData(): JsonResult
{
try {
$format = $_GET['format'] ?? 'json';
$category = $_GET['category'] ?? null;
$days = (int) ($_GET['days'] ?? 30);
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime("-{$days} days"));
$categoryFilter = $category ? AnalyticsCategory::tryFrom($category) : null;
$data = $this->reportService->exportData($startDate, $endDate, $categoryFilter, $format);
return new JsonResult([
'success' => true,
'data' => $data,
'format' => $format,
'exported_at' => date('Y-m-d H:i:s'),
'period' => [
'start' => $startDate,
'end' => $endDate,
'days' => $days,
],
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
#[Route(path: '/admin/analytics/api/track', method: Method::POST)]
public function trackEvent(): JsonResult
{
try {
$action = $_POST['action'] ?? null;
$category = $_POST['category'] ?? 'user_behavior';
$properties = json_decode($_POST['properties'] ?? '{}', true);
if (! $action) {
return new JsonResult([
'success' => false,
'error' => 'Action is required',
], Status::from(400));
}
$categoryEnum = AnalyticsCategory::tryFrom($category);
if (! $categoryEnum) {
return new JsonResult([
'success' => false,
'error' => 'Invalid category',
], Status::from(400));
}
$this->analyticsCollector->trackAction($action, $categoryEnum, $properties);
return new JsonResult([
'success' => true,
'message' => 'Event tracked successfully',
'tracked_at' => date('Y-m-d H:i:s'),
]);
} catch (\Exception $e) {
return new JsonResult([
'success' => false,
'error' => $e->getMessage(),
], Status::from(500));
}
}
}

View File

@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace App\Application\Analytics\ValueObject;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
/**
* Browser usage breakdown for analytics reporting
@@ -18,7 +20,15 @@ final readonly class BrowserBreakdown
public int $edge,
) {
if ($chrome < 0 || $firefox < 0 || $safari < 0 || $edge < 0) {
throw new \InvalidArgumentException('Browser counts cannot be negative');
throw FrameworkException::create(
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
'Browser counts cannot be negative'
)->withData([
'chrome' => $chrome,
'firefox' => $firefox,
'safari' => $safari,
'edge' => $edge,
]);
}
}

View File

@@ -4,6 +4,9 @@ declare(strict_types=1);
namespace App\Application\Analytics\ValueObject;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
/**
* Business metrics analytics report containing conversion and revenue data
*/
@@ -22,16 +25,22 @@ final readonly class BusinessMetricsReport
public array $funnelData,
) {
// Validate conversions - only check business logic
foreach ($conversions as $count) {
foreach ($conversions as $type => $count) {
if ($count < 0) {
throw new \InvalidArgumentException('Conversion counts cannot be negative');
throw FrameworkException::create(
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
'Conversion counts cannot be negative'
)->withData(['type' => $type, 'count' => $count]);
}
}
// Validate goal completions - only check business logic
foreach ($goalCompletions as $completions) {
foreach ($goalCompletions as $goal => $completions) {
if ($completions < 0) {
throw new \InvalidArgumentException('Goal completion counts cannot be negative');
throw FrameworkException::create(
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
'Goal completion counts cannot be negative'
)->withData(['goal' => $goal, 'completions' => $completions]);
}
}
@@ -39,7 +48,10 @@ final readonly class BusinessMetricsReport
$requiredRevenueFields = ['total', 'currency', 'transactions', 'average_order_value'];
foreach ($requiredRevenueFields as $field) {
if (! array_key_exists($field, $revenue)) {
throw new \InvalidArgumentException("Revenue must contain field: {$field}");
throw FrameworkException::create(
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
"Revenue must contain field: {$field}"
)->withData(['missing_field' => $field, 'available_fields' => array_keys($revenue)]);
}
}
}