diff --git a/frontend/src/components/HistoryTable.tsx b/frontend/src/components/HistoryTable.tsx new file mode 100644 index 0000000..bd976bf --- /dev/null +++ b/frontend/src/components/HistoryTable.tsx @@ -0,0 +1,42 @@ +interface Column { + key: string + label: string +} + +interface Props { + columns: Column[] + rows: Record[] + emptyText: string +} + +/** Tabla genérica de solo lectura para los historiales (seguridad, transacciones, baneos). */ +export function HistoryTable({ columns, rows, emptyText }: Props) { + return ( + + + + {columns.map((c) => ( + + ))} + + + + {rows.length > 0 ? ( + rows.map((row, i) => ( + + {columns.map((c) => ( + + ))} + + )) + ) : ( + + + + )} + +
{c.label}
{String(row[c.key] ?? '')}
+ {emptyText} +
+ ) +} diff --git a/frontend/src/entries/history_table.tsx b/frontend/src/entries/history_table.tsx new file mode 100644 index 0000000..01ba4d2 --- /dev/null +++ b/frontend/src/entries/history_table.tsx @@ -0,0 +1,17 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { HistoryTable } from '../components/HistoryTable' + +// Entry genérico para los historiales de solo lectura. La plantilla define las +// columnas en data-columns (JSON) y las filas en un json_script. +const el = document.getElementById('history-table-app') +if (el) { + const dEl = document.getElementById('history-table-data') + const rows = dEl ? JSON.parse(dEl.textContent || '[]') : [] + const columns = JSON.parse(el.dataset.columns || '[]') + createRoot(el).render( + + + , + ) +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 0814158..6f71133 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -29,6 +29,7 @@ export default defineConfig({ transfer_character: resolve(__dirname, 'src/entries/transfer_character.tsx'), rename_guild: resolve(__dirname, 'src/entries/rename_guild.tsx'), vote_points: resolve(__dirname, 'src/entries/vote_points.tsx'), + history_table: resolve(__dirname, 'src/entries/history_table.tsx'), }, }, }, diff --git a/home/templates/partials/security_history.html b/home/templates/partials/security_history.html index d62b8b0..1fcbe34 100644 --- a/home/templates/partials/security_history.html +++ b/home/templates/partials/security_history.html @@ -11,53 +11,16 @@ -
-

Historial de actividad

-
- - - {% if login_attempts %} - {% for attempt in login_attempts %} - - - - - - - {% endfor %} - {% else %} - - - - {% endif %} - -
{{ attempt.username }} {{ attempt.timestamp|date:"d-m-Y H:i:s" }}{{ attempt.status }}{{ attempt.ip_address }}
No hay actividad
-
-
-

Historial de conexiones

- - - - - - - - - - - {% for attempt in login_attempts %} - - - - - - - {% endfor %} - -
UsuarioEstadoIPFecha
{{ attempt.username }}{{ attempt.status }}{{ attempt.ip_address }}{{ attempt.timestamp|date:"d-m-Y H:i:s" }}
+ {% load django_vite %} + +
+ {{ login_attempts_data|json_script:"history-table-data" }} + {% vite_asset 'src/entries/history_table.tsx' %}
diff --git a/home/templates/partials/trans_history.html b/home/templates/partials/trans_history.html index 28618be..bcf0e29 100644 --- a/home/templates/partials/trans_history.html +++ b/home/templates/partials/trans_history.html @@ -22,42 +22,13 @@

Historial de transacciones

- - - - - - - - - - - - - - - - {% if transactions %} - {% for transaction in transactions %} - - - - - - - - - - - - {% endfor %} - {% else %} - - - - {% endif %} - -
ID de CuentaUsuario PersonajeIPEmailProductoMontoFechaModo
{{ transaction.account_id }} {{ transaction.username }} {{ transaction.character_name }}{{ transaction.acore_ip }} {{ transaction.email }} {{ transaction.product_name }}{{ transaction.amount }} €{{ transaction.timestamp|date:"d-m-Y H:i:s" }}{{ transaction.mode }}
No hay movimientos
+ {% load django_vite %} + +
+ {{ transactions_data|json_script:"history-table-data" }} + {% vite_asset 'src/entries/history_table.tsx' %}
diff --git a/home/views/history.py b/home/views/history.py index 2ae4e37..31ffee6 100644 --- a/home/views/history.py +++ b/home/views/history.py @@ -68,4 +68,17 @@ def security_history_view(request): # Obtener los últimos 20 intentos de inicio de sesión login_attempts = LoginAttempt.objects.filter(username=username).order_by('-timestamp')[:20] - return render(request, 'account/security_history.html', {'login_attempts': login_attempts}) + login_attempts_data = [ + { + 'username': a.username, + 'status': a.status, + 'ip_address': a.ip_address, + 'timestamp': a.timestamp.strftime('%d-%m-%Y %H:%M:%S') if a.timestamp else '', + } + for a in login_attempts + ] + + return render(request, 'account/security_history.html', { + 'login_attempts': login_attempts, + 'login_attempts_data': login_attempts_data, + }) diff --git a/home/views/points.py b/home/views/points.py index 1d8375f..d54736f 100644 --- a/home/views/points.py +++ b/home/views/points.py @@ -86,8 +86,26 @@ def trans_history_view(request): else: transactions = [] + transactions_data = [ + { + 'account_id': t.account_id, + 'username': t.username, + 'character_name': t.character_name, + 'acore_ip': t.acore_ip, + 'email': t.email, + 'product_name': t.product_name, + 'amount': f"{t.amount} €", + 'timestamp': t.timestamp.strftime('%d-%m-%Y %H:%M:%S') if t.timestamp else '', + 'mode': t.mode, + } + for t in transactions + ] + # Renderizamos la vista con el historial de transacciones - return render(request, 'account/trans_history.html', {'transactions': transactions}) + return render(request, 'account/trans_history.html', { + 'transactions': transactions, + 'transactions_data': transactions_data, + }) def trade_points_view(request): """ Vista para la página de intercambio de puntos.