> Źródło: https://redai.pl/dev/haki-klasy-src-narzedzie-php/ (dokumentacja modułów portalu redAi). Cała dokumentacja w jednym pliku: https://redai.pl/dev/moduly.md

## Haki klasy `src/Narzedzie.php`

Klasa haków to `Narzedzia\<Pascal>\Narzedzie extends App\Narzedzia\Narzedzie` w pliku `src/Narzedzie.php` (albo klasa z pola `klasa` w manifeście, też podklasa `App\Narzedzia\Narzedzie`). Nadpisujecie tylko to, czego moduł używa. Każdy hak portal woła w `try`: błąd w module trafia do logu, portal działa dalej.

```php
<?php

namespace Narzedzia\FakturyKosztowe;

use App\Models\User;
use App\Narzedzia\Narzedzie as Baza;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Http\Request;

class Narzedzie extends Baza
{
    // ... haki opisane niżej
}
```

### rekordyWMenu

```php
public function rekordyWMenu(User $user): array
```

Rekordy, które osoba przypięła do menu po lewej. Zwracacie listę `[['url' => ..., 'nazwa' => ...]]`. Ikona = `ikona_svg` z manifestu.

```php
public function rekordyWMenu(User $user): array
{
    return Faktura::where('user_id', $user->id)->where('w_menu', true)->orderBy('nazwa')->get()
        ->map(fn ($f) => ['url' => route('faktury_kosztowe.show', $f), 'nazwa' => $f->nazwa])
        ->all();
}
```

### blokPromptu

```php
public function blokPromptu(int $userId): array
```

Linie tekstu dopisywane do promptu każdej tury czatu tej osoby. Tak moduł mówi czatowi, co może zrobić dla użytkownika. Pusta tablica = nic. Trzymajcie to krótko: te linie idą z każdą wiadomością.

```php
public function blokPromptu(int $userId): array
{
    $ile = Faktura::where('user_id', $userId)->where('status', 'nowa')->count();
    if ($ile === 0) {
        return [];
    }

    return ["Użytkownik ma {$ile} nowych faktur kosztowych w module Faktury kosztowe (adres: /narzedzia/faktury-kosztowe)."];
}
```

### uzycie

```php
public function uzycie(): ?array
```

Skąd karta modułu w Modułach liczy „kto i kiedy korzystał”. `null` = nie liczymy.

```php
public function uzycie(): ?array
{
    return [
        'tabela' => 'faktury_kosztowe',   // własna tabela modułu
        'czas' => 'created_at',           // kolumna z datą
        'co' => 'faktur',                 // „12 faktur”
        'kto' => 'user_id',               // kolumna z id autora, domyślnie user_id
        // 'warunek' => ['status' => 'zatwierdzona'],
    ];
}
```

### modeleAdresow

```php
public function modeleAdresow(): array
```

Modele z kolumną `adres`, które zajmują krótkie adresy `/<adres>` we wspólnej przestrzeni portalu. Dzięki temu `AdresyPubliczne::zajety()` nie wyda tego samego adresu dwóm modułom.

```php
public function modeleAdresow(): array
{
    return [Oferta::class];
}
```

### pokazAdres

```php
public function pokazAdres(Request $request, string $adres): mixed
```

Wejście na `https://<instancja>/<adres>` metodą GET, **bez logowania**. Portal pyta po kolei włączone moduły. Zwracacie odpowiedź albo `null`, gdy adres nie jest Wasz. O tym, kto może zobaczyć rekord, decydujecie sami.

```php
public function pokazAdres(Request $request, string $adres): mixed
{
    $oferta = Oferta::where('adres', $adres)->first();
    if (! $oferta) {
        return null;
    }
    abort_unless($oferta->opublikowana, 404);

    return view('faktury_kosztowe::oferta', compact('oferta'));
}
```

### wyslijNaAdres

```php
public function wyslijNaAdres(Request $request, string $adres): mixed
```

Wysłanie na `/<adres>` metodą POST, bez logowania, z limitem 10 wysłań na minutę. Zwracacie odpowiedź albo `null`. Walidujcie wszystko, co przychodzi.

```php
public function wyslijNaAdres(Request $request, string $adres): mixed
{
    $oferta = Oferta::where('adres', $adres)->where('opublikowana', true)->first();
    if (! $oferta) {
        return null;
    }
    $dane = $request->validate(['email' => 'required|email|max:190', 'pytanie' => 'required|string|max:5000']);
    $oferta->pytania()->create($dane);

    return back()->with('status', 'Dziękujemy, odpowiemy mailem.');
}
```

### harmonogram

```php
public function harmonogram(Schedule $schedule): void
```

Zadania cykliczne. Portal woła ten hak tylko dla **włączonych** modułów.

```php
public function harmonogram(Schedule $schedule): void
{
    $schedule->command('faktury-kosztowe:importuj')->dailyAt('06:00')->withoutOverlapping();
}
```

Komendę artisan piszecie w `src/` i wpisujecie jej klasę w `komendy` manifestu. Nazwę komendy zacznijcie od klucza modułu.

