|
|
@@ -34,6 +34,42 @@
|
|
|
let selectedUser = null;
|
|
|
let showDetails = false;
|
|
|
|
|
|
+ let detailsLoading = false;
|
|
|
+ let detailsError = '';
|
|
|
+ let selectedUserInfo = null;
|
|
|
+
|
|
|
+ function getUserIdFromRow(row) {
|
|
|
+ return (
|
|
|
+ row?.__raw?.user_id ??
|
|
|
+ row?.__raw?.userId ??
|
|
|
+ row?.__raw?.id ??
|
|
|
+ row?.user_id ??
|
|
|
+ row?.userId ??
|
|
|
+ row?.id ??
|
|
|
+ row?.__raw?.id ??
|
|
|
+ row?.__raw?.userId ??
|
|
|
+ row?.__raw?.user_id
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ function formatBirthdate(epoch) {
|
|
|
+ if (epoch == null || epoch === '') return '-';
|
|
|
+ const n = Number(epoch);
|
|
|
+ if (!Number.isFinite(n) || n <= 0) return '-';
|
|
|
+ const ms = n < 1e12 ? n * 1000 : n;
|
|
|
+ const d = new Date(ms);
|
|
|
+ if (Number.isNaN(d.getTime())) return '-';
|
|
|
+ return d.toLocaleDateString('pt-BR');
|
|
|
+ }
|
|
|
+
|
|
|
+ function closeDetails() {
|
|
|
+ showDetails = false;
|
|
|
+ selectedUser = null;
|
|
|
+ selectedUserInfo = null;
|
|
|
+ detailsError = '';
|
|
|
+ detailsLoading = false;
|
|
|
+ }
|
|
|
+
|
|
|
function handleAddTop() {
|
|
|
showCreate = true;
|
|
|
}
|
|
|
@@ -150,6 +186,46 @@
|
|
|
selectedUser = row.__raw ?? row;
|
|
|
//console.log('clicked user details:', selectedUser);
|
|
|
showDetails = true;
|
|
|
+ detailsError = '';
|
|
|
+ selectedUserInfo = null;
|
|
|
+ const userId = getUserIdFromRow(row);
|
|
|
+ if (userId != null) loadUserInfo(userId);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function loadUserInfo(userId) {
|
|
|
+ detailsLoading = true;
|
|
|
+ detailsError = '';
|
|
|
+ try {
|
|
|
+ const res = await fetch(`${apiUrl}/user/info`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'content-type': 'application/json',
|
|
|
+ ...( $authToken ? { Authorization: `Bearer ${$authToken}` } : {} )
|
|
|
+ },
|
|
|
+ body: JSON.stringify({ user_id: userId })
|
|
|
+ });
|
|
|
+ const raw = await res.text();
|
|
|
+ let body = null;
|
|
|
+ if (raw) {
|
|
|
+ try {
|
|
|
+ body = JSON.parse(raw);
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Resposta inválida do endpoint /user/info:', err);
|
|
|
+ throw new Error('Resposta inválida do servidor.');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const isSuccess = body?.status === 'ok' || body?.status === 'success' || body?.code === 'S_OK';
|
|
|
+ if (!res.ok || !isSuccess) {
|
|
|
+ throw new Error(body?.message ?? body?.msg ?? 'Falha ao carregar detalhes do usuário.');
|
|
|
+ }
|
|
|
+ selectedUserInfo = body?.data ?? null;
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Erro ao carregar detalhes do usuário:', err);
|
|
|
+ detailsError = err?.message ?? 'Falha ao carregar detalhes do usuário.';
|
|
|
+ selectedUserInfo = null;
|
|
|
+ } finally {
|
|
|
+ detailsLoading = false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async function confirmDelete() {
|
|
|
@@ -248,37 +324,43 @@
|
|
|
role="button"
|
|
|
tabindex="0"
|
|
|
on:click={(e) => {
|
|
|
- if (e.target === e.currentTarget) showDetails = false;
|
|
|
+ if (e.target === e.currentTarget) closeDetails();
|
|
|
}}
|
|
|
on:keydown={(e) => {
|
|
|
- if (e.key === 'Escape' || e.key === 'Enter' || e.key === ' ') showDetails = false;
|
|
|
+ if (e.key === 'Escape' || e.key === 'Enter' || e.key === ' ') closeDetails();
|
|
|
}}
|
|
|
>
|
|
|
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg w-full max-w-lg p-6" role="dialog" aria-modal="true">
|
|
|
<div class="flex items-center justify-between mb-4">
|
|
|
<h4 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Detalhes do usuário</h4>
|
|
|
- <button class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" on:click={() => showDetails = false}>✕</button>
|
|
|
+ <button class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" on:click={closeDetails}>✕</button>
|
|
|
</div>
|
|
|
- {#if selectedUser}
|
|
|
+ {#if detailsLoading}
|
|
|
+ <div class="text-sm text-gray-600 dark:text-gray-300">Carregando detalhes...</div>
|
|
|
+ {:else if detailsError}
|
|
|
+ <div class="text-sm text-red-600 dark:text-red-400">{detailsError}</div>
|
|
|
+ {:else if selectedUserInfo}
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm">
|
|
|
- <div><span class="text-gray-500">ID:</span> <span class="dark:text-gray-100">{selectedUser.userId}</span></div>
|
|
|
- <div><span class="text-gray-500">Nome:</span> <span class="dark:text-gray-100">{selectedUser.userName}</span></div>
|
|
|
- <div><span class="text-gray-500">E-mail:</span> <span class="dark:text-gray-100">{selectedUser.userEmail}</span></div>
|
|
|
- <div><span class="text-gray-500">Telefone:</span> <span class="dark:text-gray-100">{selectedUser.userPhone}</span></div>
|
|
|
- <div><span class="text-gray-500">CPF:</span> <span class="dark:text-gray-100">{selectedUser.userCpf}</span></div>
|
|
|
- <div><span class="text-gray-500">Data Nasc.:</span> <span class="dark:text-gray-100">{selectedUser.userBirthdate}</span></div>
|
|
|
- <div><span class="text-gray-500">KYC:</span> <span class="dark:text-gray-100">{selectedUser.userKyc}</span></div>
|
|
|
- <div><span class="text-gray-500">Papel (roleId):</span> <span class="dark:text-gray-100">{selectedUser.roleId}</span></div>
|
|
|
- <div><span class="text-gray-500">Status:</span> <span class="dark:text-gray-100">{selectedUser.userFlag}</span></div>
|
|
|
- <div class="sm:col-span-2"><span class="text-gray-500">Endereço:</span> <span class="dark:text-gray-100">{selectedUser.userAddress}</span></div>
|
|
|
- <div><span class="text-gray-500">Cidade:</span> <span class="dark:text-gray-100">{selectedUser.userCity}</span></div>
|
|
|
- <div><span class="text-gray-500">Estado:</span> <span class="dark:text-gray-100">{selectedUser.userState}</span></div>
|
|
|
- <div><span class="text-gray-500">CEP:</span> <span class="dark:text-gray-100">{selectedUser.userZip}</span></div>
|
|
|
- <div><span class="text-gray-500">País:</span> <span class="dark:text-gray-100">{selectedUser.userCountry}</span></div>
|
|
|
+ <div><span class="text-gray-500">ID:</span> <span class="dark:text-gray-100">{selectedUserInfo.id ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">Nome:</span> <span class="dark:text-gray-100">{selectedUserInfo.nome ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">E-mail:</span> <span class="dark:text-gray-100">{selectedUserInfo.email ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">Telefone:</span> <span class="dark:text-gray-100">{selectedUserInfo.telefone ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">CPF:</span> <span class="dark:text-gray-100">{selectedUserInfo.cpf ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">Data Nasc.:</span> <span class="dark:text-gray-100">{formatBirthdate(selectedUserInfo.dataNasc)}</span></div>
|
|
|
+ <div><span class="text-gray-500">KYC:</span> <span class="dark:text-gray-100">{selectedUserInfo.kyc ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">Papel (roleId):</span> <span class="dark:text-gray-100">{selectedUserInfo.roleId ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">Status:</span> <span class="dark:text-gray-100">{selectedUserInfo.status ?? '-'}</span></div>
|
|
|
+ <div class="sm:col-span-2"><span class="text-gray-500">Endereço:</span> <span class="dark:text-gray-100">{selectedUserInfo.endereco ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">Cidade:</span> <span class="dark:text-gray-100">{selectedUserInfo.cidade ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">Estado:</span> <span class="dark:text-gray-100">{selectedUserInfo.estado ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">CEP:</span> <span class="dark:text-gray-100">{selectedUserInfo.cep ?? '-'}</span></div>
|
|
|
+ <div><span class="text-gray-500">País:</span> <span class="dark:text-gray-100">{selectedUserInfo.pais ?? '-'}</span></div>
|
|
|
</div>
|
|
|
+ {:else}
|
|
|
+ <div class="text-sm text-gray-600 dark:text-gray-300">Nenhum detalhe disponível.</div>
|
|
|
{/if}
|
|
|
<div class="mt-5 flex justify-end">
|
|
|
- <button class="px-4 py-2 rounded bg-blue-600 hover:bg-blue-700 text-white" on:click={() => showDetails = false}>Fechar</button>
|
|
|
+ <button class="px-4 py-2 rounded bg-blue-600 hover:bg-blue-700 text-white" on:click={closeDetails}>Fechar</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|