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
This commit is contained in:
parent
6518f3a9f8
commit
9138e0286a
@ -278,6 +278,7 @@ $routes->group('api/gitea', function($routes) {
|
|||||||
$routes->get('/figma', 'Figma::index');
|
$routes->get('/figma', 'Figma::index');
|
||||||
$routes->group('api/figma', function($routes) {
|
$routes->group('api/figma', function($routes) {
|
||||||
$routes->get('summary', 'Api\FigmaApi::summary');
|
$routes->get('summary', 'Api\FigmaApi::summary');
|
||||||
|
$routes->get('users', 'Api\FigmaApi::users');
|
||||||
$routes->get('snapshots', 'Api\FigmaApi::snapshots');
|
$routes->get('snapshots', 'Api\FigmaApi::snapshots');
|
||||||
$routes->get('comments', 'Api\FigmaApi::comments');
|
$routes->get('comments', 'Api\FigmaApi::comments');
|
||||||
$routes->post('sync', 'Api\FigmaApi::sync');
|
$routes->post('sync', 'Api\FigmaApi::sync');
|
||||||
|
|||||||
@ -35,6 +35,12 @@ class FigmaApi extends BaseController
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function normalizeUsername(?string $username): ?string
|
||||||
|
{
|
||||||
|
$username = trim((string) $username);
|
||||||
|
return $username === '' ? null : mb_strtolower($username);
|
||||||
|
}
|
||||||
|
|
||||||
public function summary()
|
public function summary()
|
||||||
{
|
{
|
||||||
if ($response = $this->ensureLoggedIn()) {
|
if ($response = $this->ensureLoggedIn()) {
|
||||||
@ -63,6 +69,50 @@ class FigmaApi extends BaseController
|
|||||||
], 200);
|
], 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
|
private function getPaginationParams(): array
|
||||||
{
|
{
|
||||||
$page = (int) ($this->request->getGet('page') ?? 1);
|
$page = (int) ($this->request->getGet('page') ?? 1);
|
||||||
@ -86,6 +136,7 @@ class FigmaApi extends BaseController
|
|||||||
|
|
||||||
$startDate = $this->request->getGet('start_date');
|
$startDate = $this->request->getGet('start_date');
|
||||||
$endDate = $this->request->getGet('end_date');
|
$endDate = $this->request->getGet('end_date');
|
||||||
|
$username = $this->normalizeUsername($this->request->getGet('username'));
|
||||||
[$page, $perPage] = $this->getPaginationParams();
|
[$page, $perPage] = $this->getPaginationParams();
|
||||||
$offset = ($page - 1) * $perPage;
|
$offset = ($page - 1) * $perPage;
|
||||||
|
|
||||||
@ -101,9 +152,13 @@ class FigmaApi extends BaseController
|
|||||||
$baseBuilder->where('v.created_at_figma <=', $endDate . ' 23:59:59');
|
$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();
|
$total = (int) (clone $baseBuilder)->countAllResults();
|
||||||
$rows = $baseBuilder
|
$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.last_modified_figma, v.created_at_figma, f.file_key, f.last_synced_at')
|
->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')
|
->orderBy('v.created_at_figma', 'DESC')
|
||||||
->limit($perPage, $offset)
|
->limit($perPage, $offset)
|
||||||
->get()
|
->get()
|
||||||
@ -130,6 +185,7 @@ class FigmaApi extends BaseController
|
|||||||
|
|
||||||
$startDate = $this->request->getGet('start_date');
|
$startDate = $this->request->getGet('start_date');
|
||||||
$endDate = $this->request->getGet('end_date');
|
$endDate = $this->request->getGet('end_date');
|
||||||
|
$username = $this->normalizeUsername($this->request->getGet('username'));
|
||||||
[$page, $perPage] = $this->getPaginationParams();
|
[$page, $perPage] = $this->getPaginationParams();
|
||||||
$offset = ($page - 1) * $perPage;
|
$offset = ($page - 1) * $perPage;
|
||||||
|
|
||||||
@ -145,6 +201,10 @@ class FigmaApi extends BaseController
|
|||||||
$baseBuilder->where('c.created_at_figma <=', $endDate . ' 23:59:59');
|
$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();
|
$total = (int) (clone $baseBuilder)->countAllResults();
|
||||||
$rows = $baseBuilder
|
$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')
|
->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')
|
||||||
|
|||||||
@ -103,6 +103,16 @@ class CreateFigmaTables extends Migration
|
|||||||
'constraint' => 100,
|
'constraint' => 100,
|
||||||
'null' => true,
|
'null' => true,
|
||||||
],
|
],
|
||||||
|
'figma_user_id' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 255,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
|
'user_name' => [
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 255,
|
||||||
|
'null' => true,
|
||||||
|
],
|
||||||
'last_modified_figma' => [
|
'last_modified_figma' => [
|
||||||
'type' => 'DATETIME',
|
'type' => 'DATETIME',
|
||||||
'null' => true,
|
'null' => true,
|
||||||
@ -123,6 +133,8 @@ class CreateFigmaTables extends Migration
|
|||||||
$this->forge->addKey('id', true);
|
$this->forge->addKey('id', true);
|
||||||
$this->forge->addUniqueKey(['file_id', 'figma_version_id']);
|
$this->forge->addUniqueKey(['file_id', 'figma_version_id']);
|
||||||
$this->forge->addKey('file_id');
|
$this->forge->addKey('file_id');
|
||||||
|
$this->forge->addKey('figma_user_id');
|
||||||
|
$this->forge->addKey('user_name');
|
||||||
$this->forge->addKey('created_at_figma');
|
$this->forge->addKey('created_at_figma');
|
||||||
$this->forge->addKey('last_modified_figma');
|
$this->forge->addKey('last_modified_figma');
|
||||||
$this->forge->createTable('figma_file_versions', true);
|
$this->forge->createTable('figma_file_versions', true);
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Database\Migrations;
|
|
||||||
|
|
||||||
use CodeIgniter\Database\Migration;
|
|
||||||
|
|
||||||
class AddFigmaUserIdToFileVersions extends Migration
|
|
||||||
{
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
$this->forge->addColumn('figma_file_versions', [
|
|
||||||
'figma_user_id' => [
|
|
||||||
'type' => 'VARCHAR',
|
|
||||||
'constraint' => 255,
|
|
||||||
'null' => true,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->db->query('CREATE INDEX idx_figma_file_versions_figma_user_id ON figma_file_versions(figma_user_id)');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
$this->db->query('DROP INDEX idx_figma_file_versions_figma_user_id ON figma_file_versions');
|
|
||||||
$this->forge->dropColumn('figma_file_versions', 'figma_user_id');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -184,6 +184,7 @@ class FigmaSyncService
|
|||||||
$description = $version['description'] ?? $version['notes'] ?? $version['message'] ?? null;
|
$description = $version['description'] ?? $version['notes'] ?? $version['message'] ?? null;
|
||||||
$createdAt = $this->normalizeDate($version['created_at'] ?? $version['createdAt'] ?? null);
|
$createdAt = $this->normalizeDate($version['created_at'] ?? $version['createdAt'] ?? null);
|
||||||
$figmaUserId = $version['user']['id'] ?? $version['user_id'] ?? null;
|
$figmaUserId = $version['user']['id'] ?? $version['user_id'] ?? null;
|
||||||
|
$userName = $version['user']['handle'] ?? $version['user']['name'] ?? $version['user_name'] ?? null;
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'file_id' => $fileId,
|
'file_id' => $fileId,
|
||||||
@ -194,6 +195,7 @@ class FigmaSyncService
|
|||||||
'name' => (string) (env('FIGMA_FILE_NAME') ?: 'Figma File'),
|
'name' => (string) (env('FIGMA_FILE_NAME') ?: 'Figma File'),
|
||||||
'editor_type' => $this->normalizeEditorType($version['editorType'] ?? null),
|
'editor_type' => $this->normalizeEditorType($version['editorType'] ?? null),
|
||||||
'figma_user_id' => is_scalar($figmaUserId) ? (string) $figmaUserId : null,
|
'figma_user_id' => is_scalar($figmaUserId) ? (string) $figmaUserId : null,
|
||||||
|
'user_name' => is_scalar($userName) ? (string) $userName : null,
|
||||||
'last_modified_figma' => $createdAt,
|
'last_modified_figma' => $createdAt,
|
||||||
'created_at_figma' => $createdAt,
|
'created_at_figma' => $createdAt,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -19,6 +19,7 @@ class FigmaFileVersionsModel extends Model
|
|||||||
'name',
|
'name',
|
||||||
'editor_type',
|
'editor_type',
|
||||||
'figma_user_id',
|
'figma_user_id',
|
||||||
|
'user_name',
|
||||||
'last_modified_figma',
|
'last_modified_figma',
|
||||||
'created_at_figma',
|
'created_at_figma',
|
||||||
];
|
];
|
||||||
|
|||||||
@ -15,6 +15,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-3 mb-3">
|
<div class="row g-3 mb-3">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">User</label>
|
||||||
|
<select id="filterUser" class="form-select"><option value="">All User</option></select>
|
||||||
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label">Start Date</label>
|
<label class="form-label">Start Date</label>
|
||||||
<input type="date" id="filterStart" class="form-control">
|
<input type="date" id="filterStart" class="form-control">
|
||||||
@ -80,7 +84,7 @@
|
|||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
<th>Version</th>
|
<th>Version</th>
|
||||||
<th>Editor</th>
|
<th>Editor</th>
|
||||||
<th>Figma User ID</th>
|
<th>Username</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
@ -131,6 +135,7 @@
|
|||||||
<?= $this->section('script') ?>
|
<?= $this->section('script') ?>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
const filterUser = document.getElementById('filterUser');
|
||||||
const filterStart = document.getElementById('filterStart');
|
const filterStart = document.getElementById('filterStart');
|
||||||
const filterEnd = document.getElementById('filterEnd');
|
const filterEnd = document.getElementById('filterEnd');
|
||||||
const btnApplyFilter = document.getElementById('btnApplyFilter');
|
const btnApplyFilter = document.getElementById('btnApplyFilter');
|
||||||
@ -168,6 +173,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setDefaultDateRange();
|
setDefaultDateRange();
|
||||||
|
await loadUsers();
|
||||||
await loadSummary();
|
await loadSummary();
|
||||||
await loadTables();
|
await loadTables();
|
||||||
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
||||||
@ -267,6 +273,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
|
|
||||||
function getBaseParams(page, perPage) {
|
function getBaseParams(page, perPage) {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
|
if (filterUser.value) params.set('username', filterUser.value);
|
||||||
if (filterStart.value) params.set('start_date', filterStart.value);
|
if (filterStart.value) params.set('start_date', filterStart.value);
|
||||||
if (filterEnd.value) params.set('end_date', filterEnd.value);
|
if (filterEnd.value) params.set('end_date', filterEnd.value);
|
||||||
params.set('page', String(page));
|
params.set('page', String(page));
|
||||||
@ -274,17 +281,40 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
return params.toString();
|
return params.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSummary() {
|
async function loadUsers() {
|
||||||
const response = await fetch(`<?= base_url('api/figma/summary') ?>`);
|
try {
|
||||||
const result = await response.json();
|
const response = await fetch(`<?= base_url('api/figma/users') ?>`);
|
||||||
if (!response.ok || !result.data) {
|
const result = await response.json();
|
||||||
|
if (!response.ok || !Array.isArray(result.data)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.data.forEach(item => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = item;
|
||||||
|
option.textContent = item;
|
||||||
|
filterUser.appendChild(option);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
currentFileName.innerText = result.data.file?.name || '-';
|
async function loadSummary() {
|
||||||
currentFileVersion.innerText = result.data.latest_version_label || result.data.file?.version || '-';
|
try {
|
||||||
totalSnapshots.innerText = String(result.data.versions ?? 0);
|
const response = await fetch(`<?= base_url('api/figma/summary') ?>`);
|
||||||
totalComments.innerText = String(result.data.comments ?? 0);
|
const result = await response.json();
|
||||||
|
if (!response.ok || !result.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFileName.innerText = result.data.file?.name || '-';
|
||||||
|
currentFileVersion.innerText = result.data.latest_version_label || result.data.file?.version || '-';
|
||||||
|
totalSnapshots.innerText = String(result.data.versions ?? 0);
|
||||||
|
totalComments.innerText = String(result.data.comments ?? 0);
|
||||||
|
} catch (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadTables() {
|
async function loadTables() {
|
||||||
@ -296,84 +326,100 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
const tbody = document.querySelector('#tableVersions tbody');
|
const tbody = document.querySelector('#tableVersions tbody');
|
||||||
tbody.innerHTML = '<tr><td colspan="6">Loading...</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="6">Loading...</td></tr>';
|
||||||
|
|
||||||
const response = await fetch(`<?= base_url('api/figma/snapshots') ?>?${getBaseParams(versionsState.page, versionsState.perPage)}`);
|
try {
|
||||||
const result = await response.json();
|
const response = await fetch(`<?= base_url('api/figma/snapshots') ?>?${getBaseParams(versionsState.page, versionsState.perPage)}`);
|
||||||
if (!response.ok || !Array.isArray(result.data)) {
|
const result = await response.json();
|
||||||
|
if (!response.ok || !Array.isArray(result.data)) {
|
||||||
|
versionsState.total = 0;
|
||||||
|
versionsState.totalPages = 0;
|
||||||
|
totalVersionRows.innerText = '0';
|
||||||
|
tbody.innerHTML = '<tr><td colspan="6">Failed loading snapshots</td></tr>';
|
||||||
|
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
versionsState.total = Number(result.meta?.total ?? result.data.length ?? 0);
|
||||||
|
versionsState.totalPages = Number(result.meta?.total_pages ?? 0);
|
||||||
|
versionsState.page = Number(result.meta?.page ?? versionsState.page);
|
||||||
|
versionsState.perPage = Number(result.meta?.per_page ?? versionsState.perPage);
|
||||||
|
versionsState.loaded = true;
|
||||||
|
totalVersionRows.innerText = String(versionsState.total);
|
||||||
|
|
||||||
|
if (!result.data.length) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="6">No snapshots found</td></tr>';
|
||||||
|
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody.innerHTML = result.data.map(item => {
|
||||||
|
return `<tr>
|
||||||
|
<td>${escapeHtml(item.created_at_figma || '')}</td>
|
||||||
|
<td>${escapeHtml(item.label || item.version || item.figma_version_id || '')}</td>
|
||||||
|
<td>${escapeHtml(item.description || '')}</td>
|
||||||
|
<td>${escapeHtml(item.version || item.figma_version_id || '')}</td>
|
||||||
|
<td>${escapeHtml(item.editor_type || '')}</td>
|
||||||
|
<td>${escapeHtml(item.user_name || '')}</td>
|
||||||
|
</tr>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
||||||
|
} catch (error) {
|
||||||
versionsState.total = 0;
|
versionsState.total = 0;
|
||||||
versionsState.totalPages = 0;
|
versionsState.totalPages = 0;
|
||||||
totalVersionRows.innerText = '0';
|
totalVersionRows.innerText = '0';
|
||||||
tbody.innerHTML = '<tr><td colspan="6">Failed loading snapshots</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="6">Failed loading snapshots</td></tr>';
|
||||||
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
versionsState.total = Number(result.meta?.total ?? result.data.length ?? 0);
|
|
||||||
versionsState.totalPages = Number(result.meta?.total_pages ?? 0);
|
|
||||||
versionsState.page = Number(result.meta?.page ?? versionsState.page);
|
|
||||||
versionsState.perPage = Number(result.meta?.per_page ?? versionsState.perPage);
|
|
||||||
versionsState.loaded = true;
|
|
||||||
totalVersionRows.innerText = String(versionsState.total);
|
|
||||||
|
|
||||||
if (!result.data.length) {
|
|
||||||
tbody.innerHTML = '<tr><td colspan="6">No snapshots found</td></tr>';
|
|
||||||
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody.innerHTML = result.data.map(item => {
|
|
||||||
return `<tr>
|
|
||||||
<td>${escapeHtml(item.created_at_figma || '')}</td>
|
|
||||||
<td>${escapeHtml(item.label || item.version || item.figma_version_id || '')}</td>
|
|
||||||
<td>${escapeHtml(item.description || '')}</td>
|
|
||||||
<td>${escapeHtml(item.version || item.figma_version_id || '')}</td>
|
|
||||||
<td>${escapeHtml(item.editor_type || '')}</td>
|
|
||||||
<td>${escapeHtml(item.figma_user_id || '')}</td>
|
|
||||||
</tr>`;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
updatePager(versionPrev, versionNext, versionPageInfo, versionsState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadComments() {
|
async function loadComments() {
|
||||||
const tbody = document.querySelector('#tableComments tbody');
|
const tbody = document.querySelector('#tableComments tbody');
|
||||||
tbody.innerHTML = '<tr><td colspan="4">Loading...</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="4">Loading...</td></tr>';
|
||||||
|
|
||||||
const response = await fetch(`<?= base_url('api/figma/comments') ?>?${getBaseParams(commentsState.page, commentsState.perPage)}`);
|
try {
|
||||||
const result = await response.json();
|
const response = await fetch(`<?= base_url('api/figma/comments') ?>?${getBaseParams(commentsState.page, commentsState.perPage)}`);
|
||||||
if (!response.ok || !Array.isArray(result.data)) {
|
const result = await response.json();
|
||||||
|
if (!response.ok || !Array.isArray(result.data)) {
|
||||||
|
commentsState.total = 0;
|
||||||
|
commentsState.totalPages = 0;
|
||||||
|
totalCommentRows.innerText = '0';
|
||||||
|
tbody.innerHTML = '<tr><td colspan="4">Failed loading comments</td></tr>';
|
||||||
|
updatePager(commentPrev, commentNext, commentPageInfo, commentsState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
commentsState.total = Number(result.meta?.total ?? result.data.length ?? 0);
|
||||||
|
commentsState.totalPages = Number(result.meta?.total_pages ?? 0);
|
||||||
|
commentsState.page = Number(result.meta?.page ?? commentsState.page);
|
||||||
|
commentsState.perPage = Number(result.meta?.per_page ?? commentsState.perPage);
|
||||||
|
commentsState.loaded = true;
|
||||||
|
totalCommentRows.innerText = String(commentsState.total);
|
||||||
|
|
||||||
|
if (!result.data.length) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="4">No comments found</td></tr>';
|
||||||
|
updatePager(commentPrev, commentNext, commentPageInfo, commentsState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody.innerHTML = result.data.map(item => {
|
||||||
|
const comment = escapeHtml((item.message || '').slice(0, 160));
|
||||||
|
const status = item.is_resolved ? 'Resolved' : 'Open';
|
||||||
|
return `<tr>
|
||||||
|
<td>${escapeHtml(item.created_at_figma || '')}</td>
|
||||||
|
<td>${escapeHtml(item.user_name || '')}</td>
|
||||||
|
<td>${comment}</td>
|
||||||
|
<td>${status}</td>
|
||||||
|
</tr>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
updatePager(commentPrev, commentNext, commentPageInfo, commentsState);
|
||||||
|
} catch (error) {
|
||||||
commentsState.total = 0;
|
commentsState.total = 0;
|
||||||
commentsState.totalPages = 0;
|
commentsState.totalPages = 0;
|
||||||
totalCommentRows.innerText = '0';
|
totalCommentRows.innerText = '0';
|
||||||
tbody.innerHTML = '<tr><td colspan="4">Failed loading comments</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="4">Failed loading comments</td></tr>';
|
||||||
updatePager(commentPrev, commentNext, commentPageInfo, commentsState);
|
updatePager(commentPrev, commentNext, commentPageInfo, commentsState);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commentsState.total = Number(result.meta?.total ?? result.data.length ?? 0);
|
|
||||||
commentsState.totalPages = Number(result.meta?.total_pages ?? 0);
|
|
||||||
commentsState.page = Number(result.meta?.page ?? commentsState.page);
|
|
||||||
commentsState.perPage = Number(result.meta?.per_page ?? commentsState.perPage);
|
|
||||||
commentsState.loaded = true;
|
|
||||||
totalCommentRows.innerText = String(commentsState.total);
|
|
||||||
|
|
||||||
if (!result.data.length) {
|
|
||||||
tbody.innerHTML = '<tr><td colspan="4">No comments found</td></tr>';
|
|
||||||
updatePager(commentPrev, commentNext, commentPageInfo, commentsState);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody.innerHTML = result.data.map(item => {
|
|
||||||
const comment = escapeHtml((item.message || '').slice(0, 160));
|
|
||||||
const status = item.is_resolved ? 'Resolved' : 'Open';
|
|
||||||
return `<tr>
|
|
||||||
<td>${escapeHtml(item.created_at_figma || '')}</td>
|
|
||||||
<td>${escapeHtml(item.user_name || '')}</td>
|
|
||||||
<td>${comment}</td>
|
|
||||||
<td>${status}</td>
|
|
||||||
</tr>`;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
updatePager(commentPrev, commentNext, commentPageInfo, commentsState);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user