119 lines
4.5 KiB
HTML
119 lines
4.5 KiB
HTML
@if (project) {
|
||
<section class="project-detail-panel" [style.--prj-accent]="project.accentColor" aria-live="polite">
|
||
<div class="project-detail-header">
|
||
<div>
|
||
<span class="detail-kicker">
|
||
{{ project.code }}
|
||
@if (project.inDevelopment) {
|
||
<span class="detail-dev-tag">● {{ i18n.t().fpInDevelopment }}</span>
|
||
}
|
||
</span>
|
||
<h3>{{ project.title }}</h3>
|
||
</div>
|
||
<button type="button" class="close-button" (click)="onClose()" [attr.aria-label]="i18n.t().projectDetailCloseAria">
|
||
×
|
||
</button>
|
||
</div>
|
||
|
||
<div class="project-detail-layout">
|
||
<div class="project-detail-visual">
|
||
@if (project.images && project.images.length > 0) {
|
||
<div class="main-image-viewport">
|
||
@if (getCurrentImage(); as currentImg) {
|
||
@if (!imageErrors()[currentImg]) {
|
||
<img
|
||
[src]="currentImg"
|
||
[alt]="project.title + ' vista ' + (activeImageIndex() + 1)"
|
||
(error)="onImageError(currentImg)"
|
||
loading="lazy"
|
||
/>
|
||
} @else {
|
||
<div class="detail-placeholder">
|
||
<span class="placeholder-icon">◧</span>
|
||
<span class="placeholder-code">{{ project.code }}</span>
|
||
</div>
|
||
}
|
||
} @else {
|
||
<div class="detail-placeholder">
|
||
<span class="placeholder-icon">◧</span>
|
||
<span class="placeholder-code">{{ project.code }}</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
@if (project.images.length > 1) {
|
||
<div class="gallery-thumbs" role="tablist" aria-label="Miniaturas de capturas">
|
||
@for (img of project.images; track img; let i = $index) {
|
||
<button
|
||
type="button"
|
||
class="thumb-btn"
|
||
[class.is-active]="activeImageIndex() === i"
|
||
[attr.aria-selected]="activeImageIndex() === i"
|
||
(click)="selectImage(i)"
|
||
>
|
||
@if (!imageErrors()[img]) {
|
||
<img [src]="img" [alt]="project.title + ' miniatura ' + (i + 1)" (error)="onImageError(img)" loading="lazy" />
|
||
} @else {
|
||
<span class="thumb-label">{{ i + 1 }}</span>
|
||
}
|
||
</button>
|
||
}
|
||
</div>
|
||
}
|
||
} @else {
|
||
<!-- Ficha de Arquitectura Técnica para proyectos sin imágenes (SIPLECE) -->
|
||
<div class="technical-blueprint-panel">
|
||
<div class="blueprint-circuit-bg" aria-hidden="true"></div>
|
||
<div class="blueprint-panel-content">
|
||
<span class="blueprint-panel-icon">◧</span>
|
||
<span class="blueprint-panel-code">{{ project.code }}</span>
|
||
<h4 class="blueprint-panel-title">{{ project.title }}</h4>
|
||
<p class="blueprint-panel-subtitle">{{ i18n.t().fpNoImagesTechnical }}</p>
|
||
<div class="blueprint-specs-list">
|
||
<span class="spec-item">🏛 {{ project.institution }}</span>
|
||
<span class="spec-item">🔒 Autenticación JWT & RBAC</span>
|
||
<span class="spec-item">⚡ Clean Architecture (.NET + Angular)</span>
|
||
<span class="spec-item">📊 Trazabilidad & Auditoría CACES</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<div class="project-detail-body">
|
||
<div class="project-detail-meta">
|
||
<span class="meta-pill">{{ project.institution }}</span>
|
||
<span class="meta-pill subtle">{{ project.subtitle }}</span>
|
||
</div>
|
||
|
||
<p class="project-detail-summary">{{ project.description }}</p>
|
||
|
||
<div class="detail-block">
|
||
<h4>{{ i18n.t().projectDetailChallenge }}</h4>
|
||
<p>{{ project.challenge }}</p>
|
||
</div>
|
||
|
||
<div class="detail-block">
|
||
<h4>{{ i18n.t().projectDetailSolution }}</h4>
|
||
<p>{{ project.solution }}</p>
|
||
</div>
|
||
|
||
<div class="detail-block results-block">
|
||
<h4>{{ i18n.t().projectDetailResults }}</h4>
|
||
<ul>
|
||
@for (result of project.results; track result) {
|
||
<li>{{ result }}</li>
|
||
}
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="detail-tags">
|
||
@for (tag of project.tags; track tag) {
|
||
<span>{{ tag }}</span>
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
}
|