Reformer Class: bottone consulenza conoscitiva gratuita (#22)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
// Consulenza conoscitiva gratuita con Nicola (prenotazione su Google Calendar).
|
||||
//
|
||||
// Il link vive nel pannello contenuti (tag `global.consulenza.url`): qui c'è il valore di
|
||||
// partenza e la guardia. Il tag è modificabile da admin, quindi si accetta solo https.
|
||||
export const CONSULENZA_URL_DEFAULT = 'https://calendar.app.google/nWXMGD2S5g2pVeeD7';
|
||||
|
||||
/** Slug delle pagine servizio che mostrano il bottone di prenotazione. */
|
||||
export const PAGINE_CON_CONSULENZA = ['reformer-class'];
|
||||
|
||||
/** Link da usare: il valore del pannello se è https, altrimenti quello di default
|
||||
* (niente javascript:, http:, vuoto). */
|
||||
export function linkConsulenza(valore: string): string {
|
||||
const v = (valore ?? '').trim();
|
||||
return v.startsWith('https://') ? v : CONSULENZA_URL_DEFAULT;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { partners } from './partners';
|
||||
import { disciplines, days, slots } from './agenda';
|
||||
import { services } from './services';
|
||||
import { performanceClassPricing } from './pricing';
|
||||
import { CONSULENZA_URL_DEFAULT } from './consulenza';
|
||||
|
||||
export type ContentType = 'text' | 'html' | 'image';
|
||||
export interface SeedEntry { tag: string; type: ContentType; value: string; styleOptions?: StyleGroup[] }
|
||||
@@ -270,6 +271,9 @@ export const contentSeed: SeedEntry[] = [
|
||||
T('service.pricing.entries-plural', 'ingressi settimanali', []),
|
||||
T('service.pricing.buy-cta', 'Acquista', []),
|
||||
T('service.contact-cta', 'Contattaci', []),
|
||||
T('service.consulenza-cta', 'Prenota una consulenza conoscitiva gratuita', []),
|
||||
T('service.consulenza-note', '30-45 minuti con Nicola', []),
|
||||
T('global.consulenza.url', CONSULENZA_URL_DEFAULT, []),
|
||||
|
||||
// --- Pricing performance class (data-driven da pricing.ts; monthly/entries restano nel data file) ---
|
||||
T('pricing.heading', performanceClassPricing.heading),
|
||||
|
||||
@@ -10,6 +10,7 @@ import TImg from '../../components/content/TImg.astro';
|
||||
import CalendarioModal from '../../components/CalendarioModal.astro';
|
||||
import { calendariDaMostrare, PAGINE_CON_CALENDARIO } from '../../data/calendari';
|
||||
import { t } from '../../lib/content';
|
||||
import { PAGINE_CON_CONSULENZA, linkConsulenza } from '../../data/consulenza';
|
||||
export const prerender = false;
|
||||
|
||||
const service = getService(Astro.params.slug!);
|
||||
@@ -38,8 +39,16 @@ const tones = ['tan', 'taupe', 'dark'];
|
||||
<div>
|
||||
<h2>{t(`service.${slug}.title`).toUpperCase()}</h2>
|
||||
<T tag={`service.${slug}.long-description`} as="p" class="prog__desc" />
|
||||
{slug === 'reformer-class' && (
|
||||
<p class="prog__cta"><a class="btn" href="/contact">{t('service.contact-cta')}</a></p>
|
||||
{(slug === 'reformer-class' || PAGINE_CON_CONSULENZA.includes(slug)) && (
|
||||
<div class="prog__cta">
|
||||
<div class="prog__cta-row">
|
||||
{slug === 'reformer-class' && <a class="btn" href="/contact">{t('service.contact-cta')}</a>}
|
||||
{PAGINE_CON_CONSULENZA.includes(slug) && (
|
||||
<a class="btn btn--consulenza" href={linkConsulenza(t('global.consulenza.url'))} target="_blank" rel="noopener noreferrer">{t('service.consulenza-cta')}</a>
|
||||
)}
|
||||
</div>
|
||||
{PAGINE_CON_CONSULENZA.includes(slug) && <T tag="service.consulenza-note" as="p" class="prog__cta-note" />}
|
||||
</div>
|
||||
)}
|
||||
{service.features.length > 0 && (
|
||||
<ul class="prog__features">
|
||||
@@ -149,6 +158,11 @@ const tones = ['tan', 'taupe', 'dark'];
|
||||
.prog__calendario { display: flex; align-items: center; gap: 14px; width: 100%; margin-top: 36px; padding: 10px 16px 10px 10px; border: 0; cursor: pointer; text-align: left; background: #6b5d5b; color: #fff; font-family: var(--font-heading); font-size: .82rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; transition: background .2s; }
|
||||
.prog__calendario:hover { background: var(--c-accent-dark); }
|
||||
.prog__cta { margin: 10px 0 30px; }
|
||||
.prog__cta-row { display: flex; flex-wrap: wrap; gap: 12px; }
|
||||
.btn--consulenza { background: #6b5d5b; }
|
||||
.btn--consulenza:hover { background: var(--c-accent-dark); }
|
||||
.prog__cta-note { font-size: .8rem; color: var(--c-text-light); margin: 8px 0 0; }
|
||||
@media (max-width: 600px) { .prog__cta-row .btn { flex: 1 1 100%; justify-content: center; text-align: center; } }
|
||||
.prog__icon { display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: var(--c-accent); color: #fff; flex-shrink: 0; }
|
||||
.price__group { border-top: 1px solid #ddd5ca; padding-block: 18px; }
|
||||
.price__group summary { cursor: pointer; font-family: var(--font-heading); letter-spacing: .12em; text-transform: uppercase; font-size: .85rem; color: var(--c-heading); }
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { linkConsulenza, CONSULENZA_URL_DEFAULT, PAGINE_CON_CONSULENZA } from '../src/data/consulenza';
|
||||
import { contentSeed } from '../src/data/content-seed';
|
||||
import { services } from '../src/data/services';
|
||||
|
||||
describe('consulenza conoscitiva', () => {
|
||||
it('accetta un link https, anche con spazi attorno', () => {
|
||||
expect(linkConsulenza('https://example.com/x')).toBe('https://example.com/x');
|
||||
expect(linkConsulenza(' https://example.com/x ')).toBe('https://example.com/x');
|
||||
});
|
||||
it.each(['javascript:alert(1)', 'http://x.it', '', ' '])('scarta %j e usa il default', (v) => {
|
||||
expect(linkConsulenza(v)).toBe(CONSULENZA_URL_DEFAULT);
|
||||
});
|
||||
it('il seed ha i tre tag', () => {
|
||||
const v = (tag: string) => contentSeed.find((e) => e.tag === tag)?.value;
|
||||
expect(v('service.consulenza-cta')).toBe('Prenota una consulenza conoscitiva gratuita');
|
||||
expect(v('service.consulenza-note')).toBe('30-45 minuti con Nicola');
|
||||
expect(v('global.consulenza.url')).toBe(CONSULENZA_URL_DEFAULT);
|
||||
});
|
||||
it('le pagine con consulenza esistono', () => {
|
||||
expect(PAGINE_CON_CONSULENZA).toContain('reformer-class');
|
||||
for (const slug of PAGINE_CON_CONSULENZA) {
|
||||
const s = services.find((x) => x.slug === slug);
|
||||
expect(s, slug).toBeDefined();
|
||||
expect(s!.page).not.toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user