# Activity Module

> Global activity feed + timeline widget.
> Reuses `activity_logs` table seeded by ActivityEngine (Phase 1).
> ActivityEngine's `HasActivityTimeline` trait handles per-entity logging.

---

## Routes

```php
// CrmApp/Activity/routes/tenant.php
Route::prefix('crm')->name('crm.')->group(function () {
    Route::middleware(['web', ..., 'auth', 'verified'])->group(function () {
        Route::get('activity', [ActivityController::class, 'index'])->name('activity.index');
        Route::post('activity/{entity_type}/{entity_id}', [ActivityController::class, 'store'])
            ->name('activity.store');
    });
});
```

---

## Controller

```php
namespace CrmApp\Activity\Http\Controllers;

use CoreApp\Models\ActivityLog;

class ActivityController extends Controller
{
    public function index(Request $request): Response|JsonResponse
    {
        $logs = ActivityLog::filter($request->all())
            ->with('user', 'subject')
            ->latest()
            ->simplePaginate(50);

        if ($request->wantsJson()) {
            return response()->json([
                'data'   => ActivityLogResource::collection($logs->items()),
                'meta'   => simple_pagination_meta($logs),
                'status' => 'success',
            ]);
        }

        return Inertia::render('Activity/Index', [
            'activityData' => [
                'data'        => ActivityLogResource::collection($logs->items()),
                'meta'        => simple_pagination_meta($logs),
                'queryParams' => $request->all(),
            ],
        ]);
    }

    // Log a manual note/call/meeting on an entity
    public function store(Request $request, string $entityType, int $entityId): RedirectResponse
    {
        $request->validate([
            'type'    => 'required|in:note,call,meeting,email',
            'content' => 'required|string|max:2000',
        ]);

        ActivityLog::create([
            'subject_type' => $entityType,
            'subject_id'   => $entityId,
            'user_id'      => auth()->id(),
            'type'         => $request->type,
            'content'      => $request->content,
            'meta'         => [],
        ]);

        return redirect()->back()->with('success', 'Activity logged.');
    }
}
```

---

## `ActivityLog` Filter

```php
class ActivityLogFilter extends ModelFilter
{
    use CommonFilter;

    public function subjectType(string $v): self { return $this->where('subject_type', $v); }
    public function subjectId(int $v): self      { return $this->where('subject_id', $v); }
    public function userId(int $v): self         { return $this->where('user_id', $v); }
    public function type(string $v): self        { return $this->where('type', $v); }
}
```

---

## Timeline Widget Component

Shared TSX component for use in Lead/Show.tsx, Deal/Show.tsx, Contact/Show.tsx.

```tsx
// resources/js/components/crm/activity-timeline.tsx
interface TimelineEvent {
    id: number;
    type: 'note' | 'call' | 'meeting' | 'email' | 'stage_change' | 'created' | 'auto_assign';
    content: string;
    meta: Record<string, unknown>;
    user: { id: number; name: string };
    created_at: string;
}

export function ActivityTimeline({ events, entityType, entityId }: {
    events: TimelineEvent[];
    entityType: string;
    entityId: number;
}) {
    // Vertical timeline with icon per type, user avatar, timestamp
    // Add note form at top (POST crm.activity.store)
}
```

---

## `ActivityLogResource`

```php
public function toArray($request): array
{
    return [
        'id'           => $this->id,
        'type'         => $this->type,
        'content'      => $this->content,
        'meta'         => $this->meta,
        'subject_type' => $this->subject_type,
        'subject_id'   => $this->subject_id,
        'user'         => $this->whenLoaded('user', fn () => [
            'id' => $this->user->id, 'name' => $this->user->name,
        ]),
        'created_at'   => $this->created_at->toDateTimeString(),
    ];
}
```

---

## Pages

| Page | Path |
|------|------|
| Global feed | `CrmApp/Activity/resources/assets/js/pages/Activity/Index.tsx` |

Feed: DataTable-style list with type/user/entity filters.
Breadcrumbs: `Home → CRM → Activity`
No show page — detail context provided by subject entity's Show page timeline widget.

---

## Required Log Points per Entity

Log these automatically via `HasActivityTimeline` hooks (ActivityEngine, Phase 1):

| Entity | Event | Log type | Meta keys |
|--------|-------|----------|-----------|
| Lead | `created` | `created` | — |
| Lead | `convert()` | `conversion` | `{contact_id, deal_id}` |
| Lead/Deal | `moveToStage()` | `stage_change` | `{from_stage_id, to_stage_id}` |
| Lead/Deal | `AssignToUser` action | `auto_assign` | `{assigned_to, round_robin_index}` |
