Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Improve UI
- Change the structure of cookie consent banner
- Split experience timeline template into different cotton components

## 0.6.0

Expand Down
272 changes: 0 additions & 272 deletions src/home/templates/cotton/experience_timeline.html

This file was deleted.

73 changes: 73 additions & 0 deletions src/home/templates/cotton/experience_timeline/card.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{% load i18n %}

<div class="mb-10! w-full {% if forloop.counter|divisibleby:2 %}timeline-start md:text-end{% else %}timeline-end{% endif %} animate-fade-in">
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 hover:scale-[1.02] border border-base-300 hover:border-primary/30">
<div class="card-body">
<div class="flex flex-col sm:flex-row sm:items-center gap-2 {% if forloop.counter|divisibleby:2 %}md:justify-end{% endif %}">
<time id="{{ item_type|default:'item' }}-{{ item.id }}-period"
class="font-mono text-sm font-bold text-primary bg-primary/10 px-3 py-1 rounded-full whitespace-nowrap w-fit">
{{ item.period }}
</time>
<div id="{{ item_type|default:'item' }}-{{ item.id }}-duration"
class="font-mono text-xs text-primary/70 whitespace-nowrap px-3 sm:px-0">
{{ item.duration }}
</div>
</div>
<div id="{{ item_type|default:'item' }}-{{ item.id }}-title"
class="text-2xl font-black mt-3 mb-2">
{{ item.title }}
</div>
{% if item.institution %}
<div id="{{ item_type|default:'item' }}-{{ item.id }}-institution"
class="font-semibold text-secondary flex items-center gap-2 {% if forloop.counter|divisibleby:2 %}md:justify-end{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
{{ item.institution }}
</div>
{% endif %}
<div id="{{ item_type|default:'item' }}-{{ item.id }}-location"
class="font-mono text-sm text-base-content/60 flex items-center gap-2 {% if forloop.counter|divisibleby:2 %}md:justify-end{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{{ item.location }}
</div>
{% if item.technologies %}
<div id="{{ item_type|default:'item' }}-{{ item.id }}-technologies"
class="mt-4 flex flex-wrap gap-2 {% if forloop.counter|divisibleby:2 %}md:justify-end{% endif %}">
{% for tech in item.technologies.all %}
<span id="{{ item_type|default:'item' }}-{{ item.id }}-technology-{{ tech.id }}"
class="badge badge-primary badge-outline hover:badge-primary transition-colors duration-200 font-semibold">
{{ tech.name }}
</span>
{% endfor %}
</div>
{% endif %}
<div class="card-actions {% if forloop.counter|divisibleby:2 %}md:justify-end{% else %}justify-start{% endif %} mt-4">
<!-- Button to show modal with item info -->
<button id="{{ item_type|default:'item' }}-{{ item.id }}-more-button"
class="btn btn-primary btn-sm gap-2 group"
onclick="{{ item_type|default:'item' }}_{{ item.id }}_modal.showModal()">
<span>{% translate "View more" %}</span>
<svg xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 group-hover:translate-x-1 transition-transform"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
</svg>
</button>
</div>
</div>
</div>
</div>
23 changes: 23 additions & 0 deletions src/home/templates/cotton/experience_timeline/experience.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% load i18n %}
{% load filters %}

<li id="{{ item_type|default:'item' }}-{{ item.id }}-item">
{% if not is_first %}
<hr />
{% endif %}
<div class="timeline-middle">
<div class="w-12 h-12 rounded-full bg-linear-to-br from-primary to-primary/60 flex items-center justify-center shadow-lg ring-4 ring-base-100 animate-pulse">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="h-6 w-6 text-primary-content">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
</svg>
</div>
</div>
<c-experience-timeline.card :item="item" />
<c-experience-timeline.modal :item="item" />
{% if not is_last %}
<hr />
{% endif %}
</li>
6 changes: 6 additions & 0 deletions src/home/templates/cotton/experience_timeline/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<ul id="{{ list_id|default:'timeline-list' }}"
class="list-none timeline timeline-snap-icon max-md:timeline-compact timeline-vertical">
{% for item in items %}
<c-experience-timeline.experience :item="item" :is-first="forloop.first" :is-last="forloop.last" />
{% endfor %}
</ul>
101 changes: 101 additions & 0 deletions src/home/templates/cotton/experience_timeline/modal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{% load i18n %}
{% load filters %}

<dialog id="{{ item_type|default:'item' }}_{{ item.id }}_modal"
class="modal modal-bottom sm:modal-middle">
<div class="modal-box w-full max-w-5xl mx-auto bg-linear-to-br from-base-100 to-base-200 shadow-2xl p-0 flex flex-col max-h-[90vh]">
<!-- Header section - fixed at top -->
<div class="bg-linear-to-r from-primary/10 to-transparent p-4 sm:p-6 border-b-2 border-primary/20 backdrop-blur-sm bg-base-100/95 shrink-0">
<h3 id="modal-{{ item_type|default:'item' }}-{{ item.id }}-title"
class="text-2xl sm:text-3xl font-bold mb-3 sm:mb-4 bg-linear-to-r from-primary to-primary/60 bg-clip-text text-transparent">
{{ item.title }}
</h3>
<div class="flex flex-wrap items-center gap-3">
<time id="modal-{{ item_type|default:'item' }}-{{ item.id }}-period"
class="font-mono text-xs sm:text-sm font-bold text-primary bg-primary/20 px-3 sm:px-4 py-1.5 rounded-full whitespace-nowrap">
{{ item.period }}
</time>
<div id="modal-{{ item_type|default:'item' }}-{{ item.id }}-duration"
class="font-mono text-xs sm:text-sm text-primary/80 font-semibold">
{{ item.duration }}
</div>
</div>
</div>

<!-- Scrollable content area -->
<div class="overflow-y-auto flex-1">
<!-- Content section with padding -->
<div class="p-4 sm:p-6">
<!-- Institution and location -->
<div class="space-y-2 mb-4">
{% if item.institution %}
<div id="modal-{{ item_type|default:'item' }}-{{ item.id }}-institution"
class="flex items-center gap-2 font-semibold text-secondary">
<svg xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
{{ item.institution }}
</div>
{% endif %}
<div id="modal-{{ item_type|default:'item' }}-{{ item.id }}-location"
class="flex items-center gap-2 text-sm text-base-content/70">
<svg xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{{ item.location }}
</div>
</div>

<!-- Technologies -->
{% if item.technologies %}
<div id="modal-{{ item_type|default:'item' }}-{{ item.id }}-technologies"
class="flex flex-wrap gap-2 mb-4">
{% for tech in item.technologies.all %}
<span id="modal-{{ item_type|default:'item' }}-{{ item.id }}-technology-{{ tech.id }}"
class="badge badge-primary badge-outline hover:badge-primary transition-colors duration-200 font-semibold whitespace-nowrap max-w-full truncate inline-block">
{{ tech.name }}
</span>
{% endfor %}
</div>
{% endif %}

<!-- Description -->
<div id="modal-{{ item_type|default:'item' }}-{{ item.id }}-description"
class="prose max-w-none py-4 prose-headings:text-primary prose-a:text-primary">
{{ item.description|md }}
</div>

<!-- Sub-projects section -->
{% if item.sub_projects.exists %}
<c-experience-timeline.sub-project-list :item="item" />
{% endif %}
</div>
<!-- End of content section -->
</div>

<!-- Footer - fixed at bottom -->
<div class="modal-action border-t border-primary/20 p-4 sm:p-6 bg-base-200/95 backdrop-blur-sm shrink-0 m-0">
<form method="dialog">
<button class="btn btn-primary gap-2 group">
<svg xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
<span>{% translate "Close" %}</span>
</button>
</form>
</div>
</div>
</dialog>
61 changes: 61 additions & 0 deletions src/home/templates/cotton/experience_timeline/sub_project.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{% load filters %}

<details class="collapse collapse-arrow bg-linear-to-r from-base-200 to-base-300 hover:from-base-300 hover:to-base-200 transition-all duration-300 rounded-lg shadow-md border border-base-300 hover:border-primary/30"
name="subprojects-{{ item.id }}-accordion"
{% if forloop.first %}open{% endif %}>
<summary class="collapse-title hover:text-primary transition-colors">
<div class="flex flex-col gap-3">
<!-- Title -->
<h4 id="{{ item_type|default:'item' }}-{{ item.id }}-subproject-{{ sub_project.id }}-title"
class="font-bold text-lg">
{{ sub_project.title }}
</h4>

<!-- Period and duration -->
<div class="flex items-center gap-2 flex-wrap">
<time id="{{ item_type|default:'item' }}-{{ item.id }}-subproject-{{ sub_project.id }}-period"
class="font-mono text-xs font-bold text-primary bg-primary/10 px-3 py-1 rounded-full whitespace-nowrap">
{{ sub_project.period }}
</time>
<div id="{{ item_type|default:'item' }}-{{ item.id }}-subproject-{{ sub_project.id }}-duration"
class="font-mono text-xs text-primary/70">
{{ sub_project.duration }}
</div>
</div>

<!-- Client -->
{% if sub_project.client %}
<div id="{{ item_type|default:'item' }}-{{ item.id }}-subproject-{{ sub_project.id }}-client"
class="flex items-center gap-2 text-sm text-secondary font-semibold">
<svg xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
{{ sub_project.client }}
</div>
{% endif %}

<!-- Technologies -->
{% if sub_project.technologies %}
<div id="{{ item_type|default:'item' }}-{{ item.id }}-subproject-{{ sub_project.id }}-technologies"
class="flex flex-wrap gap-1.5">
{% for tech in sub_project.technologies.all %}
<span id="{{ item_type|default:'item' }}-{{ item.id }}-subproject-{{ sub_project.id }}-technology-{{ tech.id }}"
class="badge badge-primary badge-outline badge-sm hover:badge-primary transition-colors whitespace-nowrap max-w-full truncate inline-block">
{{ tech.name }}
</span>
{% endfor %}
</div>
{% endif %}
</div>
</summary>
<div class="collapse-content bg-base-100/50 rounded-b-lg">
<div id="{{ item_type|default:'item' }}-{{ item.id }}-subproject-{{ sub_project.id }}-description"
class="prose prose-sm max-w-none pt-4 prose-headings:text-primary prose-a:text-primary">
{{ sub_project.description|md }}
</div>
</div>
</details>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% load i18n %}

<div id="{{ item_type|default:'item' }}-{{ item.id }}-subprojects"
class="mt-6 border-t-2 border-primary/20 pt-6">
<div class="flex items-center gap-2 mb-6">
<svg xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 text-primary"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
<h4 id="{{ item_type|default:'item' }}-{{ item.id }}-subprojects-title"
class="text-xl font-bold text-primary">
{% translate "Projects" %}
</h4>
</div>
<div class="space-y-3">
{% for sub_project in item.sub_projects.all %}
<c-experience-timeline.sub-project :item="item" :sub-project="sub_project" />
{% endfor %}
</div>
</div>
2 changes: 1 addition & 1 deletion src/home/tests/test_views/test_my_career_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class BaseTestMyCareerViewContent(BaseViewTest):
def test_response(self) -> None:
self._assert_reponse_status_code(expected_status_code=200)
self._assert_template_is_used("my-career.html")
self._assert_template_is_used("cotton/experience_timeline.html")
self._assert_template_is_used("cotton/experience_timeline/index.html")
self._assert_template_is_used("cotton/base.html")

def test_json_ld_context_and_structure(self) -> None:
Expand Down
16 changes: 8 additions & 8 deletions src/locale/es/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-01-08 12:29+0000\n"
"POT-Creation-Date: 2026-01-08 19:39+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand All @@ -22,19 +22,19 @@ msgstr ""
msgid "portfolio, CV, biography, career"
msgstr "portfolio, CV, biografía, carrera"

#: base/templates/cotton/buttons/accept_button.html:21
#: base/templates/cotton/cookie_consent_banner/buttons/accept_button.html:21
msgid "Accept"
msgstr "Aceptar"

#: base/templates/cotton/buttons/settings_button.html:18
#: base/templates/cotton/cookie_consent_banner/buttons/settings_button.html:18
msgid "Settings"
msgstr "Opciones"

#: base/templates/cotton/buttons/settings_button.html:40
#: base/templates/cotton/cookie_consent_banner/modal.html:15
msgid "Cookie Settings"
msgstr "Configuración de Cookies"

#: base/templates/cotton/buttons/settings_button.html:84
#: base/templates/cotton/cookie_consent_banner/modal.html:60
msgid "Save"
msgstr "Guardar"

Expand All @@ -43,7 +43,7 @@ msgid "Legal & Privacy"
msgstr "Condiciones y Privacidad"

#: base/templates/cotton/footer.html:57
#: home/templates/cotton/experience_timeline.html:262
#: home/templates/cotton/experience_timeline/modal.html:96
#: home/templates/index.html:90
msgid "Close"
msgstr "Cerrar"
Expand Down Expand Up @@ -121,11 +121,11 @@ msgstr[1] "%d meses"
msgid "%(title)s at %(institution)s"
msgstr "%(title)s en %(institution)s"

#: home/templates/cotton/experience_timeline.html:79
#: home/templates/cotton/experience_timeline/card.html:61
msgid "View more"
msgstr "Ver más"

#: home/templates/cotton/experience_timeline.html:179
#: home/templates/cotton/experience_timeline/sub_project_list.html:15
msgid "Projects"
msgstr "Proyectos"

Expand Down