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
125 changes: 110 additions & 15 deletions app/interfaces/web/routes/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,76 @@ def _render_profile_shell(request: Request, flash: str):
)


def _render_profile_skills_shell(request: Request, flash: str = "", include_oob: bool = False):
context = _build_profile_context(flash=flash)
template_name = "profile/_skills_fragment.html" if include_oob else "profile/_skills_shell.html"
return templates.TemplateResponse(
request=request,
name=template_name,
context={
"request": request,
**context,
"skills_flash_message": context["flash_message"] if flash else None,
},
)


def _render_profile_basics_shell(request: Request, flash: str = "", include_oob: bool = False):
context = _build_profile_context(flash=flash)
template_name = "profile/_basics_fragment.html" if include_oob else "profile/_basics.html"
return templates.TemplateResponse(
request=request,
name=template_name,
context={
"request": request,
**context,
"basics_flash_message": context["flash_message"] if flash else None,
},
)


def _render_profile_formation_shell(request: Request, flash: str = "", include_oob: bool = False):
context = _build_profile_context(flash=flash)
template_name = "profile/_formation_fragment.html" if include_oob else "profile/_formation_shell.html"
return templates.TemplateResponse(
request=request,
name=template_name,
context={
"request": request,
**context,
"formation_flash_message": context["flash_message"] if flash else None,
},
)


def _render_profile_projects_shell(request: Request, flash: str = "", include_oob: bool = False):
context = _build_profile_context(flash=flash)
template_name = "profile/_projects_fragment.html" if include_oob else "profile/_projects_shell.html"
return templates.TemplateResponse(
request=request,
name=template_name,
context={
"request": request,
**context,
"projects_flash_message": context["flash_message"] if flash else None,
},
)


def _render_profile_experiences_shell(request: Request, flash: str = "", include_oob: bool = False):
context = _build_profile_context(flash=flash)
template_name = "profile/_experiences_fragment.html" if include_oob else "profile/_experiences_shell.html"
return templates.TemplateResponse(
request=request,
name=template_name,
context={
"request": request,
**context,
"experiences_flash_message": context["flash_message"] if flash else None,
},
)


@router.get("/app/profile")
def profile_index(request: Request, flash: str | None = None):
context = _build_profile_context(flash=flash)
Expand All @@ -230,6 +300,31 @@ def profile_shell_partial(request: Request, flash: str | None = None):
return _render_profile_shell(request, flash or "")


@router.get("/app/profile/skills", response_class=HTMLResponse)
def profile_skills_partial(request: Request, flash: str | None = None):
return _render_profile_skills_shell(request, flash or "")


@router.get("/app/profile/basics", response_class=HTMLResponse)
def profile_basics_partial(request: Request, flash: str | None = None):
return _render_profile_basics_shell(request, flash or "")


@router.get("/app/profile/formation", response_class=HTMLResponse)
def profile_formation_partial(request: Request, flash: str | None = None):
return _render_profile_formation_shell(request, flash or "")


@router.get("/app/profile/projects", response_class=HTMLResponse)
def profile_projects_partial(request: Request, flash: str | None = None):
return _render_profile_projects_shell(request, flash or "")


@router.get("/app/profile/experiences", response_class=HTMLResponse)
def profile_experiences_partial(request: Request, flash: str | None = None):
return _render_profile_experiences_shell(request, flash or "")


@router.post("/app/profile/save")
def save_profile(
request: Request,
Expand Down Expand Up @@ -268,7 +363,7 @@ def save_profile(
)
flash = "profile_saved" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_basics_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand All @@ -287,7 +382,7 @@ def add_skill(
flash = "skill_added" if result["success"] else "profile_error"

if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_skills_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand All @@ -296,7 +391,7 @@ def delete_skill(request: Request, skill_id: int):
result = profile_repository.delete_skill(skill_id)
flash = "skill_deleted" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_skills_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand Down Expand Up @@ -333,7 +428,7 @@ def add_experience(
)
flash = "experience_saved" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_experiences_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand Down Expand Up @@ -367,7 +462,7 @@ def update_experience(
)
flash = "experience_saved" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_experiences_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand All @@ -376,7 +471,7 @@ def delete_experience(request: Request, experience_id: int):
result = profile_repository.delete_experience(experience_id)
flash = "experience_deleted" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_experiences_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand Down Expand Up @@ -415,7 +510,7 @@ def add_project(
)
flash = "project_saved" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_projects_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand Down Expand Up @@ -451,7 +546,7 @@ def update_project(
)
flash = "project_saved" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_projects_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand All @@ -460,7 +555,7 @@ def delete_project(request: Request, project_id: int):
result = profile_repository.delete_project(project_id)
flash = "project_deleted" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_projects_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand Down Expand Up @@ -493,7 +588,7 @@ def add_education(
)
flash = "education_saved" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_formation_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand All @@ -502,7 +597,7 @@ def delete_education(request: Request, education_id: int):
result = profile_repository.delete_education(education_id)
flash = "education_deleted" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_formation_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand Down Expand Up @@ -533,7 +628,7 @@ def add_course(
)
flash = "course_saved" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_formation_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand All @@ -542,7 +637,7 @@ def delete_course(request: Request, course_id: int):
result = profile_repository.delete_course(course_id)
flash = "course_deleted" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_formation_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand Down Expand Up @@ -573,7 +668,7 @@ def add_certification(
)
flash = "certification_saved" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_formation_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)


Expand All @@ -582,6 +677,6 @@ def delete_certification(request: Request, certification_id: int):
result = profile_repository.delete_certification(certification_id)
flash = "certification_deleted" if result["success"] else "profile_error"
if request.headers.get("HX-Request") == "true":
return _render_profile_shell(request, flash)
return _render_profile_formation_shell(request, flash, include_oob=True)
return RedirectResponse(url=f"/app/profile?flash={flash}", status_code=303)

39 changes: 39 additions & 0 deletions app/interfaces/web/static/css/app.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* Layer 1: Design tokens */
:root {
--bg: #f5f7fb;
--surface: #ffffff;
Expand All @@ -12,6 +13,7 @@
--shadow: 0 10px 24px rgba(15, 23, 42, 0.08);
}

/* Layer 2: Reset and base elements */
* {
box-sizing: border-box;
}
Expand All @@ -27,6 +29,7 @@ a {
color: var(--brand);
}

/* Layer 3: Global shell and page layout */
.shell-header,
.kpi-strip,
.page-shell {
Expand Down Expand Up @@ -214,6 +217,7 @@ a {
font-size: 0.92rem;
}

/* Layer 4: Shared surfaces, links and summary components */
.primary-link,
.stack-form button {
display: inline-flex;
Expand Down Expand Up @@ -304,6 +308,7 @@ a {
color: var(--muted);
}

/* Layer 5: Forms and interactive controls */
.filter-form {
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(220px, 1fr) auto;
Expand Down Expand Up @@ -403,6 +408,7 @@ a {
font: inherit;
}

/* Layer 6: Shared tables, rows, details and status states */
.simple-table {
display: grid;
gap: 8px;
Expand Down Expand Up @@ -782,6 +788,7 @@ a {
padding: 16px;
}

/* Layer 7: Vacancies feature */
.inbox-workspace {
align-items: start;
}
Expand Down Expand Up @@ -1086,6 +1093,35 @@ a {
margin: 0;
}

/* Layer 8: Applications feature */
.application-row {
border: 1px solid var(--border);
border-radius: 14px;
background: #fbfcfe;
}

.application-row:hover {
border-color: #bfdbfe;
background: #f8fbff;
transform: translateY(-1px);
}

.application-row.is-selected {
border-color: #b3c9ff;
background: #eef4ff;
box-shadow: inset 4px 0 0 #1d4ed8;
}

.application-list-panel,
.application-detail-panel {
min-width: 0;
}

.application-detail-panel {
align-self: start;
}

/* Layer 9: Shared helpers and feedback */
.pill-group {
display: flex;
flex-wrap: wrap;
Expand Down Expand Up @@ -1120,6 +1156,7 @@ a {
margin: 0 0 8px;
}

/* Layer 10: Flash feedback states */
.flash-message {
margin-bottom: 14px;
padding: 12px 14px;
Expand All @@ -1145,6 +1182,7 @@ a {
color: #92400e;
}

/* Layer 11: Profile feature and CV preview */
.profile-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
Expand Down Expand Up @@ -1457,6 +1495,7 @@ a {
color: var(--text);
}

/* Layer 12: Responsive adjustments */
@media (max-width: 900px) {
.shell-header,
.page-header {
Expand Down
35 changes: 35 additions & 0 deletions app/interfaces/web/templates/applications/_filters.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<section class="panel filter-panel">
<form
class="filter-form"
method="get"
action="/app/applications"
hx-get="/app/applications/shell"
hx-target="#applications-shell"
hx-swap="outerHTML"
hx-push-url="true"
>
<label>
<span>Buscar</span>
<input type="text" name="q" value="{{ query }}" placeholder="Empresa o cargo">
</label>
<label>
<span>Estado</span>
<select name="state">
<option value="Todos"{% if current_state == "Todos" %} selected{% endif %}>Todos</option>
{% for option in follow_up_states %}
<option value="{{ option }}"{% if option == current_state %} selected{% endif %}>{{ status_labels.get(option, option) }}</option>
{% endfor %}
</select>
</label>
<label>
<span>Por pagina</span>
<select name="page_size">
{% for option in pagination.page_size_options %}
<option value="{{ option }}"{% if option == pagination.page_size %} selected{% endif %}>{{ option }}</option>
{% endfor %}
</select>
</label>
<input type="hidden" name="page" value="1">
<button class="secondary-action filter-submit" type="submit">Filtrar</button>
</form>
</section>
Loading
Loading