diff --git a/.github/workflows/quality-pipeline.yml b/.github/workflows/quality-pipeline.yml
index 5aaadf7..a9826c9 100644
--- a/.github/workflows/quality-pipeline.yml
+++ b/.github/workflows/quality-pipeline.yml
@@ -645,7 +645,7 @@ jobs:
FAIL=0
if find . \( -name "*.js" -o -name "*.ts" -o -name "*.vue" -o -name "*.py" -o -name "*.java" \) \
- -not -path "*/node_modules/*" -not -path "*/dist/*" 2>/dev/null | grep -q .; then
+ -not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/tests/*" -not -path "*/.venv/*" 2>/dev/null | grep -q .; then
DETECTED=1
else
DETECTED=0
@@ -654,7 +654,7 @@ jobs:
# Unconditional frontend router guard bypass
if grep -rEn "next\(true\)|beforeEach.*return true|navigate\(.*\{.*replace.*\}\)" \
--include="*.js" --include="*.ts" --include="*.vue" . 2>/dev/null \
- | grep -v node_modules | grep -v "//"; then
+ | grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//"; then
echo "::warning::Possible unconditional navigation guard bypass detected"
fi
@@ -662,14 +662,14 @@ jobs:
if grep -rEn "password\s*[=:]\s*['\"][^'\"]{4,}['\"]" \
--include="*.js" --include="*.ts" --include="*.vue" --include="*.py" --include="*.java" \
. 2>/dev/null \
- | grep -v node_modules \
+ | grep -vE "node_modules|tests/|\.venv|dist/" \
| grep -v "//" \
| grep -vE "[[:space:]]:[a-zA-Z][a-zA-Z0-9_-]*=" \
| grep -q .; then
grep -rEn "password\s*[=:]\s*['\"][^'\"]{4,}['\"]" \
--include="*.js" --include="*.ts" --include="*.vue" --include="*.py" --include="*.java" \
. 2>/dev/null \
- | grep -v node_modules | grep -v "//" \
+ | grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//" \
| grep -vE "[[:space:]]:[a-zA-Z][a-zA-Z0-9_-]*="
echo "::error::Hardcoded password value detected in source code"
FAIL=1
@@ -678,14 +678,14 @@ jobs:
# eval() / exec() usage
if grep -rEn "\beval\(|\bexec\(" \
--include="*.js" --include="*.ts" --include="*.vue" --include="*.py" \
- . 2>/dev/null | grep -v node_modules | grep -v "//"; then
+ . 2>/dev/null | grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//"; then
echo "::warning::eval()/exec() usage detected — review for potential code injection risk"
fi
# Disabled SSL/TLS certificate verification
if grep -rEn "rejectUnauthorized\s*:\s*false|verify\s*=\s*False|ssl_verify\s*=\s*false" \
--include="*.js" --include="*.ts" --include="*.py" --include="*.java" \
- . 2>/dev/null | grep -v node_modules | grep -v "//"; then
+ . 2>/dev/null | grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//"; then
echo "::error::SSL/TLS certificate verification disabled — man-in-the-middle attack risk"
FAIL=1
fi
@@ -693,7 +693,7 @@ jobs:
# Sensitive tokens in URL query strings
if grep -rEn "[?&](token|api_key|apikey|access_token|secret)=" \
--include="*.js" --include="*.ts" --include="*.vue" --include="*.py" \
- . 2>/dev/null | grep -v node_modules | grep -v "//"; then
+ . 2>/dev/null | grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//"; then
echo "::warning::Sensitive token/key passed in URL query string — use Authorization header instead."
fi
diff --git a/Templates/Details/details_base.html b/Templates/Details/details_base.html
index 3412911..8eefb20 100644
--- a/Templates/Details/details_base.html
+++ b/Templates/Details/details_base.html
@@ -48,7 +48,6 @@
{{ content.title }}
data-contingut-id="{{ content.contingut.id }}"
data-api-id="{{ api.id }}">
{{ api.name|default:api.port }}
-
{% empty %}
{% trans "No hay información de API disponible." %}
@@ -137,15 +136,35 @@ {% trans "Deja tu opinión" %}
✅ {% trans "Añadido a tu historial" %}
{% endblock %}
\ No newline at end of file
diff --git a/Templates/Details/details_serie.html b/Templates/Details/details_serie.html
index 9eaa8e6..e6fc94a 100644
--- a/Templates/Details/details_serie.html
+++ b/Templates/Details/details_serie.html
@@ -17,4 +17,27 @@
{% endif %}
{% endblock %}
-{% block favorite_url %}{% url 'toggle_series_favorite' content.id %}{% endblock %}
\ No newline at end of file
+{% block favorite_url %}{% url 'toggle_series_favorite' content.id %}{% endblock %}
+
+{% block extra_main_content %}
+
+
{% trans "También te puede gustar..." %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/Templates/base.html b/Templates/base.html
index 9f42415..ededc38 100644
--- a/Templates/base.html
+++ b/Templates/base.html
@@ -125,7 +125,7 @@
{% endif %}
-
+
@@ -174,7 +175,11 @@ {% trans "Legal" %}
diff --git a/Templates/paginator.html b/Templates/paginator.html
new file mode 100644
index 0000000..3cb8853
--- /dev/null
+++ b/Templates/paginator.html
@@ -0,0 +1,44 @@
+{% load i18n %}
+
+
+
+
\ No newline at end of file
diff --git a/users/middleware.py b/users/middleware.py
index 7109573..3ee5712 100644
--- a/users/middleware.py
+++ b/users/middleware.py
@@ -22,6 +22,7 @@ def __call__(self, request):
'update_user_role',
'gestion_cartelleres',
'editar_cartellera',
+ 'system_logs',
]
if current_url_name not in exempt_url_names and not request.path.startswith('/static/'):
diff --git a/users/static/users/css/history.css b/users/static/users/css/history.css
index 34dc29c..a556053 100644
--- a/users/static/users/css/history.css
+++ b/users/static/users/css/history.css
@@ -1,72 +1,115 @@
-/* ---- Movie cards ---- */
-.movie-card {
- background: rgba(31, 40, 51, 0.6);
+/* ---- Filtros y Selects ---- */
+.filter-select {
+ background: rgba(0, 0, 0, 0.4);
+ color: var(--text-primary);
border: 1px solid var(--border-glass);
- border-radius: 8px;
- overflow: hidden;
- transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
- height: 100%;
+ border-radius: 6px;
+ padding: 6px 12px;
+ font-size: 0.85rem;
+ outline: none;
+ transition: all 0.3s ease;
}
-.movie-card:hover {
- transform: translateY(-5px);
- border-color: rgba(102, 252, 241, 0.25);
- box-shadow: 0 8px 24px rgba(102, 252, 241, 0.08);
+.filter-select:focus {
+ border-color: var(--neon-cyan);
+ box-shadow: 0 0 8px rgba(102, 252, 241, 0.2);
}
-.movie-card-body {
- padding: 1rem 1.1rem;
+.filter-select option {
+ background: var(--bg-deep);
+ color: var(--text-primary);
}
-.movie-title {
+/* ---- Agrupación por Fechas ---- */
+.history-group-title {
color: var(--text-primary);
- font-size: 14px;
- font-weight: 600;
- margin-bottom: 4px;
font-family: 'Syne', sans-serif;
- letter-spacing: -0.01em;
+ font-size: 1.1rem;
+ font-weight: 600;
+ margin-bottom: 1rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 1px solid var(--border-glass);
}
-.movie-year {
- color: var(--text-muted);
- font-size: 11px;
- margin-bottom: 4px;
+/* ---- Tarjeta Horizontal (Lista de Historial) ---- */
+.history-item-card {
+ transition: transform 0.2s, border-color 0.2s;
+ border: 1px solid transparent;
+ background: rgba(31, 40, 51, 0.4);
}
-.movie-genre {
- color: var(--neon-cyan);
- font-size: 11px;
- margin-bottom: 8px;
- opacity: 0.8;
+.history-item-card:hover {
+ transform: translateX(5px);
+ border-color: rgba(102, 252, 241, 0.2);
+ background: rgba(31, 40, 51, 0.6);
+}
+
+/* Miniatura del Póster */
+.history-poster-container {
+ width: 60px;
+ height: 90px;
+ flex-shrink: 0;
+ border-radius: 4px;
+ overflow: hidden;
+ background: rgba(0,0,0,0.5);
}
-.movie-rating {
- color: #f5c518;
- font-size: 12px;
- margin-bottom: 8px;
+.history-poster {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
}
-.movie-synopsis {
+.history-poster-placeholder {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
color: var(--text-muted);
- font-size: 12px;
- line-height: 1.5;
+ font-size: 1.5rem;
+ font-weight: bold;
+}
+
+/* Información y Badges */
+.history-title {
+ color: var(--text-primary);
+ font-size: 1rem;
+ font-weight: 600;
margin: 0;
}
-/* ---- Empty state ---- */
+.type-badge {
+ font-size: 0.7rem;
+ padding: 2px 8px;
+ border-radius: 12px;
+}
+
+.platform-badge {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ background: rgba(255,255,255,0.05);
+ padding: 3px 8px;
+ border-radius: 4px;
+ border: 1px solid rgba(255,255,255,0.1);
+}
+
+.history-time {
+ font-size: 0.8rem;
+ opacity: 0.8;
+}
+
+/* ---- Estado Vacío (Empty state) ---- */
.history-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
- min-height: 200px;
- color: var(--text-muted);
- font-size: 14px;
- gap: 8px;
+ text-align: center;
}
.history-empty-icon {
- font-size: 32px;
- opacity: 0.3;
- margin-bottom: 8px;
+ font-size: 48px;
+ margin-bottom: 1rem;
+ opacity: 0.5;
}
\ No newline at end of file
diff --git a/users/static/users/css/user_settings.css b/users/static/users/css/user_settings.css
index 73a61db..a2d7009 100644
--- a/users/static/users/css/user_settings.css
+++ b/users/static/users/css/user_settings.css
@@ -1,9 +1,16 @@
-¡/* ---- Tabs ---- */
+/* ---- Tabs wrapper ---- */
+.nav-tabs-wrapper {
+ border: 1px solid var(--border-glass);
+ border-bottom: none;
+ border-radius: 8px 8px 0 0;
+}
+
+/* ---- Tabs ---- */
.nav-tabs {
- border-bottom: 1px solid var(--border-glass) !important;
+ border: none !important;
background: var(--bg-glass);
border-radius: 8px 8px 0 0;
- padding: 0 0.5rem;
+ padding: 0;
margin-bottom: 0 !important;
}
@@ -12,23 +19,30 @@
border: none !important;
border-bottom: 2px solid transparent !important;
border-radius: 0 !important;
+ margin-bottom: 0 !important;
padding: 14px 18px;
font-size: 13px;
font-weight: 500;
- transition: color 0.2s, border-color 0.2s;
+ transition: color 0.2s, background 0.2s;
background: transparent !important;
font-family: 'DM Sans', sans-serif;
letter-spacing: 0.02em;
}
.nav-tabs .nav-link:hover {
- color: var(--neon-cyan) !important;
+ color: var(--text-primary) !important;
}
.nav-tabs .nav-link.active {
- color: var(--neon-cyan) !important;
- border-bottom: 2px solid var(--neon-cyan) !important;
- text-shadow: 0 0 10px rgba(102, 252, 241, 0.4);
+ color: #0B0C10 !important;
+ background: #E0E6ED !important;
+ border-bottom: 2px solid transparent !important;
+ border-radius: 6px 6px 0 0 !important;
+ text-shadow: none;
+}
+
+.nav-item:first-child .nav-link.active {
+ border-radius: 8px 6px 0 0 !important;
}
/* ---- Tab content area ---- */
diff --git a/users/templates/users/parts/dashboard_age_ratings.html b/users/templates/users/parts/dashboard_age_ratings.html
index 85c41eb..c65a0d1 100644
--- a/users/templates/users/parts/dashboard_age_ratings.html
+++ b/users/templates/users/parts/dashboard_age_ratings.html
@@ -7,7 +7,7 @@
@@ -27,7 +27,7 @@
Age Rating Table
| Rating |
- Views |
+ Visualizations |
@@ -54,7 +54,7 @@ Age Rating Table
data: {
labels: {{ age_stats.labels|safe }},
datasets: [{
- label: 'Views',
+ label: 'Visualizations',
data: {{ age_stats.values|safe }},
backgroundColor: '#3498db',
borderRadius: 5
diff --git a/users/templates/users/parts/dashboard_directors.html b/users/templates/users/parts/dashboard_directors.html
index ab4abc3..9c62a15 100644
--- a/users/templates/users/parts/dashboard_directors.html
+++ b/users/templates/users/parts/dashboard_directors.html
@@ -7,7 +7,7 @@
@@ -27,7 +27,7 @@
Top Directors Table
| Director |
- Views |
+ Visualizations |
@@ -54,7 +54,7 @@ Top Directors Table
data: {
labels: {{ director_stats.labels|safe }},
datasets: [{
- label: 'Views',
+ label: 'Visualizations',
data: {{ director_stats.values|safe }},
backgroundColor: '#2c3e50',
borderRadius: 5
diff --git a/users/templates/users/parts/dashboard_genres.html b/users/templates/users/parts/dashboard_genres.html
index ab8fc21..d494d15 100644
--- a/users/templates/users/parts/dashboard_genres.html
+++ b/users/templates/users/parts/dashboard_genres.html
@@ -7,7 +7,7 @@
@@ -27,7 +27,7 @@
Top Genres Data
| Genre |
- Views |
+ Visualizations |
diff --git a/users/templates/users/parts/dashboard_overview.html b/users/templates/users/parts/dashboard_overview.html
index f66f0f2..5148afd 100644
--- a/users/templates/users/parts/dashboard_overview.html
+++ b/users/templates/users/parts/dashboard_overview.html
@@ -5,7 +5,7 @@
{% include 'users/parts/filter_form.html' %}