crm-summit/app/Controllers/Api/FigmaApi.php
mahdahar 9138e0286a feat(figma): add DB-backed dashboard filters and paginated APIs
- add Figma API endpoints for summary, users, snapshots, comments, and admin sync
- support date and user filters plus pagination for snapshots and comments
- expand sync service and schema to store Figma user ids
- refresh dashboard UI with summary cards, filters, pagination, and sync action
- fold figma_user_id into base migration and remove extra migration
2026-04-28 09:01:32 +07:00

250 lines
6.6 KiB
PHP

<?php
namespace App\Controllers\Api;
use App\Controllers\BaseController;
use App\Libraries\FigmaSyncService;
use CodeIgniter\API\ResponseTrait;
class FigmaApi extends BaseController
{
use ResponseTrait;
private function ensureLoggedIn()
{
if (!session()->get('userid')) {
return $this->respond([
'status' => 'error',
'message' => 'Unauthorized',
], 401);
}
return null;
}
private function ensureAdmin()
{
$level = (int) session()->get('level');
if (!in_array($level, [0, 1, 2], true)) {
return $this->respond([
'status' => 'error',
'message' => 'Forbidden. Admin only.',
], 403);
}
return null;
}
private function normalizeUsername(?string $username): ?string
{
$username = trim((string) $username);
return $username === '' ? null : mb_strtolower($username);
}
public function summary()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$db = \Config\Database::connect();
$file = $db->table('figma_files')->orderBy('id', 'DESC')->get()->getRowArray();
$versionsCount = $db->table('figma_file_versions')->countAllResults();
$commentsCount = $db->table('figma_comments')->countAllResults();
$latestVersion = $db->table('figma_file_versions')->orderBy('created_at_figma', 'DESC')->get()->getRowArray();
$latestComment = $db->table('figma_comments')->selectMax('created_at_figma', 'latest')->get()->getRowArray();
return $this->respond([
'status' => 'success',
'message' => 'Summary fetched',
'data' => [
'file' => $file,
'versions' => $versionsCount,
'comments' => $commentsCount,
'latest_version_at' => $latestVersion['created_at_figma'] ?? null,
'latest_version_label' => $latestVersion['label'] ?? $latestVersion['version'] ?? null,
'latest_version_description' => $latestVersion['description'] ?? null,
'latest_comment_at' => $latestComment['latest'] ?? null,
],
], 200);
}
public function users()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$db = \Config\Database::connect();
$versionUsers = $db->table('figma_file_versions')
->select('user_name')
->where('user_name IS NOT NULL', null, false)
->where('user_name !=', '')
->get()
->getResultArray();
$commentUsers = $db->table('figma_comments')
->select('user_name')
->where('user_name IS NOT NULL', null, false)
->where('user_name !=', '')
->get()
->getResultArray();
$users = [];
foreach (array_merge($versionUsers, $commentUsers) as $row) {
$name = trim((string) ($row['user_name'] ?? ''));
if ($name === '') {
continue;
}
$key = mb_strtolower($name);
if (!isset($users[$key])) {
$users[$key] = $name;
}
}
$rows = array_values($users);
sort($rows, SORT_NATURAL | SORT_FLAG_CASE);
return $this->respond([
'status' => 'success',
'message' => 'Users fetched',
'data' => $rows,
], 200);
}
private function getPaginationParams(): array
{
$page = (int) ($this->request->getGet('page') ?? 1);
if ($page <= 0) {
$page = 1;
}
$perPage = (int) ($this->request->getGet('per_page') ?? $this->request->getGet('limit') ?? 25);
if ($perPage <= 0 || $perPage > 100) {
$perPage = 25;
}
return [$page, $perPage];
}
public function snapshots()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$username = $this->normalizeUsername($this->request->getGet('username'));
[$page, $perPage] = $this->getPaginationParams();
$offset = ($page - 1) * $perPage;
$db = \Config\Database::connect();
$baseBuilder = $db->table('figma_file_versions v')
->join('figma_files f', 'f.id = v.file_id', 'inner');
if (!empty($startDate)) {
$baseBuilder->where('v.created_at_figma >=', $startDate . ' 00:00:00');
}
if (!empty($endDate)) {
$baseBuilder->where('v.created_at_figma <=', $endDate . ' 23:59:59');
}
if (!empty($username)) {
$baseBuilder->where('LOWER(TRIM(v.user_name)) = ' . $db->escape($username), null, false);
}
$total = (int) (clone $baseBuilder)->countAllResults();
$rows = $baseBuilder
->select('v.id, v.figma_version_id, v.version, v.label, v.description, v.name, v.editor_type, v.figma_user_id, v.user_name, v.last_modified_figma, v.created_at_figma, f.file_key, f.last_synced_at')
->orderBy('v.created_at_figma', 'DESC')
->limit($perPage, $offset)
->get()
->getResultArray();
return $this->respond([
'status' => 'success',
'message' => 'Snapshots fetched',
'data' => $rows,
'meta' => [
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'total_pages' => $perPage > 0 ? (int) ceil($total / $perPage) : 0,
],
], 200);
}
public function comments()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$username = $this->normalizeUsername($this->request->getGet('username'));
[$page, $perPage] = $this->getPaginationParams();
$offset = ($page - 1) * $perPage;
$db = \Config\Database::connect();
$baseBuilder = $db->table('figma_comments c')
->join('figma_files f', 'f.id = c.file_id', 'inner');
if (!empty($startDate)) {
$baseBuilder->where('c.created_at_figma >=', $startDate . ' 00:00:00');
}
if (!empty($endDate)) {
$baseBuilder->where('c.created_at_figma <=', $endDate . ' 23:59:59');
}
if (!empty($username)) {
$baseBuilder->where('LOWER(TRIM(c.user_name)) = ' . $db->escape($username), null, false);
}
$total = (int) (clone $baseBuilder)->countAllResults();
$rows = $baseBuilder
->select('c.id, c.figma_comment_id, c.user_name, c.message, c.is_resolved, c.resolved_at, c.created_at_figma, c.client_meta_json, f.file_key')
->orderBy('c.created_at_figma', 'DESC')
->limit($perPage, $offset)
->get()
->getResultArray();
return $this->respond([
'status' => 'success',
'message' => 'Comments fetched',
'data' => $rows,
'meta' => [
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'total_pages' => $perPage > 0 ? (int) ceil($total / $perPage) : 0,
],
], 200);
}
public function sync()
{
if ($response = $this->ensureLoggedIn()) {
return $response;
}
if ($response = $this->ensureAdmin()) {
return $response;
}
$service = new FigmaSyncService();
$result = $service->syncIncremental(1);
$statusCode = $result['success'] ? 200 : 500;
return $this->respond([
'status' => $result['success'] ? 'success' : 'error',
'message' => $result['message'],
'data' => $result['stats'] ?? [],
], $statusCode);
}
}