Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
147 commits
Select commit Hold shift + click to select a range
6e9d24e
refactor: improved header
Bogdan2005-19 Apr 30, 2026
7120b40
refactor(css): small improvement at sidebar user profile part
Bogdan2005-19 Apr 30, 2026
6067af4
refactor(home): improved html structure for better readability
Bogdan2005-19 Apr 30, 2026
24d79e4
feat(content): Added platforms aviability in details page
Bogdan2005-19 Apr 30, 2026
816cf7a
Deleted comments
Bogdan2005-19 Apr 30, 2026
64421a7
Deleted comments
Bogdan2005-19 Apr 30, 2026
bd51e5e
fix(home): Search catlog does not show duplicates
Bogdan2005-19 Apr 30, 2026
3ed5014
Merge pull request #108 from Null-Pointers2-0/HTML-Refactor-branch
gery25 Apr 30, 2026
fc19df6
Merge pull request #110 from Null-Pointers2-0/23-featcontent-ui-displ…
gery25 Apr 30, 2026
291eb45
Merge branch 'dev' into 109-bugcatalog-duplicated-contents-when-searc…
gery25 Apr 30, 2026
350d27c
Merge pull request #111 from Null-Pointers2-0/109-bugcatalog-duplicat…
gery25 Apr 30, 2026
783dde5
feat: added plataform filtering, and seeing on catalog witch platafor…
May 1, 2026
e68c9e9
fix: fixed the pagination
May 1, 2026
28ad232
fix: fixed to string on Api
May 1, 2026
0924ed1
Merge pull request #112 from Null-Pointers2-0/21-featcontent-implemen…
gery25 May 2, 2026
dc2b8d0
fixed: port api models problems
May 2, 2026
a9e482b
feat (DDBB): added neon with postgres sql to database settings
May 5, 2026
5af3c69
feat (BBDD): added implementation of bbdd as the uml
May 5, 2026
e995cb3
fix: delete unnecessary files
May 5, 2026
15f6ec6
fix: merge conflicts solved
May 5, 2026
6166bcf
Merge branch 'main' into dev
Bogdan2005-19 May 5, 2026
14d53a5
fix: changed branch push activation on quality-pipeline
Bogdan2005-19 May 5, 2026
7061e0d
added comments for fog metric (Need add more)
Bogdan2005-19 May 5, 2026
e41aaff
fix: changed fog index search path
Bogdan2005-19 May 5, 2026
6601c97
fix(profile-api): resolve AttributeError in profile API and include f…
Karu174 May 5, 2026
e93b24d
Merge pull request #115 from Null-Pointers2-0/fix/profile-api-bug
Bogdan2005-19 May 6, 2026
7a6848f
Update push branch configuration in workflow
Bogdan2005-19 May 6, 2026
9964fd2
refactor(action): small changes on json naming
Bogdan2005-19 May 6, 2026
7c101ff
modified fog metric json to match html names
Bogdan2005-19 May 6, 2026
f3c0a97
refactor: added NO DATA to pass on html report
Bogdan2005-19 May 6, 2026
aca7533
refactored metrig html report
Bogdan2005-19 May 6, 2026
dce80ce
Improved html for metrics
Bogdan2005-19 May 6, 2026
b7c0a83
feat (Data Base): added database tables, and relations as the uml
May 6, 2026
8e8fcdc
feat (Data Base): added database tables, and relations as the uml
May 6, 2026
24c86ca
fix (DataBase): fixed conflicts with models and views
May 6, 2026
56163f2
fix: Localy fixed db work
Bogdan2005-19 May 6, 2026
eca1d46
refactor
Bogdan2005-19 May 6, 2026
f013c0a
refactor: added sqlite if no DATABASE_URL is given
Bogdan2005-19 May 6, 2026
569e5fd
refactor: added some confirm prints
Bogdan2005-19 May 6, 2026
d295a0e
feat (test_impl): added first test implementation for a form creation…
May 7, 2026
b9aab73
feat(tests): added tests for update customer
May 7, 2026
2730f5a
feat (test_models): added tests on models, and solved tests from forms
May 8, 2026
6c22e8b
feat (test_views): added tests on views
May 8, 2026
6df84f9
feat (test_views): added tests on views
May 8, 2026
2349920
Merge branch '119-feattests-tests-to-django-project' into dev
May 8, 2026
1602086
Fix (tests): fixed and added all tests
May 8, 2026
76ba174
feat(data-base): add the visualitzations table
gery25 May 8, 2026
de9d7c6
Merge pull request #118 from Null-Pointers2-0/DeployFailDB
gery25 May 8, 2026
d24ed21
Merge branch 'dev' into 19-featexport-generate-and-download-csv-reports
Bogdan2005-19 May 8, 2026
5644f8d
merged with dev to allow PR deploy preview
Bogdan2005-19 May 8, 2026
ccc1614
added --fake-initial to decrease migrate conflicts
Bogdan2005-19 May 8, 2026
d9096d9
refactor: changed trusted urls from render
Bogdan2005-19 May 8, 2026
463d352
Merge pull request #137 from Null-Pointers2-0/19-featexport-generate-…
Bogdan2005-19 May 8, 2026
7d8d38a
feat(export-csv): add the logic to create the csv data
gery25 May 9, 2026
efed7f5
feat(export): add the logic to download the csv
gery25 May 9, 2026
19145a3
Merge pull request #138 from Null-Pointers2-0/dev
gery25 May 9, 2026
54aee48
Merge branch '19-featexport-generate-and-download-csv-reports' of git…
gery25 May 9, 2026
3a63ebb
Merge pull request #139 from Null-Pointers2-0/19-featexport-generate-…
gery25 May 9, 2026
00f47fd
Merge pull request #140 from Null-Pointers2-0/17-featanalytics-implem…
gery25 May 9, 2026
24491fd
refactor(views): login requiret refactored and imports
gery25 May 11, 2026
97f72f7
Merge pull request #141 from Null-Pointers2-0/20-featadmin-ui-impleme…
gery25 May 11, 2026
9af0adb
feat(login-redirection): implement dynamic redirection based on user …
gery25 May 11, 2026
c4c4061
refactor(user_profile): chaged files name profile to user_profile, re…
gery25 May 11, 2026
c4174ec
fix: Changed settings to use sqlite on debug
Bogdan2005-19 May 12, 2026
ffca2e8
feat(backend-dashboard): add the logic to generet the dashboard grafics
gery25 May 12, 2026
f67e3a3
feat(security-restrictions): aplay security restrictions in the profi…
gery25 May 12, 2026
ce73f12
feat(dashboard-ui): create the templates of the dasboards and grafics…
gery25 May 12, 2026
6deb22e
feat(dashboard-logic): aply the filter of the platforms subscriptions
gery25 May 12, 2026
10c3a6e
Merge pull request #143 from Null-Pointers2-0/UserCreationWRole
gery25 May 12, 2026
949ca00
Merge pull request #144 from Null-Pointers2-0/18-featadmin-ui-create-…
gery25 May 12, 2026
0915c06
Remove 'StreamSync Premium' label from details sidebar
Bogdan2005-19 May 12, 2026
e2c1653
feat (image): added image to the content
May 12, 2026
0ed92ea
Merge branch 'dev' into 124-featcatalog-content-poster-selection-and-…
May 12, 2026
51ef477
fix: ficed dependencies
May 12, 2026
b67a57b
Merge branch '124-featcatalog-content-poster-selection-and-merging' i…
May 12, 2026
1b13a60
feat(forms-filter): create the forms filter and update the views to d…
gery25 May 12, 2026
2f00931
feat(ui-forms): add the filter to the dashboards graphics
gery25 May 12, 2026
ac1b63d
feature(admin): Implemented admin users creation and deletion (Needs …
Bogdan2005-19 May 13, 2026
c4e2647
fix(filter-date): apply date filter when only one date is provided
gery25 May 13, 2026
85cbcdb
Merge branch 'dev' into 20-featadmin-ui-implement-data-filtering-by-d…
gery25 May 13, 2026
1392e78
Merge pull request #145 from Null-Pointers2-0/20-featadmin-ui-impleme…
gery25 May 13, 2026
b9b0a54
Merge pull request #146 from Null-Pointers2-0/17-featanalytics-implem…
Bogdan2005-19 May 13, 2026
2644508
fix(render): Making dynamic port asignment and use unicorn
Bogdan2005-19 May 13, 2026
16d4d6a
fix(render): Changing dockerfile
Bogdan2005-19 May 13, 2026
3d9ad2e
chore: added requirements on .txt
Bogdan2005-19 May 13, 2026
19ecab6
fix(render): Changing dockerfile
Bogdan2005-19 May 13, 2026
25219a0
refactor: changed app.py logic
Bogdan2005-19 May 13, 2026
c47dc96
refactor: changed scheduler logic
Bogdan2005-19 May 13, 2026
f1e43b9
experimenting
Bogdan2005-19 May 13, 2026
e28b2ed
feat(social): implement community reviews, average ratings, and movie…
Karu174 May 13, 2026
77cb93b
feat: experimenting for better page charging
Bogdan2005-19 May 13, 2026
36cbc3e
fix: render now charging properly
Bogdan2005-19 May 13, 2026
4503409
Merge pull request #148 from Null-Pointers2-0/RenderFailure
Karu174 May 13, 2026
041bee7
Merge branch 'dev' into feat/social-reviews-system-and-content-recomm…
Bogdan2005-19 May 13, 2026
b629170
Merge pull request #149 from Null-Pointers2-0/feat/social-reviews-sys…
Bogdan2005-19 May 13, 2026
d6e0cf1
Merge pull request #149 from Null-Pointers2-0/feat/social-reviews-sys…
Bogdan2005-19 May 13, 2026
b0e3580
Merge branch 'fix/star-rating-visuals' of https://github.com/Null-Poi…
Karu174 May 13, 2026
4585ff2
fix: solved merge conflicts
Bogdan2005-19 May 14, 2026
d65330d
fix: solved merge conflicts
Bogdan2005-19 May 14, 2026
025a6d0
feat(staffadmin): Create default staff admin user, implemented staff …
Bogdan2005-19 May 14, 2026
950f00f
Merge branch 'dev' into UserCreationWRole
Bogdan2005-19 May 14, 2026
8a39a45
fix: changed mail message to be send using thread
Bogdan2005-19 May 14, 2026
7f3c421
added timeout to mail sending
Bogdan2005-19 May 14, 2026
bad9ae6
Merge pull request #150 from Null-Pointers2-0/fix/star-rating-visuals
Bogdan2005-19 May 14, 2026
6adee32
added comprobations for mail sending
Bogdan2005-19 May 14, 2026
c97b7de
feat(DB_backup): added the db backup with instructions comented
May 14, 2026
e92c6dd
added view to verify enviroment mail variable
Bogdan2005-19 May 14, 2026
0531bc1
refactor mail format
Bogdan2005-19 May 14, 2026
dff5764
refactor mail format
Bogdan2005-19 May 14, 2026
d6da377
fix (test-Failures): fixed tests failures
May 14, 2026
e38a7da
fix (test-Failures): fixed tests failures
May 14, 2026
a1b0d92
Merge branch 'dev' into 124-featcatalog-content-poster-selection-and-…
NorbertAguilera May 14, 2026
6c4a471
feat (DB_compare): added the compare betwen DB and API to remove old …
May 14, 2026
8b6ad6c
Merge branch '124-featcatalog-content-poster-selection-and-merging' o…
May 14, 2026
dd5e60c
feat: Sending cretentials via mail when admin creates users
Bogdan2005-19 May 14, 2026
09b08ba
feat: Sending cretentials via mail when admin creates users
Bogdan2005-19 May 14, 2026
0084095
fix: user filter now works
Bogdan2005-19 May 14, 2026
bf56ddf
feat(url): Created redirection for Admin(Platforms) and Staff Admin
Bogdan2005-19 May 15, 2026
83ad678
fix(api): Implemented parallel execution on first api call when runse…
Bogdan2005-19 May 15, 2026
f7abb6b
feat(DB_clone): cloning db on a local SQLite
May 16, 2026
882151a
refactor(db): Improved Continguts to use ManyToMany on apis
Bogdan2005-19 May 16, 2026
21f69c7
fix(test): changed tests to work with new db
Bogdan2005-19 May 16, 2026
57cc7db
refactor(utils): Improved movies and series creation
Bogdan2005-19 May 16, 2026
cd74071
refactor(view): Changed code for work with new db
Bogdan2005-19 May 16, 2026
048b7de
refactor: Changed services and views to work with new db
Bogdan2005-19 May 16, 2026
e7bbf06
refactor(utils): Added exception handlers when importing posters
Bogdan2005-19 May 16, 2026
5c71f6a
refactor(docker): setted gunicorn workers to default(1)
Bogdan2005-19 May 17, 2026
3af6bdf
refactor(users): Added platform asignment for manual admin creation, …
Bogdan2005-19 May 17, 2026
d0f5d74
feat(reports): automatic anonymization of personal data in CSV export…
gery25 May 18, 2026
0ae45a8
Merge pull request #162 from Null-Pointers2-0/121-featreports-automat…
gery25 May 18, 2026
99500fa
feat(legal): GDPR compliant anonymization for external platform exports
gery25 May 18, 2026
9f2c6be
Merge pull request #163 from Null-Pointers2-0/122-featlegal-gdpr-comp…
gery25 May 18, 2026
86644ec
Merge pull request #151 from Null-Pointers2-0/UserCreationWRole
gery25 May 18, 2026
ac38f38
Merge branch 'dev' into 124-featcatalog-content-poster-selection-and-…
gery25 May 18, 2026
5cf4b6a
Merge pull request #153 from Null-Pointers2-0/124-featcatalog-content…
gery25 May 18, 2026
7a17d70
Merge pull request #155 from Null-Pointers2-0/RedirectRole
gery25 May 18, 2026
35e03b0
Merge branch 'dev' into CriticalRefactors
Bogdan2005-19 May 18, 2026
1ec66b7
fix: uploaded gitignore, added .vscode
May 18, 2026
95b3df0
fix: uploaded gitignore, added .vscode
May 18, 2026
b244cd8
fix (DB_clon): solved problems with db clonning
May 18, 2026
f125169
feat(redirect): implement internal tracking for platform clicks witho…
Karu174 May 18, 2026
746d94a
Merge pull request #164 from Null-Pointers2-0/120-featreports-autonom…
gery25 May 19, 2026
750f9d6
Merge pull request #165 from Null-Pointers2-0/feat/history-platform-t…
gery25 May 19, 2026
df09529
Merge pull request #161 from Null-Pointers2-0/CriticalRefactors
Karu174 May 19, 2026
b2ef806
fix(migration): corrected migration
Bogdan2005-19 May 19, 2026
1282d42
feat(poster-path): added the poster-path edition from admin staff
May 19, 2026
f31f331
fix (content-gestion-merge) solving merge conflicts on merge with dev
May 19, 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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
API_KEY_8080=secret8080
API_KEY_8081=secret8081
API_KEY_8082=secret8082
DATABASE_URL=postgres://mi_usuario:mi_password_seguro@db:5432/joinproject_db
8 changes: 4 additions & 4 deletions .github/scripts/depth_conditional_nesting_GP.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ def visit_If(self, node: ast.If) -> None:

def get_status_code(max_depth: int) -> str:
if max_depth > CRITICAL_THRESHOLD:
return "CRITICAL"
return "CRITICAL"
if max_depth >= WARNING_THRESHOLD:
return "🚨 WARNING"
return "OK"
return "WARNING"
return "OK"


def get_metrics(filepath: Path) -> Optional[Dict[str, Any]]:
Expand Down Expand Up @@ -144,7 +144,7 @@ def main() -> None:
all_results.append({
"file": str(filepath),
"avg_depth": metrics["avg"],
"max_depth": metrics["max"],
"max_nesting_depth": metrics["max"],
"status_code": metrics["status_code"]
})
if metrics["max"] > CRITICAL_THRESHOLD:
Expand Down
12 changes: 5 additions & 7 deletions .github/scripts/fog_comments_indexGP.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import re
import os
import sys
import json
import textstat
import re, os, sys, json, textstat
from pathlib import Path

SUPPORTED_EXTENSIONS = {'.py', '.js', '.ts', '.cpp', '.java', '.html'}

EXCLUDE_DIRS = {'.git', 'node_modules', 'venv', 'env', '__pycache__', 'dist'}

def extract_comments(file_path):
extension = os.path.splitext(file_path)[1]

Expand Down Expand Up @@ -75,7 +73,7 @@ def scan(target_path):

files = (
[path] if path.is_file()
else [f for f in path.rglob('*') if f.suffix in SUPPORTED_EXTENSIONS]
else [f for f in path.rglob('*') if f.suffix in SUPPORTED_EXTENSIONS and not any(part in EXCLUDE_DIRS for part in f.parts)]
)

results = []
Expand All @@ -92,7 +90,7 @@ def scan(target_path):
results.append({
"file": str(file),
"avg_fog": round(avg, 1) if avg else None,
"max_fog": round(max_score, 1) if max_score else None,
"fog_score": round(max_score, 1) if max_score else None,
"status_code": status
})

Expand Down
6 changes: 3 additions & 3 deletions .github/scripts/render-integrity-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,15 @@ function buildQualitySection(key, data) {

const results = data.results || []
const totalFiles = (data.summary && data.summary.total_files != null) ? data.summary.total_files : results.length
const violations = results.filter((r) => r.status_code !== 'OK' && r.status_code !== 'OK!')
const violations = results.filter((r) => r.status_code !== 'OK' && r.status_code !== 'OK!' && r.status_code !== 'NO DATA')
const passed = totalFiles - violations.length

const findingsHtml = violations.length === 0
? `<p style="color:#64748b;font-style:italic;margin:0;font-size:14px">All ${totalFiles} file(s) passed — no violations found.</p>`
: violations.map((r) => {
const val = r[qm.metric]
const display = typeof val === 'number' ? val.toFixed(qm.decimals) : (val ?? '?')
const lvl = (r.status_code === 'DANGER' || r.status_code === '⚠️ IMPROVE NAMING') ? 'error' : 'warning'
const lvl = (r.status_code === 'DANGER' || r.status_code === '⚠️ IMPROVE NAMING' || r.status_code === 'COMPLEX') ? 'error' : 'warning'
return `
<div style="display:flex;gap:10px;align-items:flex-start;padding:10px 0;border-bottom:1px solid #f1f5f9">
<div style="flex-shrink:0;padding-top:1px">${chip(lvl)}</div>
Expand Down Expand Up @@ -225,7 +225,7 @@ const qualitySummaryRows = quality
const qm = QUALITY_META[key]
const data = quality[key]
const status = qualityJobStatus(data)
const violations = data ? (data.results || []).filter((r) => r.status_code !== 'OK' && r.status_code !== 'OK!').length : 0
const violations = data ? (data.results || []).filter((r) => r.status_code !== 'OK' && r.status_code !== 'OK!' && r.status_code !== 'NO DATA').length : 0
return `
<tr>
<td style="padding:12px 16px"><strong>${qm.icon} ${esc(qm.name)}</strong></td>
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/quality-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ jobs:
- name: Run fog index analysis
run: |
mkdir -p quality-job4
python .github/scripts/fog_comments_indexGP.py src/ \
python .github/scripts/fog_comments_indexGP.py . \
> quality-job4/fog-results.json
cat quality-job4/fog-results.json

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ db.sqlite3
media/
env/
staticfiles/

.vscode/
9 changes: 3 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
FROM python:3.13-slim
FROM python:3.12-slim

WORKDIR /app

COPY ./requirements.txt /app/

RUN pip install -r requirements.txt
RUN pip install --upgrade pip && pip install -r requirements.txt

COPY . .

EXPOSE 8000

RUN python manage.py collectstatic --noinput

CMD python3 manage.py migrate && python3 manage.py runserver 0.0.0.0:8000



CMD python3 manage.py migrate --fake-initial && gunicorn web.wsgi:application --bind 0.0.0.0:${PORT:-8000} --timeout 90
141 changes: 120 additions & 21 deletions Templates/Details/details_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,28 @@
{% block content %}
<div class="details-wrapper">
<aside class="details-sidebar">
{% if content.poster_path %}
<div class="poster-container">
<img src="{{ content.poster_path }}" alt="{{ content.title }}" class="details-poster">
</div>
{% endif %}
<div class="side-meta">
<span class="label-neon">StreamSync Premium</span>
<h1 class="main-title">{{ content.title }}</h1>

<div class="badge-group">
{% if content.genre %}
<span class="badge">{{ content.genre.name }}</span>
{% endif %}
{% if content.age_rating %}
<span class="badge">{{ content.age_rating.description }}</span>
{% endif %}
{% if content.rating %}
<span class="badge" style="color: var(--rating); border-color: rgba(245, 197, 24, 0.2);">★ {{ content.rating }}</span>
{% endif %}
</div>
</div>

<div class="side-info-list" style="display: flex; flex-direction: column; gap: 12px;">
{# Los archivos hijos (movie_detail y series_detail) rellenarán esto #}
{% block content_metadata %}{% endblock %}

{% if content.director %}
Expand All @@ -31,8 +38,22 @@ <h1 class="main-title">{{ content.title }}</h1>
<div class="data-card-value">{{ content.director.name }}</div>
</div>
{% endif %}

{% block platforms %}{% endblock %}

<div class="data-card">
<div class="data-card-label">Disponible en:</div>
<div class="data-card-value api-buttons-container">
{% for api in available_apis %}
<button class="api-btn track-platform-btn"
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);">No hay información de API disponible.</span>
{% endfor %}
</div>
</div>
</div>
</aside>

Expand All @@ -48,51 +69,129 @@ <h2 class="section-title">Sinopsis</h2>
<h2 class="section-title">Acciones</h2>
<div style="display: flex; gap: 16px;">
{% if user.is_authenticated %}
<button id="btn-save" class="btn-action"
<button id="btn-save" class="btn-action"
data-url="{% block favorite_url %}{% endblock %}">
{% if is_favorite %} Dejar de seguir {% else %} Seguir {% endif %}
</button>
{% endif %}
</div>
</section>

<section class="community-reviews-section">
<h2 class="section-title">Valoracions de la comunitat</h2>

<div class="rating-summary">
{% if total_reviews > 0 %}
<span class="stars-avg">★ {{ avg_rating|floatformat:1 }}</span>
<span class="count">({{ total_reviews }})</span>
{% else %}
<span class="count" style="font-style: italic;">Sense valoracions encara</span>
{% endif %}
</div>

{% if user.is_authenticated %}
<div class="review-form-container">
<h4>Deixa la teva opinió</h4>
<form action="{% url 'submit_review' content.contingut.id %}" method="POST">
{% csrf_token %}

<div class="star-rating-input">
{% for i in "54321" %}
<input type="radio" id="star{{i}}" name="rating" value="{{i}}" required />
<label for="star{{i}}" class="star-label">★</label>
{% endfor %}
</div>

<textarea name="comment" class="review-textarea" placeholder="Què t'ha semblat?"></textarea>

<button type="submit" class="submit-btn mt-3">Publicar valoració</button>
</form>
</div>
{% endif %}

<div class="reviews-list">
{% for review in reviews %}
<div class="review-card">
<div class="review-header">
<span class="review-user">{{ review.user.username }}</span>
<span class="review-stars">
{% for i in "12345" %}
{% if forloop.counter <= review.puntuacio %}★{% else %}<span style="opacity:0.3">★</span>{% endif %}
{% endfor %}
</span>
</div>
<div class="review-date">{{ review.creada_a|date:"d M Y" }}</div>
{% if review.comentari %}
<p class="review-text">{{ review.comentari }}</p>
{% endif %}
</div>
{% empty %}
<p style="color: rgba(255,255,255,0.5);">Sigues el primer en valorar aquest contingut!</p>
{% endfor %}
</div>
</section>
</div>
</div>

<div id="history-toast" class="toast-notification">✅ Añadido a tu historial</div>

<script>
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() {
// Lee la URL específica generada por el bloque de Django
const targetUrl = this.getAttribute('data-url');
const btn = this;

fetch(targetUrl, {
method: 'POST',
headers: { 'X-CSRFToken': '{{ csrf_token }}', 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(data => {
this.innerText = (data.status === 'added') ? 'Dejar de seguir' : 'Seguir';
this.style.color = (data.status === 'added') ? 'var(--cyan)' : 'var(--tm)';
});
});
}

// 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: {
// Al estar incrustado en el template, Django inyecta el token aquí directamente
'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 la petición');
if (!response.ok) throw new Error('Error en el registro del clic');
return response.json();
})
.then(data => {
// Actualización directa de la interfaz gráfica
if (data.status === 'added') {
btn.innerText = 'Quitar de la lista';
btn.style.color = 'var(--cyan)';
} else {
btn.innerText = 'Seguir';
btn.style.color = 'var(--tm)';
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:', error));
.catch(error => console.error('Error tracking click:', error));
});
}
});
});
</script>
{% endblock %}
24 changes: 23 additions & 1 deletion Templates/Details/details_movie.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,26 @@
</div>
{% endblock %}

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

{% block extra_main_content %}
<div class="recommendations-section" style="margin: 30px 0;">
<h3 class="section-title">També et pot agradar...</h3>
<div class="recommendations-grid" style="display: flex; gap: 15px; overflow-x: auto; padding-bottom: 10px;">
{% for rec in recommendations %}
<a href="{% url 'movie_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 %}
<p style="color: rgba(255,255,255,0.5);">No hi ha recomanacions disponibles.</p>
{% endfor %}
</div>
</div>
{% endblock %}
9 changes: 6 additions & 3 deletions Templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@
{% if user.is_authenticated %}
<span class="welcome-text">{% trans "Welcome" %} {{ user.username }}</span>
{% endif %}
</div>
<div class="header-right">
<form action="{% url 'set_language' %}" method="post" class="nav-item dropdown">
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}">
Expand Down Expand Up @@ -111,7 +109,12 @@

{% if user.is_authenticated %}
<div class="menu-user">
<a href="{% url 'profile' %}">
{% if user.type == 'Consumer' %}
<a href="{% url 'user_profile' %}" class="hidden-link">
{% else %}
<a href="{% url 'admin_dashboard' %}" class="hidden-link">
{% endif %}

<div class="user-info">
<div class="user-avatar">{{ user.username|first|upper }}</div>
<div class="user-details">
Expand Down
Loading
Loading