feat: phase 6 — dashboard widgets, sales & stock reports, user management, activity log

This commit is contained in:
sean
2026-08-25 19:25:40 +07:00
parent 28937a786e
commit feba1ec839
18 changed files with 1011 additions and 5 deletions
+38 -3
View File
@@ -72,10 +72,45 @@ class InsForgeClient
public function countRecords(string $table, array $filters = []): int
{
$query = $this->buildFilters($filters);
$response = $this->request('HEAD', "/api/database/records/{$table}", query: array_merge($query, ['limit' => 1]));
$response = $this->send(
'GET',
"/api/database/records/{$table}",
query: array_merge($this->buildFilters($filters), ['limit' => 0]),
headers: ['Prefer' => 'count=exact'],
);
return (int) ($response->header('Content-Range') ? explode('/', explode('-', $response->header('Content-Range'))[1])[0] : 0);
return $this->parseTotalFromRange($response->header('Content-Range'));
}
/**
* Jalankan SQL read-only/administratif via management API (admin key).
*
* @return list<array<string, mixed>>
*/
public function rawSql(string $query): array
{
$response = Http::baseUrl(rtrim($this->baseUrl, '/'))
->timeout($this->timeout)
->withToken($this->apiKey)
->post('/api/database/advance/rawsql', ['query' => $query]);
throw_unless($response->successful(), fn () => InsForgeException::fromResponse(
$response->status(),
$response->json() ?? $response->body(),
));
return $response->json('rows') ?? [];
}
private function parseTotalFromRange(?string $contentRange): int
{
if (! filled($contentRange)) {
return 0;
}
$total = explode('/', $contentRange)[1] ?? '';
return ctype_digit($total) ? (int) $total : 0;
}
public function rpc(string $function, array $args = [], bool $asUser = true): mixed