Skip to content
Merged

Dev #196

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
927ec08
fix(search-menu-left-site) deleted the search menu from left menu, be…
NorbertAguilera Jun 3, 2026
9d60a09
fix(footer): added mailto to the footer email
Bogdan2005-19 Jun 5, 2026
216b108
Merge pull request #183 from Null-Pointers2-0/179-fixfooter-make-emai…
gery25 Jun 5, 2026
4179d85
fix(info-card): moved the info card to user_client file
gery25 Jun 6, 2026
33ba597
style(profile-tab):redifined the visual tab
gery25 Jun 6, 2026
1db00b6
Merge branch 'dev' into 176-styleprofile-add-colored-borders-and-visu…
gery25 Jun 6, 2026
541ff74
fix(dashboard): renamed the grafics to more understanding
gery25 Jun 6, 2026
fe63507
Merge branch 'dev' into 177-refactorsidebar-remove-non-functional-sea…
NorbertAguilera Jun 7, 2026
3059c07
Update quality-pipeline.yml to solve errors
NorbertAguilera Jun 7, 2026
5a13e00
Update quality-pipeline.yml
NorbertAguilera Jun 7, 2026
325b170
fix(ci): exclude tests/venv from static scan and fix hardcoded password
NorbertAguilera Jun 7, 2026
470f76d
Merge branch '177-refactorsidebar-remove-non-functional-search-input'…
NorbertAguilera Jun 7, 2026
bc04d3f
fix: added old configurations
NorbertAguilera Jun 7, 2026
b745797
Merge pull request #185 from Null-Pointers2-0/180-fixdashboard-rename…
NorbertAguilera Jun 7, 2026
22e0181
Merge pull request #186 from Null-Pointers2-0/177-refactorsidebar-rem…
gery25 Jun 7, 2026
ec23072
feat(history): update backend logic with date grouping, pagination, a…
Karu174 Jun 8, 2026
e5d54bc
feat(history): redesign UI with horizontal cards, filters, and groupe…
Karu174 Jun 8, 2026
eefdc98
fix(history): avoid duplicate entries on platform click and clean up UI
Karu174 Jun 8, 2026
b9e960b
Merge pull request #188 from Null-Pointers2-0/feat/history-redesign
gery25 Jun 8, 2026
abe6c74
refactor(image-profile): chage the defoult profile image
gery25 Jun 9, 2026
986995d
feat(): create the municipalitites of spain
gery25 Jun 9, 2026
aff5f25
feat(db-forms):add new information of user
gery25 Jun 9, 2026
ed29e00
feat: chec municipality exist
gery25 Jun 9, 2026
2956339
feat(ui-profile): modify and add the informations of register and pro…
gery25 Jun 9, 2026
3125f78
feat(recommendations): fix duplicates and implement carousel in serie…
Karu174 Jun 9, 2026
6243618
Merge pull request #189 from Null-Pointers2-0/feat/recommendations
Bogdan2005-19 Jun 9, 2026
52c06d7
feat(service): add the geters of gender age province
gery25 Jun 9, 2026
7c681a0
feat(views): add the views of demographics dashboard
gery25 Jun 9, 2026
96de2ce
feat(urls): add the demographics dashboard urls and added in middleware
gery25 Jun 9, 2026
869acd9
fix(export-csv): null evaluate error solved
gery25 Jun 9, 2026
eaf2d35
feat(botton-dashboards): add teh botton of demographics dashboards
gery25 Jun 9, 2026
170ceba
feat(demographics-dashboards): create the new demographics dashboards…
gery25 Jun 9, 2026
19d3973
Merge branch 'dev' into 181-featprofile-add-complete-user-profile-dis…
gery25 Jun 9, 2026
0624ce4
feat(admin): add system logs tab and restrict platform tracking to lo…
Karu174 Jun 10, 2026
64dcc5e
Merge pull request #193 from Null-Pointers2-0/feat/system-logs-dashboard
Bogdan2005-19 Jun 10, 2026
419730e
Merge branch 'dev' into 181-featprofile-add-complete-user-profile-dis…
NorbertAguilera Jun 11, 2026
7f2fd23
Merge pull request #190 from Null-Pointers2-0/181-featprofile-add-com…
NorbertAguilera Jun 11, 2026
33154dd
feat: modify a comand
gery25 Jun 11, 2026
ef4529d
Merge branch '181-featprofile-add-complete-user-profile-display-with-…
gery25 Jun 11, 2026
7fec44c
Merge pull request #194 from Null-Pointers2-0/181-featprofile-add-com…
gery25 Jun 11, 2026
c5fae52
feat: docker modify
gery25 Jun 11, 2026
2539fba
feat: docker modify
gery25 Jun 11, 2026
5592719
Merge pull request #195 from Null-Pointers2-0/181-featprofile-add-com…
gery25 Jun 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/quality-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -654,22 +654,22 @@ 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

# Hardcoded passwords / tokens
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
Expand All @@ -678,22 +678,22 @@ 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

# 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

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ EXPOSE 8000

RUN python manage.py collectstatic --noinput

CMD python3 manage.py migrate --fake-initial && gunicorn web.wsgi:application --bind 0.0.0.0:${PORT:-8000} --timeout 90
CMD python3 manage.py migrate --fake-initial && python3 manage.py load_municipalities && gunicorn web.wsgi:application --bind 0.0.0.0:${PORT:-8000} --timeout 90
105 changes: 65 additions & 40 deletions Templates/Details/details_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ <h1 class="main-title">{{ content.title }}</h1>
data-contingut-id="{{ content.contingut.id }}"
data-api-id="{{ api.id }}">
{{ api.name|default:api.port }}
<span class="api-stats"></span>
</button>
{% empty %}
<span style="font-size: 12px; color: var(--tm);">{% trans "No hay información de API disponible." %}</span>
Expand Down Expand Up @@ -137,15 +136,35 @@ <h4>{% trans "Deja tu opinión" %}</h4>
<div id="history-toast" class="toast-notification">✅ {% trans "Añadido a tu historial" %}</div>

<script>
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}

document.addEventListener('DOMContentLoaded', () => {
// 1. Lógica del botón de favoritos (Seguir / Dejar de seguir)
const favoriteBtn = document.getElementById('btn-save');
if (favoriteBtn) {
favoriteBtn.addEventListener('click', function() {
const targetUrl = this.getAttribute('data-url');
const csrftoken = getCookie('csrftoken');

fetch(targetUrl, {
method: 'POST',
headers: { 'X-CSRFToken': '{{ csrf_token }}', 'Content-Type': 'application/json' }
headers: {
'X-CSRFToken': csrftoken,
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
Expand All @@ -155,44 +174,50 @@ <h4>{% trans "Deja tu opinión" %}</h4>
});
}

// 2. Lógica de tracking asíncrono de clics en plataformas (Issue #24)
const platformButtons = document.querySelectorAll('.track-platform-btn');
const toast = document.getElementById('history-toast');

platformButtons.forEach(btn => {
btn.addEventListener('click', function() {
const contingutId = this.getAttribute('data-contingut-id');
const apiId = this.getAttribute('data-api-id');
const statsSpan = this.querySelector('.api-stats');

fetch("{% url 'register_platform_click' %}", {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token }}',
'Content-Type': 'application/json'
},
body: JSON.stringify({ contingut_id: contingutId, api_id: apiId })
})
.then(response => {
if (!response.ok) throw new Error('Error en el registro del clic');
return response.json();
})
.then(data => {
if (data.status === 'success') {
// Feedback visual inmediato en el botón e inyección de la métrica
this.classList.add('clicked');
statsSpan.innerText = `(${data.total_clicks} views)`;

// Disparar el banner flotante (Toast)
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
})
.catch(error => console.error('Error tracking click:', error));
// 2. Lógica de plataformas según si el usuario está logueado o no
{% if user.is_authenticated %}
// Si ESTÁ logueado: Rastreamos el clic y mostramos el Toast
const platformButtons = document.querySelectorAll('.track-platform-btn');
const toast = document.getElementById('history-toast');

platformButtons.forEach(btn => {
btn.addEventListener('click', function() {
const contingutId = this.getAttribute('data-contingut-id');
const apiId = this.getAttribute('data-api-id');
const csrftoken = getCookie('csrftoken');

fetch("{% url 'register_platform_click' %}", {
method: 'POST',
headers: {
'X-CSRFToken': csrftoken,
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ contingut_id: contingutId, api_id: apiId })
})
.then(response => {
if (!response.ok) throw new Error('Error en el registro del clic');
return response.json();
})
.then(data => {
if (data.status === 'success') {
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
})
.catch(error => console.error('Error tracking click:', error));
});
});
{% else %}
const platformButtons = document.querySelectorAll('.track-platform-btn');
platformButtons.forEach(btn => {
btn.addEventListener('click', function() {
window.location.href = "{% url 'login' %}";
});
});
});
{% endif %}
});
</script>
{% endblock %}
25 changes: 24 additions & 1 deletion Templates/Details/details_serie.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,27 @@
{% endif %}
{% endblock %}

{% block favorite_url %}{% url 'toggle_series_favorite' content.id %}{% endblock %}
{% block favorite_url %}{% url 'toggle_series_favorite' content.id %}{% endblock %}

{% block extra_main_content %}
<div class="recommendations-section" style="margin: 30px 0;">
<h3 class="section-title">{% trans "También te puede gustar..." %}</h3>
<div class="recommendations-grid" style="display: flex; gap: 15px; overflow-x: auto; padding-bottom: 10px;">
{% for rec in recommendations %}
<a href="{% url 'series_detail' rec.id %}" style="text-decoration: none; color: white; min-width: 140px;">
{% if rec.poster_path %}
<img src="{{ rec.poster_path }}" alt="{{ rec.title }}" style="width: 140px; height: 210px; object-fit: cover; border-radius: 8px;">
{% else %}
<div style="width: 140px; height: 210px; background: rgba(255,255,255,0.05); border-radius: 8px; display: flex; align-items: center; justify-content: center; text-align: center; padding: 10px;">
{{ rec.title }}
</div>
{% endif %}
<p style="font-size: 0.9rem; margin-top: 8px; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{ rec.title }}</p>
</a>
{% empty %}
<!-- Mensaje de Fallback -->
<p style="color: rgba(255,255,255,0.5);">{% trans "No hay recomendaciones disponibles." %}</p>
{% endfor %}
</div>
</div>
{% endblock %}
9 changes: 7 additions & 2 deletions Templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
</a>
</div>
{% endif %}

<!--
<div class="menu-search">
<div class="search-input-container">
<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
Expand All @@ -136,6 +136,7 @@
<input type="text" class="search-input" placeholder="{% trans 'Search...' %}">
</div>
</div>
-->
</nav>

<!-- Overlay -->
Expand Down Expand Up @@ -174,7 +175,11 @@ <h5>{% trans "Legal" %}</h5>
</div>
<div class="col-md-3 mb-4">
<h5>{% trans "Contact" %}</h5>
<p>info@StreamSync.com</p>
<p>
<a href="mailto:info@StreamSync.com">
info@StreamSync.com
</a>
</p>
<p>+34 600 000 000</p>
</div>
</div>
Expand Down
70 changes: 70 additions & 0 deletions Templates/identify/register.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,51 @@ <h1>{% trans "Create Account" %}</h1>
{% endfor %}
</div>

<div class="form-group">
<select name="gender" id="id_gender" required>
<option value="">{% trans "Select gender" %}</option>
{% for value, label in form.gender.field.choices %}
<option value="{{ value }}">{{ label }}</option>
{% endfor %}
</select>
{% for error in form.gender.errors %}
<small style="color: #e50914; font-size: 12px;">{{ error }}</small>
{% endfor %}
</div>

<div class="form-group">
<select name="age_range" id="id_age_range" required>
<option value="">{% trans "Select age range" %}</option>
{% for value, label in form.age_range.field.choices %}
<option value="{{ value }}">{{ label }}</option>
{% endfor %}
</select>
{% for error in form.age_range.errors %}
<small style="color: #e50914; font-size: 12px;">{{ error }}</small>
{% endfor %}
</div>

<div class="form-group">
<select name="province" id="id_province" required>
<option value="">{% trans "Select province" %}</option>
{% for province in provinces %}
<option value="{{ province.id }}">{{ province.name }}</option>
{% endfor %}
</select>
{% for error in form.province.errors %}
<small style="color: #e50914; font-size: 12px;">{{ error }}</small>
{% endfor %}
</div>

<div class="form-group">
<select name="municipality" id="id_municipality" required disabled>
<option value="">{% trans "First select a province" %}</option>
</select>
{% for error in form.municipality.errors %}
<small style="color: #e50914; font-size: 12px;">{{ error }}</small>
{% endfor %}
</div>

<div class="checkbox-group">
<input type="checkbox" name="terms_accepted" id="id_terms_accepted" required>
<label for="id_terms_accepted">
Expand Down Expand Up @@ -203,6 +248,31 @@ <h1>{% trans "Create Account" %}</h1>
});
});

document.getElementById('id_province').addEventListener('change', function() {
const provinceId = this.value;
const municipalitySelect = document.getElementById('id_municipality');
municipalitySelect.innerHTML = '<option value="">{% filter escapejs %}{% trans "Loading..." %}{% endfilter %}</option>';
municipalitySelect.disabled = true;

if (!provinceId) {
municipalitySelect.innerHTML = '<option value="">{% filter escapejs %}{% trans "First select a province" %}{% endfilter %}</option>';
return;
}

fetch('/api/municipalities/' + provinceId + '/')
.then(response => response.json())
.then(municipalities => {
municipalitySelect.innerHTML = '<option value="">{% filter escapejs %}{% trans "Select municipality" %}{% endfilter %}</option>';
municipalities.forEach(m => {
municipalitySelect.innerHTML += '<option value="' + m.id + '">' + m.name + '</option>';
});
municipalitySelect.disabled = false;
})
.catch(() => {
municipalitySelect.innerHTML = '<option value="">{% filter escapejs %}{% trans "Error loading municipalities" %}{% endfilter %}</option>';
});
});

/* document.getElementById('registerForm').addEventListener('submit', function (e) {
e.preventDefault();

Expand Down
44 changes: 44 additions & 0 deletions Templates/paginator.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{% load i18n %}

<nav aria-label="Page navigation">
<ul class="pagination justify-content-center mb-0 custom-pagination">
{% if items.has_previous %}
<li class="page-item">
<a class="page-link text-white-50 bg-transparent border-secondary" href="?page=1{% if request.GET.platform %}&platform={{ request.GET.platform }}{% endif %}{% if request.GET.date %}&date={{ request.GET.date }}{% endif %}" aria-label="{% trans 'Primera' %}">
<span aria-hidden="true">&laquo;&laquo;</span>
</a>
</li>
<li class="page-item">
<a class="page-link text-white-50 bg-transparent border-secondary" href="?page={{ items.previous_page_number }}{% if request.GET.platform %}&platform={{ request.GET.platform }}{% endif %}{% if request.GET.date %}&date={{ request.GET.date }}{% endif %}" aria-label="{% trans 'Anterior' %}">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% endif %}

<li class="page-item active" aria-current="page">
<span class="page-link text-neon bg-dark border-secondary" style="border-color: var(--neon-cyan) !important;">
{% trans "Pàgina" %} {{ items.number }} {% trans "de" %} {{ items.paginator.num_pages }}
</span>
</li>

{% if items.has_next %}
<li class="page-item">
<a class="page-link text-white-50 bg-transparent border-secondary" href="?page={{ items.next_page_number }}{% if request.GET.platform %}&platform={{ request.GET.platform }}{% endif %}{% if request.GET.date %}&date={{ request.GET.date }}{% endif %}" aria-label="{% trans 'Següent' %}">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
<li class="page-item">
<a class="page-link text-white-50 bg-transparent border-secondary" href="?page={{ items.paginator.num_pages }}{% if request.GET.platform %}&platform={{ request.GET.platform }}{% endif %}{% if request.GET.date %}&date={{ request.GET.date }}{% endif %}" aria-label="{% trans 'Última' %}">
<span aria-hidden="true">&raquo;&raquo;</span>
</a>
</li>
{% endif %}
</ul>
</nav>

<style>
.custom-pagination .page-link:hover {
background-color: rgba(102, 252, 241, 0.1) !important;
color: var(--neon-cyan) !important;
}
</style>
Loading
Loading