Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21d9755e37 | |||
| ee54e9a1f4 | |||
| 62e0120ea2 | |||
| 2f94fc0373 | |||
| e6c493b756 |
@@ -14,8 +14,8 @@ const { variant = 'full' } = Astro.props;
|
|||||||
data-msg-error-network={t('form.msg.error-network')}
|
data-msg-error-network={t('form.msg.error-network')}
|
||||||
>
|
>
|
||||||
<input class="field" name="firstName" placeholder={t('form.field.first-name')} required maxlength="100" />
|
<input class="field" name="firstName" placeholder={t('form.field.first-name')} required maxlength="100" />
|
||||||
{variant === 'full' && <input class="field" name="lastName" placeholder={t('form.field.last-name')} maxlength="100" />}
|
<input class="field" name="lastName" placeholder={t('form.field.last-name')} required maxlength="100" />
|
||||||
{variant === 'full' && <input class="field" name="phone" placeholder={t('form.field.phone')} maxlength="40" />}
|
<input class="field" type="tel" name="phone" placeholder={t('form.field.phone')} required maxlength="40" />
|
||||||
<input class="field" type="email" name="email" placeholder={t('form.field.email')} required maxlength="200" />
|
<input class="field" type="email" name="email" placeholder={t('form.field.email')} required maxlength="200" />
|
||||||
<textarea class="field" name="message" placeholder={t('form.field.message')} required maxlength="5000"></textarea>
|
<textarea class="field" name="message" placeholder={t('form.field.message')} required maxlength="5000"></textarea>
|
||||||
<input class="hp" type="text" name="website" tabindex="-1" autocomplete="off" />
|
<input class="hp" type="text" name="website" tabindex="-1" autocomplete="off" />
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { site } from '../data/site';
|
import { site } from '../data/site';
|
||||||
import ContactForm from './ContactForm.astro';
|
|
||||||
import { t, contentImageUrl } from '../lib/content';
|
import { t, contentImageUrl } from '../lib/content';
|
||||||
import T from './content/T.astro';
|
import T from './content/T.astro';
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
@@ -24,10 +23,6 @@ const year = new Date().getFullYear();
|
|||||||
<T tag="footer.col.services-title" as="h4" />
|
<T tag="footer.col.services-title" as="h4" />
|
||||||
{site.footerServices.map((l, i) => <a href={l.href}>{t(`footer.services.${i + 1}.label`)}</a>)}
|
{site.footerServices.map((l, i) => <a href={l.href}>{t(`footer.services.${i + 1}.label`)}</a>)}
|
||||||
</nav>
|
</nav>
|
||||||
<div>
|
|
||||||
<T tag="footer.col.contact-title" as="h4" />
|
|
||||||
<ContactForm variant="footer" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ftr__bar">
|
<div class="ftr__bar">
|
||||||
<div class="container ftr__bar-in">
|
<div class="container ftr__bar-in">
|
||||||
@@ -42,7 +37,11 @@ const year = new Date().getFullYear();
|
|||||||
.ftr a { color: #b9aea4; text-decoration: none; display: block; padding: 4px 0; }
|
.ftr a { color: #b9aea4; text-decoration: none; display: block; padding: 4px 0; }
|
||||||
.ftr a:hover { color: #fff; }
|
.ftr a:hover { color: #fff; }
|
||||||
.ftr h4 { color: #fff; text-transform: uppercase; letter-spacing: .18em; font-size: .78rem; margin-bottom: 18px; }
|
.ftr h4 { color: #fff; text-transform: uppercase; letter-spacing: .18em; font-size: .78rem; margin-bottom: 18px; }
|
||||||
.ftr__grid { display: grid; grid-template-columns: 1.3fr 1fr 1fr 1.3fr; gap: 40px; padding-block: 70px; }
|
.ftr__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr; gap: 40px; padding-block: 26px; text-align: center; align-items: start; }
|
||||||
|
/* Il logo è display:block (reset globale): lo centro nella colonna */
|
||||||
|
.ftr__grid img { margin-inline: auto; }
|
||||||
|
/* Training e Servizi allineati alla riga "Promuoviamo il movimento..." (logo+tagline ~110px) */
|
||||||
|
.ftr__grid nav { padding-top: 110px; }
|
||||||
.ftr__tag { font-family: var(--font-heading); font-size: .7rem; letter-spacing: .2em; color: #fff; }
|
.ftr__tag { font-family: var(--font-heading); font-size: .7rem; letter-spacing: .2em; color: #fff; }
|
||||||
.ftr__hours { margin: 0; }
|
.ftr__hours { margin: 0; }
|
||||||
.ftr__legal { margin-top: 14px; font-size: .78rem; opacity: .8; }
|
.ftr__legal { margin-top: 14px; font-size: .78rem; opacity: .8; }
|
||||||
@@ -50,6 +49,6 @@ const year = new Date().getFullYear();
|
|||||||
.ftr__bar { border-top: 1px solid rgba(255,255,255,.08); padding-block: 18px; }
|
.ftr__bar { border-top: 1px solid rgba(255,255,255,.08); padding-block: 18px; }
|
||||||
.ftr__bar-in { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
|
.ftr__bar-in { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
|
||||||
.ftr__bar-in a { display: inline; margin-left: 12px; }
|
.ftr__bar-in a { display: inline; margin-left: 12px; }
|
||||||
@media (max-width: 991px) { .ftr__grid { grid-template-columns: 1fr 1fr; } }
|
@media (max-width: 991px) { .ftr__grid { grid-template-columns: 1fr 1fr; } .ftr__grid nav { padding-top: 0; } }
|
||||||
@media (max-width: 600px) { .ftr__grid { grid-template-columns: 1fr; } }
|
@media (max-width: 600px) { .ftr__grid { grid-template-columns: 1fr; } }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ const areaHref = user?.role === 'superuser' ? '/admin/content' : '/admin';
|
|||||||
<a href="/" class="hdr__logo"><img src={contentImageUrl('header.logo.image', '/img/logo-dark.png')} alt={t('header.logo.alt')} width="150" /></a>
|
<a href="/" class="hdr__logo"><img src={contentImageUrl('header.logo.image', '/img/logo-dark.png')} alt={t('header.logo.alt')} width="150" /></a>
|
||||||
<nav class="hdr__nav" id="site-nav" aria-label="principale">
|
<nav class="hdr__nav" id="site-nav" aria-label="principale">
|
||||||
{site.navigation.map((item, i) => (
|
{site.navigation.map((item, i) => (
|
||||||
|
(!('adminOnly' in item && item.adminOnly) || user?.role === 'admin') && (
|
||||||
<a href={item.href} class:list={['hdr__link', { 'is-active': path === item.href || (item.href !== '/' && path.startsWith(item.href)) }]}>{t(`global.nav.${i + 1}.label`)}</a>
|
<a href={item.href} class:list={['hdr__link', { 'is-active': path === item.href || (item.href !== '/' && path.startsWith(item.href)) }]}>{t(`global.nav.${i + 1}.label`)}</a>
|
||||||
|
)
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
<div class="hdr__user">
|
<div class="hdr__user">
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
---
|
---
|
||||||
import hero1 from '../../assets/img/hero-1.png';
|
// Foto hero rimossa ma conservata su disco per riuso futuro: assets/img/hero-1.png
|
||||||
import T from '../content/T.astro';
|
import T from '../content/T.astro';
|
||||||
import TImg from '../content/TImg.astro';
|
|
||||||
import { t } from '../../lib/content';
|
import { t } from '../../lib/content';
|
||||||
const slides = [
|
const slides = [
|
||||||
{ titleTags: ['home.hero.title-line-1', 'home.hero.title-line-2', 'home.hero.title-line-3'], image: hero1, alt: 'Atleti InsanityLab' },
|
{ titleTags: ['home.hero.title-line-1', 'home.hero.title-line-2', 'home.hero.title-line-3'] },
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
<section class="hero" aria-label="presentazione">
|
<section class="hero" aria-label="presentazione">
|
||||||
@@ -15,7 +14,6 @@ const slides = [
|
|||||||
<h1>{s.titleTags.map((tag) => <T tag={tag} as="span" class="hero__line" />)}</h1>
|
<h1>{s.titleTags.map((tag) => <T tag={tag} as="span" class="hero__line" />)}</h1>
|
||||||
<a class="btn" href="/services">{t('home.hero.cta')}</a>
|
<a class="btn" href="/services">{t('home.hero.cta')}</a>
|
||||||
</div>
|
</div>
|
||||||
<TImg tag="home.hero.image" src={s.image} alt={s.alt} class="hero__img" widths={[600, 1000, 1400]} sizes="(max-width: 991px) 100vw, 60vw" loading="eager" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -34,32 +32,28 @@ const slides = [
|
|||||||
.hero__slide.is-active { display: block; }
|
.hero__slide.is-active { display: block; }
|
||||||
/* Come nel Figma: figure a piena altezza ancorate in basso a destra,
|
/* Come nel Figma: figure a piena altezza ancorate in basso a destra,
|
||||||
testo sovrapposto sulla sinistra. */
|
testo sovrapposto sulla sinistra. */
|
||||||
.hero__in { position: relative; display: flex; align-items: center; min-height: calc(100vh - var(--header-h)); }
|
.hero__in { position: relative; display: flex; align-items: center; min-height: 0; padding-block: 90px; }
|
||||||
/* Lo span .hero__line è reso dal componente <T> e non riceve il data-astro-cid
|
/* Lo span .hero__line è reso dal componente <T> e non riceve il data-astro-cid
|
||||||
di questo componente: si ancora a .hero__text (che il cid ce l'ha) + :global. */
|
di questo componente: si ancora a .hero__text (che il cid ce l'ha) + :global. */
|
||||||
.hero__text h1 :global(.hero__line) { display: block; }
|
.hero__text h1 :global(.hero__line) { display: block; }
|
||||||
.hero__text { position: relative; z-index: 1; max-width: 46%; }
|
.hero__text { position: relative; z-index: 1; max-width: 640px; }
|
||||||
.hero__text h1 { font-weight: 500; }
|
.hero__text h1 { font-weight: 500; }
|
||||||
.hero__img { position: absolute; right: 0; bottom: 0; height: 100%; width: auto; max-width: 80%; object-fit: contain; object-position: right bottom; }
|
|
||||||
.hero__arrow { position: absolute; top: 50%; transform: translateY(-50%); background: none; border: 0; font-size: 3rem; color: var(--c-heading); cursor: pointer; }
|
.hero__arrow { position: absolute; top: 50%; transform: translateY(-50%); background: none; border: 0; font-size: 3rem; color: var(--c-heading); cursor: pointer; }
|
||||||
.hero__arrow--prev { left: 12px; } .hero__arrow--next { right: 12px; }
|
.hero__arrow--prev { left: 12px; } .hero__arrow--next { right: 12px; }
|
||||||
@media (max-width: 991px) {
|
@media (max-width: 991px) {
|
||||||
.hero__in { flex-direction: column; align-items: flex-start; padding-block: 40px; min-height: 0; gap: 24px; }
|
.hero__in { flex-direction: column; align-items: flex-start; padding-block: 40px; min-height: 0; gap: 24px; }
|
||||||
.hero__text { max-width: none; }
|
.hero__text { max-width: none; }
|
||||||
.hero__img { position: static; max-width: 100%; height: auto; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ingresso al load stile Slider Revolution del template */
|
/* Ingresso al load stile Slider Revolution del template */
|
||||||
@keyframes hero-up { from { opacity: 0; transform: translateY(60px); } to { opacity: 1; transform: none; } }
|
@keyframes hero-up { from { opacity: 0; transform: translateY(60px); } to { opacity: 1; transform: none; } }
|
||||||
@keyframes hero-side { from { opacity: 0; transform: translateX(60px); } to { opacity: 1; transform: none; } }
|
|
||||||
:global(html.js) .hero__text h1 :global(.hero__line) { opacity: 0; animation: hero-up .8s ease forwards; }
|
:global(html.js) .hero__text h1 :global(.hero__line) { opacity: 0; animation: hero-up .8s ease forwards; }
|
||||||
:global(html.js) .hero__text h1 :global(.hero__line:nth-child(1)) { animation-delay: .15s; }
|
:global(html.js) .hero__text h1 :global(.hero__line:nth-child(1)) { animation-delay: .15s; }
|
||||||
:global(html.js) .hero__text h1 :global(.hero__line:nth-child(2)) { animation-delay: .3s; }
|
:global(html.js) .hero__text h1 :global(.hero__line:nth-child(2)) { animation-delay: .3s; }
|
||||||
:global(html.js) .hero__text h1 :global(.hero__line:nth-child(3)) { animation-delay: .45s; }
|
:global(html.js) .hero__text h1 :global(.hero__line:nth-child(3)) { animation-delay: .45s; }
|
||||||
:global(html.js) .hero__text .btn { opacity: 0; animation: hero-up .8s ease .7s forwards; }
|
:global(html.js) .hero__text .btn { opacity: 0; animation: hero-up .8s ease .7s forwards; }
|
||||||
:global(html.js) .hero__img { opacity: 0; animation: hero-side 1s ease .2s forwards; }
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
:global(html.js) .hero__text h1 :global(.hero__line), :global(html.js) .hero__text .btn, :global(html.js) .hero__img { opacity: 1; animation: none; }
|
:global(html.js) .hero__text h1 :global(.hero__line), :global(html.js) .hero__text .btn { opacity: 1; animation: none; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -18,7 +18,8 @@ export const site = {
|
|||||||
{ label: "Blog & Edugo", href: "/blog" },
|
{ label: "Blog & Edugo", href: "/blog" },
|
||||||
{ label: "Promo ed eventi", href: "/promo-eventi" },
|
{ label: "Promo ed eventi", href: "/promo-eventi" },
|
||||||
{ label: "Contatti", href: "/contact" },
|
{ label: "Contatti", href: "/contact" },
|
||||||
{ label: "Promo 2 (test)", href: "/promo-eventi2" },
|
// Pagina di test: visibile solo agli admin loggati, nascosta al pubblico.
|
||||||
|
{ label: "Promo 2 (test)", href: "/promo-eventi2", adminOnly: true },
|
||||||
],
|
],
|
||||||
footerTraining: [
|
footerTraining: [
|
||||||
{ label: "One to one", href: "/training#one-to-one" },
|
{ label: "One to one", href: "/training#one-to-one" },
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ export function validateContact(data: unknown):
|
|||||||
};
|
};
|
||||||
if (value.website) return { ok: false, error: 'Richiesta non valida.' };
|
if (value.website) return { ok: false, error: 'Richiesta non valida.' };
|
||||||
if (!value.firstName) return { ok: false, error: 'Il nome è obbligatorio.' };
|
if (!value.firstName) return { ok: false, error: 'Il nome è obbligatorio.' };
|
||||||
|
if (!value.lastName) return { ok: false, error: 'Il cognome è obbligatorio.' };
|
||||||
|
if (!value.phone) return { ok: false, error: 'Il telefono è obbligatorio.' };
|
||||||
if (!EMAIL_RE.test(value.email)) return { ok: false, error: 'Inserisci una e-mail valida.' };
|
if (!EMAIL_RE.test(value.email)) return { ok: false, error: 'Inserisci una e-mail valida.' };
|
||||||
if (!value.message) return { ok: false, error: 'Il messaggio è obbligatorio.' };
|
if (!value.message) return { ok: false, error: 'Il messaggio è obbligatorio.' };
|
||||||
if (value.message.length > 5000) return { ok: false, error: 'Messaggio troppo lungo.' };
|
if (value.message.length > 5000) return { ok: false, error: 'Messaggio troppo lungo.' };
|
||||||
|
|||||||
+19
-1
@@ -91,7 +91,8 @@ export function createDb(path?: string): Database.Database {
|
|||||||
// le righe rimaste identiche al valore originale del seed (mai modificate dal pannello):
|
// le righe rimaste identiche al valore originale del seed (mai modificate dal pannello):
|
||||||
// il nuovo valore è quello del seed corrente per lo stesso tag.
|
// il nuovo valore è quello del seed corrente per lo stesso tag.
|
||||||
const OLD_SECTION_DEFAULTS: [string, string][] = [
|
const OLD_SECTION_DEFAULTS: [string, string][] = [
|
||||||
['global.nav.3.label', 'Programmi'],
|
// La voce "Servizi" (ex "Programmi") è la 2ª del menu dopo il riordino (commit 5ff4771).
|
||||||
|
['global.nav.2.label', 'Programmi'],
|
||||||
['footer.col.services-title', 'Programmi'],
|
['footer.col.services-title', 'Programmi'],
|
||||||
['home.services.title', 'Programmi'],
|
['home.services.title', 'Programmi'],
|
||||||
['home.services.body', 'Questi sono i programmi delle classi di Insanity Lab selezionati per voi. Abbiamo scelto i migliori percorsi per offrirvi un’esperienza di allenamento completa e mirata.'],
|
['home.services.body', 'Questi sono i programmi delle classi di Insanity Lab selezionati per voi. Abbiamo scelto i migliori percorsi per offrirvi un’esperienza di allenamento completa e mirata.'],
|
||||||
@@ -165,6 +166,23 @@ export function createDb(path?: string): Database.Database {
|
|||||||
const updMetodoTx = db.transaction(() => { for (const [tag, oldV, newV] of METODO) upd02.run(newV, tag, oldV); });
|
const updMetodoTx = db.transaction(() => { for (const [tag, oldV, newV] of METODO) upd02.run(newV, tag, oldV); });
|
||||||
updMetodoTx();
|
updMetodoTx();
|
||||||
|
|
||||||
|
// Modifiche promo (commit 1522d97): la freccia "→" dei bottoni è passata al CSS (::after) e il
|
||||||
|
// maiuscolo a text-transform. Il seed è ora minuscolo e senza freccia. Le righe di prod ancora
|
||||||
|
// al vecchio default vanno allineate, altrimenti il CSS ::after produce una DOPPIA freccia.
|
||||||
|
// Guardato dal vecchio valore → idempotente, non tocca eventuali modifiche del pannello.
|
||||||
|
const PROMO_FRECCE: [string, string, string][] = [
|
||||||
|
['promo.hero.cta', 'LE NOSTRE PROPOSTE →', 'Le nostre proposte'],
|
||||||
|
['promo.buy-label', 'ACQUISTA →', 'Acquista'],
|
||||||
|
];
|
||||||
|
const updPromoTx = db.transaction(() => { for (const [tag, oldV, newV] of PROMO_FRECCE) upd02.run(newV, tag, oldV); });
|
||||||
|
updPromoTx();
|
||||||
|
|
||||||
|
// Fix: il titolo del servizio Performance Class era rimasto troncato a "Class" nei DB esistenti
|
||||||
|
// (valore vecchio mai allineato dal seed INSERT OR IGNORE), mostrando "CLASS" come H1 della
|
||||||
|
// pagina /services/performance-class e nelle card di /services e home. Guardato dal vecchio
|
||||||
|
// valore → idempotente, non tocca eventuali modifiche del pannello.
|
||||||
|
db.prepare("UPDATE content_blocks SET value = 'Performance Class' WHERE tag = 'service.performance-class.title' AND value = 'Class'").run();
|
||||||
|
|
||||||
// Modifiche 03: due ingressi dello stesso stabile → indirizzo esteso in footer e contatti.
|
// Modifiche 03: due ingressi dello stesso stabile → indirizzo esteso in footer e contatti.
|
||||||
// Il tag global.contact.map-query è nuovo (inserito dal seed); qui si aggiorna solo l'indirizzo
|
// Il tag global.contact.map-query è nuovo (inserito dal seed); qui si aggiorna solo l'indirizzo
|
||||||
// già esistente, se non modificato dal pannello. Idempotente.
|
// già esistente, se non modificato dal pannello. Idempotente.
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { describe, it, expect } from 'vitest';
|
|||||||
import { validateContact } from '../src/lib/contact';
|
import { validateContact } from '../src/lib/contact';
|
||||||
|
|
||||||
describe('validateContact', () => {
|
describe('validateContact', () => {
|
||||||
const good = { firstName: 'Mario', email: 'mario@example.com', message: 'Ciao, vorrei informazioni.' };
|
const good = { firstName: 'Mario', lastName: 'Rossi', phone: '3331234567', email: 'mario@example.com', message: 'Ciao, vorrei informazioni.' };
|
||||||
|
|
||||||
it('accetta payload valido e normalizza i campi opzionali', () => {
|
it('accetta payload valido', () => {
|
||||||
const r = validateContact(good);
|
const r = validateContact(good);
|
||||||
expect(r.ok).toBe(true);
|
expect(r.ok).toBe(true);
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
expect(r.value.lastName).toBe('');
|
expect(r.value.lastName).toBe('Rossi');
|
||||||
expect(r.value.email).toBe('mario@example.com');
|
expect(r.value.email).toBe('mario@example.com');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -19,6 +19,8 @@ describe('validateContact', () => {
|
|||||||
|
|
||||||
it('rifiuta campi obbligatori mancanti o vuoti', () => {
|
it('rifiuta campi obbligatori mancanti o vuoti', () => {
|
||||||
expect(validateContact({ ...good, firstName: ' ' }).ok).toBe(false);
|
expect(validateContact({ ...good, firstName: ' ' }).ok).toBe(false);
|
||||||
|
expect(validateContact({ ...good, lastName: ' ' }).ok).toBe(false);
|
||||||
|
expect(validateContact({ ...good, phone: '' }).ok).toBe(false);
|
||||||
expect(validateContact({ ...good, message: '' }).ok).toBe(false);
|
expect(validateContact({ ...good, message: '' }).ok).toBe(false);
|
||||||
expect(validateContact(null).ok).toBe(false);
|
expect(validateContact(null).ok).toBe(false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ describe('applyContentUpdate', () => {
|
|||||||
|
|
||||||
it('404 su tag inesistente, 400 su styles fuori whitelist', () => {
|
it('404 su tag inesistente, 400 su styles fuori whitelist', () => {
|
||||||
expect(applyContentUpdate(db, 'no.tag', { value: 'x' }, 'a')).toMatchObject({ ok: false, status: 404 });
|
expect(applyContentUpdate(db, 'no.tag', { value: 'x' }, 'a')).toMatchObject({ ok: false, status: 404 });
|
||||||
expect(applyContentUpdate(db, textTag, { styles: { size: 'xxl' } }, 'a')).toMatchObject({ ok: false, status: 400 });
|
expect(applyContentUpdate(db, textTag, { styles: { size: 'xxxl' } }, 'a')).toMatchObject({ ok: false, status: 400 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sanifica i tag html e rifiuta value su tag image', () => {
|
it('sanifica i tag html e rifiuta value su tag image', () => {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ describe('schema content_blocks e ruoli', () => {
|
|||||||
ins.run('footer.col.programs-title', 'Colonna modificata');
|
ins.run('footer.col.programs-title', 'Colonna modificata');
|
||||||
// Riga mai toccata dal pannello: valore identico al vecchio seed → deve prendere il nuovo default.
|
// Riga mai toccata dal pannello: valore identico al vecchio seed → deve prendere il nuovo default.
|
||||||
db1.prepare("INSERT INTO content_blocks (tag, type, value) VALUES ('programs.meta.title', 'text', 'Programmi')").run();
|
db1.prepare("INSERT INTO content_blocks (tag, type, value) VALUES ('programs.meta.title', 'text', 'Programmi')").run();
|
||||||
db1.prepare("UPDATE content_blocks SET value = 'Programmi' WHERE tag = 'global.nav.3.label'").run();
|
db1.prepare("UPDATE content_blocks SET value = 'Programmi' WHERE tag = 'global.nav.2.label'").run();
|
||||||
});
|
});
|
||||||
old();
|
old();
|
||||||
db1.close();
|
db1.close();
|
||||||
@@ -60,8 +60,10 @@ describe('schema content_blocks e ruoli', () => {
|
|||||||
expect(val('footer.col.services-title')).toMatchObject({ value: 'Colonna modificata', updated_by: 'editor' });
|
expect(val('footer.col.services-title')).toMatchObject({ value: 'Colonna modificata', updated_by: 'editor' });
|
||||||
|
|
||||||
// Le righe rimaste al vecchio valore di default prendono il nuovo ("Servizi").
|
// Le righe rimaste al vecchio valore di default prendono il nuovo ("Servizi").
|
||||||
|
// Dopo il riordino del menu (commit 5ff4771) la voce "Servizi" è la 2ª: la migrazione
|
||||||
|
// aggiorna global.nav.2.label da 'Programmi' al seed corrente 'Servizi'.
|
||||||
expect(val('services.meta.title')).toMatchObject({ value: 'Servizi' });
|
expect(val('services.meta.title')).toMatchObject({ value: 'Servizi' });
|
||||||
expect(val('global.nav.3.label')).toMatchObject({ value: 'Servizi' });
|
expect(val('global.nav.2.label')).toMatchObject({ value: 'Servizi' });
|
||||||
// La riga rinominata ma modificata dal pannello resta invariata (già verificata sopra):
|
// La riga rinominata ma modificata dal pannello resta invariata (già verificata sopra):
|
||||||
// home.services.title = 'Titolo modificato'.
|
// home.services.title = 'Titolo modificato'.
|
||||||
|
|
||||||
@@ -82,6 +84,61 @@ describe('schema content_blocks e ruoli', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('allinea i bottoni promo col vecchio testo-freccia al seed senza freccia', () => {
|
||||||
|
const dir = mkdtempSync(join(tmpdir(), 'il-db-promo-'));
|
||||||
|
const path = join(dir, 'test.db');
|
||||||
|
try {
|
||||||
|
// DB pre-refactor: valori con freccia letterale e maiuscolo (poi spostati nel CSS).
|
||||||
|
const db1 = createDb(path);
|
||||||
|
db1.prepare("UPDATE content_blocks SET value = 'LE NOSTRE PROPOSTE →' WHERE tag = 'promo.hero.cta'").run();
|
||||||
|
db1.prepare("UPDATE content_blocks SET value = 'ACQUISTA →' WHERE tag = 'promo.buy-label'").run();
|
||||||
|
// Riga modificata dal pannello: NON deve essere toccata.
|
||||||
|
db1.prepare("UPDATE content_blocks SET value = 'Compra ora', updated_by = 'editor' WHERE tag = 'promo.buy-label'").run();
|
||||||
|
db1.close();
|
||||||
|
|
||||||
|
// Riapertura: la migrazione allinea solo le righe rimaste al vecchio default con freccia.
|
||||||
|
const db2 = createDb(path);
|
||||||
|
const val = (tag: string) => (db2.prepare('SELECT value FROM content_blocks WHERE tag = ?').get(tag) as { value: string }).value;
|
||||||
|
expect(val('promo.hero.cta')).toBe('Le nostre proposte');
|
||||||
|
// promo.buy-label era stato editato dal pannello → resta invariato.
|
||||||
|
expect(val('promo.buy-label')).toBe('Compra ora');
|
||||||
|
db2.close();
|
||||||
|
|
||||||
|
// Idempotente: terza apertura non altera.
|
||||||
|
const db3 = createDb(path);
|
||||||
|
expect((db3.prepare("SELECT value FROM content_blocks WHERE tag = 'promo.hero.cta'").get() as { value: string }).value).toBe('Le nostre proposte');
|
||||||
|
db3.close();
|
||||||
|
} finally {
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('corregge il titolo Performance Class rimasto troncato a "Class"', () => {
|
||||||
|
const dir = mkdtempSync(join(tmpdir(), 'il-db-pc-'));
|
||||||
|
const path = join(dir, 'test.db');
|
||||||
|
try {
|
||||||
|
// DB con il vecchio valore troncato.
|
||||||
|
const db1 = createDb(path);
|
||||||
|
db1.prepare("UPDATE content_blocks SET value = 'Class', updated_by = NULL WHERE tag = 'service.performance-class.title'").run();
|
||||||
|
db1.close();
|
||||||
|
|
||||||
|
const db2 = createDb(path);
|
||||||
|
const val = (tag: string) => (db2.prepare('SELECT value FROM content_blocks WHERE tag = ?').get(tag) as { value: string }).value;
|
||||||
|
expect(val('service.performance-class.title')).toBe('Performance Class');
|
||||||
|
db2.close();
|
||||||
|
|
||||||
|
// Non deve toccare un valore modificato dal pannello.
|
||||||
|
const db3 = createDb(path);
|
||||||
|
db3.prepare("UPDATE content_blocks SET value = 'Titolo mio', updated_by = 'admin' WHERE tag = 'service.performance-class.title'").run();
|
||||||
|
db3.close();
|
||||||
|
const db4 = createDb(path);
|
||||||
|
expect((db4.prepare("SELECT value FROM content_blocks WHERE tag = 'service.performance-class.title'").get() as { value: string }).value).toBe('Titolo mio');
|
||||||
|
db4.close();
|
||||||
|
} finally {
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('migra un DB esistente senza colonna role', () => {
|
it('migra un DB esistente senza colonna role', () => {
|
||||||
// simula DB vecchio: createDb due volte sullo stesso path in-memory non è possibile,
|
// simula DB vecchio: createDb due volte sullo stesso path in-memory non è possibile,
|
||||||
// quindi si verifica che la migrazione sia idempotente (doppia esecuzione non lancia)
|
// quindi si verifica che la migrazione sia idempotente (doppia esecuzione non lancia)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ describe('style presets', () => {
|
|||||||
|
|
||||||
it('rifiuta gruppo sconosciuto e valore fuori lista', () => {
|
it('rifiuta gruppo sconosciuto e valore fuori lista', () => {
|
||||||
expect(validateStyles({ font: 'comic' }).ok).toBe(false);
|
expect(validateStyles({ font: 'comic' }).ok).toBe(false);
|
||||||
expect(validateStyles({ size: 'xxl' }).ok).toBe(false);
|
expect(validateStyles({ size: 'xxxl' }).ok).toBe(false);
|
||||||
expect(validateStyles('non-oggetto').ok).toBe(false);
|
expect(validateStyles('non-oggetto').ok).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user