Piattaforma ITI TECHNOLOGY: monorepo, Medusa, API, sito, area riservata e deploy
Deploy stack su Proxmox / deploy (push) Failing after 3s
Deploy stack su Proxmox / deploy (push) Failing after 3s
Monorepo npm con apps/public (Angular SSR), apps/private (Angular SPA), apps/api (Fastify), packages/shared e packages/styles; Medusa 2.21 in medusa/; infrastruttura Docker (Postgres PostGIS+pgvector, Redis, Caddy, backup) e workflow Gitea Actions per il deploy via SSH a ogni push su master. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jQyT6dR9czzGwqb7FkG6u
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, effect, inject, input } from '@angular/core'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { COMPANY_PAGES, companyPagePath, findCompanyPage } from '../../content/company-pages'
|
||||
import { SITE_CONFIG } from '../core/app-config'
|
||||
import { SeoService } from '../core/seo.service'
|
||||
import { breadcrumbJsonLd } from '../core/structured-data'
|
||||
import { PageHeader } from '../shared/page-header'
|
||||
import { PlaceholderBadge } from '../shared/placeholder-badge'
|
||||
|
||||
/**
|
||||
* /azienda e /azienda/:pagina, generate da src/content/company/<slug>.ts. /azienda mostra la prima pagina
|
||||
* (Chi siamo); gli slug sconosciuti non arrivano qui (canMatch → 404).
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-company-page',
|
||||
imports: [RouterLink, PageHeader, PlaceholderBadge],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
@let p = page();
|
||||
<app-page-header [heading]="p.title" [lead]="p.lead" [crumbs]="crumbs()">
|
||||
<app-placeholder-badge badge [show]="p.placeholder" />
|
||||
</app-page-header>
|
||||
<div class="container layout">
|
||||
<nav class="subnav" aria-label="Azienda">
|
||||
<ul>
|
||||
@for (item of pages; track item.slug) {
|
||||
<li>
|
||||
<a [routerLink]="path(item)" [attr.aria-current]="item === p ? 'page' : null">{{ item.title }}</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
<article>
|
||||
@for (section of p.sections; track section.heading) {
|
||||
<section>
|
||||
<h2>{{ section.heading }}</h2>
|
||||
@for (paragraph of section.paragraphs; track $index) {
|
||||
<p>{{ paragraph }}</p>
|
||||
}
|
||||
@if (section.items?.length) {
|
||||
<ul class="items">
|
||||
@for (item of section.items; track $index) {
|
||||
<li>{{ item }}</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
@if (p.cta; as cta) {
|
||||
<a class="btn btn--primary" [routerLink]="cta.path" [queryParams]="cta.query ?? null">{{ cta.label }}</a>
|
||||
}
|
||||
</article>
|
||||
</div>
|
||||
`,
|
||||
styles: `
|
||||
@use 'mixins' as mq;
|
||||
|
||||
.layout {
|
||||
display: grid;
|
||||
gap: var(--space-6);
|
||||
padding-block: var(--space-6) var(--space-8);
|
||||
|
||||
@include mq.up(lg) {
|
||||
grid-template-columns: 220px 1fr;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
}
|
||||
.subnav ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-2);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
@include mq.up(lg) {
|
||||
flex-direction: column;
|
||||
position: sticky;
|
||||
top: calc(var(--header-height) + var(--space-5));
|
||||
}
|
||||
}
|
||||
.subnav a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 44px;
|
||||
padding-inline: var(--space-3);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-ink);
|
||||
text-decoration: none;
|
||||
|
||||
&[aria-current='page'] {
|
||||
background: var(--color-primary-soft);
|
||||
color: var(--color-primary);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
}
|
||||
article {
|
||||
max-width: 70ch;
|
||||
}
|
||||
section {
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
h2 {
|
||||
font-size: var(--font-size-2xl);
|
||||
}
|
||||
.items {
|
||||
padding-left: var(--space-5);
|
||||
|
||||
li {
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
export default class CompanyPage {
|
||||
/** Parametro di route (assente su /azienda). */
|
||||
readonly pagina = input<string>()
|
||||
|
||||
protected readonly pages = COMPANY_PAGES
|
||||
protected readonly path = companyPagePath
|
||||
protected readonly page = computed(() => findCompanyPage(this.pagina() ?? '') ?? COMPANY_PAGES[0]!)
|
||||
protected readonly crumbs = computed(() =>
|
||||
this.pagina() ? [{ label: 'Azienda', path: '/azienda' }, { label: this.page().title }] : [{ label: 'Azienda' }],
|
||||
)
|
||||
|
||||
constructor() {
|
||||
const seo = inject(SeoService)
|
||||
const { siteUrl } = inject(SITE_CONFIG)
|
||||
effect(() => {
|
||||
const page = this.page()
|
||||
const path = companyPagePath(page)
|
||||
seo.setPage({ title: page.title, description: page.description, path })
|
||||
seo.setJsonLd(
|
||||
'breadcrumb',
|
||||
breadcrumbJsonLd(siteUrl, path === '/azienda' ? [{ name: 'Azienda', path }] : [{ name: 'Azienda', path: '/azienda' }, { name: page.title, path }]),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user