@jjlmoya/utils-babies 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +69 -0
- package/src/category/i18n/en.ts +48 -0
- package/src/category/i18n/es.ts +48 -0
- package/src/category/i18n/fr.ts +48 -0
- package/src/category/index.ts +24 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +30 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +19 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +146 -0
- package/src/pages/[locale].astro +251 -0
- package/src/pages/index.astro +4 -0
- package/src/tests/faq_count.test.ts +19 -0
- package/src/tests/locale_completeness.test.ts +42 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_h1_in_components.test.ts +48 -0
- package/src/tests/seo_length.test.ts +23 -0
- package/src/tests/tool_validation.test.ts +17 -0
- package/src/tool/baby-feeding-calculator/bibliography.astro +7 -0
- package/src/tool/baby-feeding-calculator/component.astro +210 -0
- package/src/tool/baby-feeding-calculator/i18n/en.ts +162 -0
- package/src/tool/baby-feeding-calculator/i18n/es.ts +162 -0
- package/src/tool/baby-feeding-calculator/i18n/fr.ts +162 -0
- package/src/tool/baby-feeding-calculator/index.ts +47 -0
- package/src/tool/baby-feeding-calculator/logic.ts +85 -0
- package/src/tool/baby-feeding-calculator/seo.astro +58 -0
- package/src/tool/baby-feeding-calculator/style.css +329 -0
- package/src/tool/baby-percentile-calculator/bibliography.astro +7 -0
- package/src/tool/baby-percentile-calculator/component.astro +388 -0
- package/src/tool/baby-percentile-calculator/i18n/en.ts +244 -0
- package/src/tool/baby-percentile-calculator/i18n/es.ts +244 -0
- package/src/tool/baby-percentile-calculator/i18n/fr.ts +244 -0
- package/src/tool/baby-percentile-calculator/index.ts +54 -0
- package/src/tool/baby-percentile-calculator/lmsData.ts +80 -0
- package/src/tool/baby-percentile-calculator/logic.ts +85 -0
- package/src/tool/baby-percentile-calculator/seo.astro +36 -0
- package/src/tool/baby-percentile-calculator/style.css +393 -0
- package/src/tool/baby-size-converter/bibliography.astro +7 -0
- package/src/tool/baby-size-converter/component.astro +289 -0
- package/src/tool/baby-size-converter/data.json +11 -0
- package/src/tool/baby-size-converter/i18n/en.ts +203 -0
- package/src/tool/baby-size-converter/i18n/es.ts +203 -0
- package/src/tool/baby-size-converter/i18n/fr.ts +203 -0
- package/src/tool/baby-size-converter/index.ts +53 -0
- package/src/tool/baby-size-converter/logic.ts +44 -0
- package/src/tool/baby-size-converter/seo.astro +36 -0
- package/src/tool/baby-size-converter/style.css +394 -0
- package/src/tool/fertile-days-estimator/bibliography.astro +7 -0
- package/src/tool/fertile-days-estimator/component.astro +265 -0
- package/src/tool/fertile-days-estimator/i18n/en.ts +258 -0
- package/src/tool/fertile-days-estimator/i18n/es.ts +262 -0
- package/src/tool/fertile-days-estimator/i18n/fr.ts +258 -0
- package/src/tool/fertile-days-estimator/index.ts +47 -0
- package/src/tool/fertile-days-estimator/logic.ts +58 -0
- package/src/tool/fertile-days-estimator/seo.astro +36 -0
- package/src/tool/fertile-days-estimator/style.css +419 -0
- package/src/tool/pregnancy-calculator/bibliography.astro +7 -0
- package/src/tool/pregnancy-calculator/calculator.ts +41 -0
- package/src/tool/pregnancy-calculator/component.astro +432 -0
- package/src/tool/pregnancy-calculator/i18n/en.ts +315 -0
- package/src/tool/pregnancy-calculator/i18n/es.ts +319 -0
- package/src/tool/pregnancy-calculator/i18n/fr.ts +315 -0
- package/src/tool/pregnancy-calculator/index.ts +55 -0
- package/src/tool/pregnancy-calculator/milestones.ts +153 -0
- package/src/tool/pregnancy-calculator/seo.astro +36 -0
- package/src/tool/pregnancy-calculator/store.ts +60 -0
- package/src/tool/pregnancy-calculator/style.css +807 -0
- package/src/tool/vaccination-calendar/bibliography.astro +7 -0
- package/src/tool/vaccination-calendar/component.astro +286 -0
- package/src/tool/vaccination-calendar/i18n/en.ts +170 -0
- package/src/tool/vaccination-calendar/i18n/es.ts +174 -0
- package/src/tool/vaccination-calendar/i18n/fr.ts +170 -0
- package/src/tool/vaccination-calendar/index.ts +47 -0
- package/src/tool/vaccination-calendar/logic.ts +59 -0
- package/src/tool/vaccination-calendar/seo.astro +36 -0
- package/src/tool/vaccination-calendar/style.css +316 -0
- package/src/tool/vaccination-calendar/vaccinationData.ts +21 -0
- package/src/tools.ts +17 -0
- package/src/types.ts +72 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { VaccinationCalendarLocaleContent } from '../index';
|
|
2
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
3
|
+
|
|
4
|
+
const slug = 'calendrier-vaccination-bebe-espagne';
|
|
5
|
+
const title = 'Calendrier de Vaccination en Espagne';
|
|
6
|
+
const description = 'Calculez les dates exactes de vaccination de votre bébé selon le calendrier AEP 2026.';
|
|
7
|
+
const faq = [
|
|
8
|
+
{
|
|
9
|
+
question: "Qu'est-ce que le calendrier de vaccination AEP 2026 ?",
|
|
10
|
+
answer: "C'est le calendrier de vaccinations recommandé par l'Association Espagnole de Pédiatrie pour 2026. Il comprend tous les vaccins systématiques et optionnels pour les bébés et les enfants de la naissance à 14 ans.",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
question: "La vaccination de mon bébé est-elle obligatoire ?",
|
|
14
|
+
answer: "En Espagne, la vaccination n'est pas légalement obligatoire, mais elle est fortement recommandée. Les vaccins du calendrier systématique sont gratuits et administrés dans les centres de santé publics.",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
question: "Que se passe-t-il si nous manquons une dose ?",
|
|
18
|
+
answer: "Si une dose est retardée, il n'est pas nécessaire de recommencer depuis le début. Votre pédiatre vous indiquera comment reprendre le calendrier là où il a été interrompu. L'essentiel est de compléter le schéma le plus tôt possible.",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
question: "Peut-on administrer plusieurs vaccins le même jour ?",
|
|
22
|
+
answer: "Oui, il est courant et sans danger d'administrer plusieurs vaccins lors de la même visite. Les vaccins combinés comme l'hexavalent protègent déjà contre six maladies en une seule injection.",
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
const howTo = [
|
|
26
|
+
{
|
|
27
|
+
name: "Saisissez la date de naissance",
|
|
28
|
+
text: "Entrez le jour, le mois et l'année de naissance de votre bébé dans les champs correspondants.",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "Consultez le prochain rendez-vous",
|
|
32
|
+
text: "La calculatrice vous indique automatiquement quand est la prochaine vaccination et quels vaccins sont prévus.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "Consultez le calendrier complet",
|
|
36
|
+
text: "Développez les sections des rendez-vous passés et futurs pour voir l'intégralité du calendrier de vaccination.",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "Exportez les rappels",
|
|
40
|
+
text: "Appuyez sur le bouton pour télécharger un fichier .ics avec tous les rendez-vous futurs et les ajouter au calendrier de votre téléphone.",
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
45
|
+
'@context': 'https://schema.org',
|
|
46
|
+
'@type': 'FAQPage',
|
|
47
|
+
mainEntity: faq.map((item) => ({
|
|
48
|
+
'@type': 'Question',
|
|
49
|
+
name: item.question,
|
|
50
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
51
|
+
})),
|
|
52
|
+
};
|
|
53
|
+
const howToSchema: WithContext<HowTo> = {
|
|
54
|
+
'@context': 'https://schema.org',
|
|
55
|
+
'@type': 'HowTo',
|
|
56
|
+
name: title,
|
|
57
|
+
description,
|
|
58
|
+
step: howTo.map((step) => ({
|
|
59
|
+
'@type': 'HowToStep',
|
|
60
|
+
name: step.name,
|
|
61
|
+
text: step.text,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
65
|
+
'@context': 'https://schema.org',
|
|
66
|
+
'@type': 'SoftwareApplication',
|
|
67
|
+
name: title,
|
|
68
|
+
description,
|
|
69
|
+
applicationCategory: 'UtilitiesApplication',
|
|
70
|
+
operatingSystem: 'Web',
|
|
71
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
72
|
+
inLanguage: 'fr',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const content: VaccinationCalendarLocaleContent = {
|
|
76
|
+
slug,
|
|
77
|
+
title,
|
|
78
|
+
description,
|
|
79
|
+
ui: {
|
|
80
|
+
labelBirthDate: "Date de naissance du bébé",
|
|
81
|
+
placeholderDD: "JJ",
|
|
82
|
+
placeholderMM: "MM",
|
|
83
|
+
placeholderAAAA: "AAAA",
|
|
84
|
+
emptyMsg: "Saisissez le jour, le mois et l'année de naissance de votre bébé pour afficher le calendrier de vaccination.",
|
|
85
|
+
invalidMsg: "La date saisie n'est pas valide.",
|
|
86
|
+
futureMsg: "Un bébé ne peut pas être né dans le futur.",
|
|
87
|
+
labelNextAppointment: "Prochain rendez-vous calculé",
|
|
88
|
+
btnAddReminder: "Ajouter un rappel sur le téléphone",
|
|
89
|
+
btnToday: "C'est aujourd'hui ! N'oubliez pas le carnet",
|
|
90
|
+
labelPassed: "Rendez-vous passés",
|
|
91
|
+
labelFuture: "Calendrier futur",
|
|
92
|
+
labelStatusOk: "OK",
|
|
93
|
+
labelStatusPending: "EN ATT.",
|
|
94
|
+
labelSource: "Source : AEP 2026",
|
|
95
|
+
labelShare: "Partager ces dates",
|
|
96
|
+
faqTitle: "Questions fréquentes",
|
|
97
|
+
bibliographyTitle: "Références",
|
|
98
|
+
},
|
|
99
|
+
seo: [
|
|
100
|
+
{ type: 'title', text: "Calculateur de vaccins : quand est le prochain rendez-vous de mon enfant ?", level: 2 },
|
|
101
|
+
{ type: 'stats', columns: 4, items: [
|
|
102
|
+
{ value: '+95 %', label: 'Efficacité' },
|
|
103
|
+
{ value: '14', label: 'Doses totales' },
|
|
104
|
+
{ value: 'Public', label: 'Coût AEP' },
|
|
105
|
+
{ value: 'Élevée', label: 'Sécurité' },
|
|
106
|
+
]},
|
|
107
|
+
{ type: 'tip', html: "Le calendrier AEP 2026 comprend d'importantes nouveautés, notamment l'extension du vaccin contre le méningocoque B et la mise à jour des recommandations pour le HPV chez les deux sexes dès 12 ans." },
|
|
108
|
+
{ type: 'title', text: 'Nouveautés du calendrier AEP 2026', level: 3 },
|
|
109
|
+
{ type: 'list', items: [
|
|
110
|
+
"Méningocoque B (Bexsero) : schéma 2+1 consolidé avec des doses à 2, 4 et 12 mois.",
|
|
111
|
+
"HPV étendu à tous les adolescents à partir de 12 ans, quel que soit le sexe.",
|
|
112
|
+
"Pneumocoque : recommandation mise à jour avec VPC15 ou VPC20 selon la disponibilité régionale.",
|
|
113
|
+
"Rotavirus : vaccin oral inclus dans le calendrier systématique dans toutes les communautés.",
|
|
114
|
+
"Tdca : rappel recommandé à 6 et 12 ans pour maintenir l'immunité contre la coqueluche.",
|
|
115
|
+
]},
|
|
116
|
+
{ type: 'title', text: "Différences entre les communautés autonomes d'Espagne", level: 3 },
|
|
117
|
+
{ type: 'list', items: [
|
|
118
|
+
"Certaines communautés incluent des vaccins supplémentaires non prévus dans le calendrier national.",
|
|
119
|
+
"Le financement du rotavirus varie selon la communauté, bien que la tendance soit à la couverture universelle.",
|
|
120
|
+
"Le méningocoque ACWY peut être administré à des âges légèrement différents selon le protocole régional.",
|
|
121
|
+
"Consultez toujours votre pédiatre ou le centre de santé local pour confirmer le calendrier en vigueur.",
|
|
122
|
+
]},
|
|
123
|
+
{ type: 'title', text: 'Comment exporter le calendrier sur votre téléphone', level: 3 },
|
|
124
|
+
{ type: 'list', items: [
|
|
125
|
+
"Saisissez la date de naissance de votre bébé dans le calculateur.",
|
|
126
|
+
'Appuyez sur le bouton "Ajouter un rappel sur le téléphone" pour télécharger le fichier .ics.',
|
|
127
|
+
"Ouvrez le fichier avec votre application de calendrier (Google Calendar, Apple Calendar, etc.).",
|
|
128
|
+
"Tous les événements de vaccination seront enregistrés avec la date et les vaccins correspondants.",
|
|
129
|
+
]},
|
|
130
|
+
{ type: 'title', text: 'Effets secondaires habituels', level: 3 },
|
|
131
|
+
{ type: 'list', items: [
|
|
132
|
+
"Rougeur ou gonflement au site d'injection : disparaît en 1 à 2 jours.",
|
|
133
|
+
"Fièvre légère (37,5–38,5 °C) : normale dans les premières 24 à 48 heures.",
|
|
134
|
+
"Irritabilité ou pleurs : fréquents chez les bébés après les premières doses.",
|
|
135
|
+
"Somnolence ou perte d'appétit transitoire : aucun traitement nécessaire.",
|
|
136
|
+
"Les réactions graves comme l'anaphylaxie sont extrêmement rares (moins d'1 par million de doses).",
|
|
137
|
+
]},
|
|
138
|
+
{ type: 'tip', html: "Amener le bébé bien nourri et habillé confortablement facilite la visite. Après le vaccin, le contact peau à peau ou l'allaitement maternel aide à calmer la douleur et l'inflammation naturellement." },
|
|
139
|
+
{ type: 'summary', title: "Ce qu'il faut retenir", items: [
|
|
140
|
+
"Le calendrier AEP 2026 comprend 14 doses jusqu'à 12 ans pour une protection complète.",
|
|
141
|
+
"Les vaccins de la première année protègent contre jusqu'à 9 maladies graves simultanément.",
|
|
142
|
+
"Les effets secondaires légers sont normaux et disparaissent en 1 à 2 jours.",
|
|
143
|
+
"Vous pouvez exporter tous les rendez-vous de vaccination vers le calendrier de votre téléphone en un seul clic.",
|
|
144
|
+
"Consultez toujours votre pédiatre si vous avez des questions sur le calendrier de votre région.",
|
|
145
|
+
]},
|
|
146
|
+
],
|
|
147
|
+
faqTitle: "Questions fréquentes",
|
|
148
|
+
faq,
|
|
149
|
+
bibliographyTitle: "Références",
|
|
150
|
+
bibliography: [
|
|
151
|
+
{
|
|
152
|
+
name: "Association Espagnole de Pédiatrie - Calendrier de Vaccination 2026",
|
|
153
|
+
url: "https://www.aeped.es/comite-vacunas/calendario-vacunaciones",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "Ministère de la Santé Espagne - Calendrier de Vaccination",
|
|
157
|
+
url: "https://www.sanidad.gob.es/areas/promocionPrevencion/vacunaciones/calendario/home.htm",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "OMS - Vaccination",
|
|
161
|
+
url: "https://www.who.int/fr/health-topics/vaccines-and-immunization",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: "CDC - Calendrier de vaccination recommandé pour les enfants et les adolescents",
|
|
165
|
+
url: "https://www.cdc.gov/vaccines/schedules/hcp/imz/child-adolescent.html",
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
howTo,
|
|
169
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
170
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { BabiesToolEntry, ToolDefinition, ToolLocaleContent } from '../../types';
|
|
2
|
+
import VaccinationCalendarComponent from './component.astro';
|
|
3
|
+
import VaccinationCalendarSEO from './seo.astro';
|
|
4
|
+
import VaccinationCalendarBibliography from './bibliography.astro';
|
|
5
|
+
|
|
6
|
+
export interface VaccinationCalendarUI {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
labelBirthDate: string;
|
|
9
|
+
placeholderDD: string;
|
|
10
|
+
placeholderMM: string;
|
|
11
|
+
placeholderAAAA: string;
|
|
12
|
+
emptyMsg: string;
|
|
13
|
+
invalidMsg: string;
|
|
14
|
+
futureMsg: string;
|
|
15
|
+
labelNextAppointment: string;
|
|
16
|
+
btnAddReminder: string;
|
|
17
|
+
btnToday: string;
|
|
18
|
+
labelPassed: string;
|
|
19
|
+
labelFuture: string;
|
|
20
|
+
labelStatusOk: string;
|
|
21
|
+
labelStatusPending: string;
|
|
22
|
+
labelSource: string;
|
|
23
|
+
labelShare: string;
|
|
24
|
+
faqTitle: string;
|
|
25
|
+
bibliographyTitle: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type VaccinationCalendarLocaleContent = ToolLocaleContent<VaccinationCalendarUI>;
|
|
29
|
+
|
|
30
|
+
export const vaccinationCalendar: BabiesToolEntry<VaccinationCalendarUI> = {
|
|
31
|
+
id: 'vaccination-calendar',
|
|
32
|
+
icons: { bg: 'mdi:needle', fg: 'mdi:needle' },
|
|
33
|
+
i18n: {
|
|
34
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
35
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
36
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export { VaccinationCalendarComponent, VaccinationCalendarSEO, VaccinationCalendarBibliography };
|
|
41
|
+
|
|
42
|
+
export const VACCINATION_CALENDAR_TOOL: ToolDefinition = {
|
|
43
|
+
entry: vaccinationCalendar,
|
|
44
|
+
Component: VaccinationCalendarComponent,
|
|
45
|
+
SEOComponent: VaccinationCalendarSEO,
|
|
46
|
+
BibliographyComponent: VaccinationCalendarBibliography,
|
|
47
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { VACCINES } from './vaccinationData';
|
|
2
|
+
|
|
3
|
+
export interface DoseGroup {
|
|
4
|
+
months: number;
|
|
5
|
+
vaccines: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function buildDoseGroups(): DoseGroup[] {
|
|
9
|
+
const groups: Record<number, string[]> = {};
|
|
10
|
+
VACCINES.forEach((vac) => {
|
|
11
|
+
vac.doses.forEach((d) => {
|
|
12
|
+
if (!groups[d]) groups[d] = [];
|
|
13
|
+
groups[d].push(vac.name);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
return Object.entries(groups)
|
|
17
|
+
.map(([months, vaccines]) => ({ months: Number(months), vaccines }))
|
|
18
|
+
.sort((a, b) => a.months - b.months);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getAgeLabel(months: number): string {
|
|
22
|
+
if (months < 12) return `${months} meses`;
|
|
23
|
+
if (months === 12) return '12 meses (1 año)';
|
|
24
|
+
const yrs = Math.floor(months / 12);
|
|
25
|
+
const rem = months % 12;
|
|
26
|
+
if (rem === 0) return `${yrs} ${yrs === 1 ? 'año' : 'años'}`;
|
|
27
|
+
return `${months} meses`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function calculateAge(birthDate: Date, today: Date): string {
|
|
31
|
+
let years = today.getFullYear() - birthDate.getFullYear();
|
|
32
|
+
let months = today.getMonth() - birthDate.getMonth();
|
|
33
|
+
let days = today.getDate() - birthDate.getDate();
|
|
34
|
+
if (days < 0) {
|
|
35
|
+
months -= 1;
|
|
36
|
+
const lastMonth = new Date(today.getFullYear(), today.getMonth(), 0);
|
|
37
|
+
days += lastMonth.getDate();
|
|
38
|
+
}
|
|
39
|
+
if (months < 0) {
|
|
40
|
+
years -= 1;
|
|
41
|
+
months += 12;
|
|
42
|
+
}
|
|
43
|
+
if (years === 0) return `${months} meses y ${days} días`;
|
|
44
|
+
return `${years} años, ${months} meses y ${days} días`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function buildIcsContent(birthDate: Date, doseGroups: DoseGroup[]): string {
|
|
48
|
+
let ics = 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//jjlmoya//VaxCal//ES\n';
|
|
49
|
+
doseGroups.forEach(({ months, vaccines }) => {
|
|
50
|
+
const target = new Date(birthDate);
|
|
51
|
+
target.setMonth(target.getMonth() + months);
|
|
52
|
+
if (target <= new Date()) return;
|
|
53
|
+
const dateStr = target.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
|
|
54
|
+
const label = months >= 12 ? `${Math.floor(months / 12)} años` : `${months} meses`;
|
|
55
|
+
ics += `BEGIN:VEVENT\nDTSTART:${dateStr}\nSUMMARY:Vacunación ${label}: ${vaccines.join(', ')}\nDESCRIPTION:Cita de vacunación infantil.\nEND:VEVENT\n`;
|
|
56
|
+
});
|
|
57
|
+
ics += 'END:VCALENDAR';
|
|
58
|
+
return ics;
|
|
59
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
SEOTitle, SEOStats, SEOTip, SEOTable,
|
|
4
|
+
SEOList, SEOSummary, SEOComparative, SEOGlossary, SEOArticle,
|
|
5
|
+
} from '@jjlmoya/utils-shared';
|
|
6
|
+
import { vaccinationCalendar } from './index';
|
|
7
|
+
import type { KnownLocale } from '../../types';
|
|
8
|
+
|
|
9
|
+
interface Props { locale?: KnownLocale; }
|
|
10
|
+
const { locale = 'es' } = Astro.props;
|
|
11
|
+
const content = await vaccinationCalendar.i18n[locale]?.();
|
|
12
|
+
if (!content) return null;
|
|
13
|
+
const { seo } = content;
|
|
14
|
+
---
|
|
15
|
+
<SEOArticle>
|
|
16
|
+
{seo.map((section: any) => {
|
|
17
|
+
switch (section.type) {
|
|
18
|
+
case 'summary': return <SEOSummary title={section.title} items={section.items} />;
|
|
19
|
+
case 'title': return <SEOTitle title={section.text} level={section.level || 2} />;
|
|
20
|
+
case 'paragraph': return <p set:html={section.html} />;
|
|
21
|
+
case 'stats': return <SEOStats stats={section.items} columns={section.columns} />;
|
|
22
|
+
case 'tip': return <SEOTip><Fragment set:html={section.html} /></SEOTip>;
|
|
23
|
+
case 'table': return (
|
|
24
|
+
<SEOTable headers={section.headers}>
|
|
25
|
+
{section.rows.map((row: string[]) => (
|
|
26
|
+
<tr>{row.map((cell: string) => <td set:html={cell} />)}</tr>
|
|
27
|
+
))}
|
|
28
|
+
</SEOTable>
|
|
29
|
+
);
|
|
30
|
+
case 'list': return <SEOList items={section.items} />;
|
|
31
|
+
case 'comparative': return <SEOComparative items={section.items} columns={section.columns} />;
|
|
32
|
+
case 'glossary': return <SEOGlossary items={section.items} />;
|
|
33
|
+
default: return null;
|
|
34
|
+
}
|
|
35
|
+
})}
|
|
36
|
+
</SEOArticle>
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
.vaccination-calendar {
|
|
2
|
+
--vc-bg: #fff;
|
|
3
|
+
--vc-bg-muted: #eef2ff;
|
|
4
|
+
--vc-text: #1e293b;
|
|
5
|
+
--vc-text-muted: #64748b;
|
|
6
|
+
--vc-border: #e2e8f0;
|
|
7
|
+
--vc-border-inner: rgba(255, 255, 255, 0.1);
|
|
8
|
+
--vc-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
9
|
+
--vc-primary: #4f46e5;
|
|
10
|
+
--vc-primary-on: #fff;
|
|
11
|
+
--vc-primary-soft: #eef2ff;
|
|
12
|
+
--vc-success: #10b981;
|
|
13
|
+
--vc-warning: #f59e0b;
|
|
14
|
+
--vc-error: #ef4444;
|
|
15
|
+
--vc-error-bg: #fef2f2;
|
|
16
|
+
|
|
17
|
+
width: 100%;
|
|
18
|
+
color: var(--vc-text);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.theme-dark .vaccination-calendar {
|
|
22
|
+
--vc-bg: #1e293b;
|
|
23
|
+
--vc-bg-muted: rgba(79, 70, 229, 0.1);
|
|
24
|
+
--vc-text: #f1f5f9;
|
|
25
|
+
--vc-text-muted: #94a3b8;
|
|
26
|
+
--vc-border: rgba(255, 255, 255, 0.1);
|
|
27
|
+
--vc-border-inner: rgba(255, 255, 255, 0.1);
|
|
28
|
+
--vc-shadow: none;
|
|
29
|
+
--vc-primary: #818cf8;
|
|
30
|
+
--vc-primary-on: #fff;
|
|
31
|
+
--vc-primary-soft: rgba(79, 70, 229, 0.1);
|
|
32
|
+
--vc-success: #10b981;
|
|
33
|
+
--vc-warning: #f59e0b;
|
|
34
|
+
--vc-error: #ef4444;
|
|
35
|
+
--vc-error-bg: rgba(239, 68, 68, 0.05);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.vaccination-calendar-header {
|
|
39
|
+
background: var(--vc-bg);
|
|
40
|
+
border: 1px solid var(--vc-border);
|
|
41
|
+
border-radius: 1rem;
|
|
42
|
+
box-shadow: var(--vc-shadow);
|
|
43
|
+
padding: 1.5rem;
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
gap: 1rem;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.vaccination-calendar-field {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
gap: 0.5rem;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.vaccination-calendar-field-label {
|
|
56
|
+
font-size: 0.875rem;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
color: var(--vc-text-muted);
|
|
59
|
+
text-transform: uppercase;
|
|
60
|
+
letter-spacing: 0.05em;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.vaccination-calendar-triple-input {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 0.25rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.vaccination-calendar-segment {
|
|
70
|
+
width: 3rem;
|
|
71
|
+
padding: 0.625rem 0.5rem;
|
|
72
|
+
text-align: center;
|
|
73
|
+
border: 1px solid var(--vc-border);
|
|
74
|
+
border-radius: 0.5rem;
|
|
75
|
+
background: var(--vc-bg);
|
|
76
|
+
color: var(--vc-text);
|
|
77
|
+
font-size: 1rem;
|
|
78
|
+
outline: none;
|
|
79
|
+
transition: border-color 0.2s;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.vaccination-calendar-segment-year {
|
|
83
|
+
width: 5rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.vaccination-calendar-segment:focus {
|
|
87
|
+
border-color: var(--vc-primary);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.vaccination-calendar-sep {
|
|
91
|
+
color: var(--vc-text-muted);
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
padding: 0 0.125rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.vaccination-calendar-error {
|
|
97
|
+
font-size: 0.8125rem;
|
|
98
|
+
color: var(--vc-error);
|
|
99
|
+
background: var(--vc-error-bg);
|
|
100
|
+
border-radius: 0.375rem;
|
|
101
|
+
padding: 0.375rem 0.625rem;
|
|
102
|
+
min-height: 1.5rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.vaccination-calendar-error:empty {
|
|
106
|
+
display: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.vaccination-calendar-age-badge {
|
|
110
|
+
font-size: 0.875rem;
|
|
111
|
+
font-weight: 600;
|
|
112
|
+
color: var(--vc-primary);
|
|
113
|
+
background: var(--vc-primary-soft);
|
|
114
|
+
border-radius: 2rem;
|
|
115
|
+
padding: 0.375rem 0.875rem;
|
|
116
|
+
align-self: flex-start;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.vaccination-calendar-age-badge:empty {
|
|
120
|
+
display: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.vaccination-calendar-empty {
|
|
124
|
+
margin-top: 1.5rem;
|
|
125
|
+
padding: 2rem;
|
|
126
|
+
text-align: center;
|
|
127
|
+
color: var(--vc-text-muted);
|
|
128
|
+
font-size: 0.9375rem;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.vaccination-calendar-context {
|
|
132
|
+
margin-top: 1.5rem;
|
|
133
|
+
background: var(--vc-bg);
|
|
134
|
+
border: 1px solid var(--vc-border);
|
|
135
|
+
border-radius: 1rem;
|
|
136
|
+
box-shadow: var(--vc-shadow);
|
|
137
|
+
padding: 1.5rem;
|
|
138
|
+
display: flex;
|
|
139
|
+
flex-direction: column;
|
|
140
|
+
gap: 1rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.vaccination-calendar-next-title {
|
|
144
|
+
font-size: 0.75rem;
|
|
145
|
+
font-weight: 700;
|
|
146
|
+
text-transform: uppercase;
|
|
147
|
+
letter-spacing: 0.1em;
|
|
148
|
+
color: var(--vc-text-muted);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.vaccination-calendar-next-date {
|
|
152
|
+
font-size: 1.375rem;
|
|
153
|
+
font-weight: 700;
|
|
154
|
+
color: var(--vc-primary);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.vaccination-calendar-is-today .vaccination-calendar-next-date {
|
|
158
|
+
color: var(--vc-warning);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.vaccination-calendar-vac-list {
|
|
162
|
+
list-style: none;
|
|
163
|
+
padding: 0;
|
|
164
|
+
margin: 0;
|
|
165
|
+
display: flex;
|
|
166
|
+
flex-direction: column;
|
|
167
|
+
gap: 0.5rem;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.vaccination-calendar-vac-item {
|
|
171
|
+
display: flex;
|
|
172
|
+
align-items: center;
|
|
173
|
+
gap: 0.5rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.vaccination-calendar-vac-name {
|
|
177
|
+
font-size: 0.9375rem;
|
|
178
|
+
font-weight: 500;
|
|
179
|
+
color: var(--vc-text);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.vaccination-calendar-actions {
|
|
183
|
+
display: flex;
|
|
184
|
+
flex-wrap: wrap;
|
|
185
|
+
gap: 0.75rem;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.vaccination-calendar-btn-primary {
|
|
189
|
+
background: var(--vc-primary);
|
|
190
|
+
color: var(--vc-primary-on);
|
|
191
|
+
border: none;
|
|
192
|
+
border-radius: 0.5rem;
|
|
193
|
+
padding: 0.625rem 1.25rem;
|
|
194
|
+
font-size: 0.875rem;
|
|
195
|
+
font-weight: 600;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
transition: opacity 0.15s;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.vaccination-calendar-btn-primary:hover {
|
|
201
|
+
opacity: 0.88;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.vaccination-calendar-btn-share {
|
|
205
|
+
background: var(--vc-bg-muted);
|
|
206
|
+
color: var(--vc-primary);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.vaccination-calendar-footer {
|
|
210
|
+
font-size: 0.75rem;
|
|
211
|
+
color: var(--vc-text-muted);
|
|
212
|
+
border-top: 1px solid var(--vc-border);
|
|
213
|
+
padding-top: 0.75rem;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.vaccination-calendar-sections {
|
|
217
|
+
margin-top: 1.5rem;
|
|
218
|
+
display: flex;
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
gap: 0.75rem;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.vaccination-calendar-accordion-item {
|
|
224
|
+
background: var(--vc-bg);
|
|
225
|
+
border: 1px solid var(--vc-border);
|
|
226
|
+
border-radius: 0.875rem;
|
|
227
|
+
overflow: hidden;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.vaccination-calendar-accordion-trigger {
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
justify-content: space-between;
|
|
234
|
+
padding: 1rem 1.25rem;
|
|
235
|
+
font-size: 0.9375rem;
|
|
236
|
+
font-weight: 600;
|
|
237
|
+
color: var(--vc-text);
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
list-style: none;
|
|
240
|
+
user-select: none;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.vaccination-calendar-accordion-trigger::-webkit-details-marker {
|
|
244
|
+
display: none;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.vaccination-calendar-accordion-content {
|
|
248
|
+
padding: 0.25rem 1.25rem 1.25rem;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.vaccination-calendar-timeline {
|
|
252
|
+
display: flex;
|
|
253
|
+
flex-direction: column;
|
|
254
|
+
gap: 0.5rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.vaccination-calendar .vaccination-calendar-timeline-row {
|
|
258
|
+
display: grid;
|
|
259
|
+
grid-template-columns: 8rem 1fr auto;
|
|
260
|
+
align-items: center;
|
|
261
|
+
gap: 0.75rem;
|
|
262
|
+
padding: 0.625rem 0;
|
|
263
|
+
border-bottom: 1px solid var(--vc-border);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.vaccination-calendar .vaccination-calendar-timeline-row:last-child {
|
|
267
|
+
border-bottom: none;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.vaccination-calendar-timeline-age {
|
|
271
|
+
font-size: 0.8125rem;
|
|
272
|
+
font-weight: 600;
|
|
273
|
+
color: var(--vc-text-muted);
|
|
274
|
+
white-space: nowrap;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.vaccination-calendar-timeline-vac {
|
|
278
|
+
display: flex;
|
|
279
|
+
flex-wrap: wrap;
|
|
280
|
+
gap: 0.375rem;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.vaccination-calendar .vaccination-calendar-vac-pill {
|
|
284
|
+
display: inline-block;
|
|
285
|
+
background: var(--vc-primary-soft);
|
|
286
|
+
color: var(--vc-primary);
|
|
287
|
+
border-radius: 2rem;
|
|
288
|
+
padding: 0.2rem 0.625rem;
|
|
289
|
+
font-size: 0.75rem;
|
|
290
|
+
font-weight: 500;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.vaccination-calendar-timeline-status {
|
|
294
|
+
font-size: 0.6875rem;
|
|
295
|
+
font-weight: 700;
|
|
296
|
+
border-radius: 0.25rem;
|
|
297
|
+
padding: 0.2rem 0.5rem;
|
|
298
|
+
white-space: nowrap;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.vaccination-calendar-check {
|
|
302
|
+
background: rgba(16, 185, 129, 0.1);
|
|
303
|
+
color: var(--vc-success);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.vaccination-calendar-clock {
|
|
307
|
+
background: rgba(245, 158, 11, 0.1);
|
|
308
|
+
color: var(--vc-warning);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
@media (max-width: 600px) {
|
|
312
|
+
.vaccination-calendar .vaccination-calendar-timeline-row {
|
|
313
|
+
grid-template-columns: 6rem 1fr auto;
|
|
314
|
+
gap: 0.5rem;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface Vaccine {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
fullDescription: string;
|
|
6
|
+
doses: number[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const VACCINES: Vaccine[] = [
|
|
10
|
+
{ id: 'hexavalente', name: 'Hexavalente', description: 'Difteria, Tétanos, Tosferina, Polio, Hib y Hepatitis B.', fullDescription: 'Protege contra seis enfermedades graves en una sola inyección: Difteria, Tétanos, Tosferina (tos ferina), Poliomielitis, Haemophilus influenzae tipo b y Hepatitis B.', doses: [2, 4, 11] },
|
|
11
|
+
{ id: 'neumococo', name: 'Neumococo (VCN15/20)', description: 'Protección contra neumonía y meningitis neumocócica.', fullDescription: 'La vacuna neumocócica conjugada protege contra cepas de la bacteria Streptococcus pneumoniae, causantes de neumonía, otitis y meningitis.', doses: [2, 4, 11] },
|
|
12
|
+
{ id: 'meningococo_b', name: 'Meningococo B (Bexsero)', description: 'Protección contra la meningitis por serogrupo B.', fullDescription: 'Protege contra la enfermedad meningocócica invasiva causada por Neisseria meningitidis del serogrupo B, una causa principal de meningitis y sepsis en niños.', doses: [2, 4, 12] },
|
|
13
|
+
{ id: 'rotavirus', name: 'Rotavirus', description: 'Protección contra gastroenteritis aguda por rotavirus.', fullDescription: 'Vacuna oral que previene la diarrea grave y vómitos causados por el rotavirus, muy contagioso en lactantes y niños pequeños.', doses: [2, 3, 4] },
|
|
14
|
+
{ id: 'meningococo_acwy', name: 'Meningococo ACWY', description: 'Protección contra meningitis por serogueropos A, C, W e Y.', fullDescription: 'Refuerza la protección contra cuatro variedades de meningitis meningocócica. Se administra en el primer año y en la adolescencia.', doses: [4, 12, 144] },
|
|
15
|
+
{ id: 'triple_virica', name: 'Triple Vírica (SRP)', description: 'Sarampión, Rubeola y Parotiditis.', fullDescription: 'Protege contra tres virus comunes: Sarampión (fiebre y erupción), Rubeola (afecta al feto en embarazadas) y Parotiditis (paperas).', doses: [12, 48] },
|
|
16
|
+
{ id: 'varicela', name: 'Varicela', description: 'Protección contra el virus de la varicela-zóster.', fullDescription: 'Previene la varicela y sus complicaciones graves. Se administran dos dosis para una inmunidad duradera.', doses: [15, 48] },
|
|
17
|
+
{ id: 'gripe', name: 'Gripe (Estacional)', description: 'Vacunación estacional contra la gripe.', fullDescription: 'Se recomienda anualmente entre los 6 y 59 meses. El calendario calcula una ventana teórica, contacta con tu pediatra en otoño.', doses: [6, 18, 30, 42, 54] },
|
|
18
|
+
{ id: 'vph', name: 'VPH (Papiloma)', description: 'Prevención del cáncer de cuello de útero y otros.', fullDescription: 'Protege contra el Virus del Papiloma Humano, previniendo diversos tipos de cáncer y verrugas genitales en ambos sexos.', doses: [144] },
|
|
19
|
+
{ id: 'tdpa', name: 'Tdpa (Tétanos, Difteria, Tosferina)', description: 'Refuerzo de adolescente.', fullDescription: 'Refuerzo inmunológico crucial para mantener la protección contra estas tres enfermedades durante la adolescencia y edad adulta.', doses: [72, 144] },
|
|
20
|
+
{ id: 'polio_booster', name: 'Polio (Refuerzo)', description: 'Cuarta dosis de refuerzo.', fullDescription: 'Dosis final para asegurar protección permanente de por vida contra la poliomielitis.', doses: [72] },
|
|
21
|
+
];
|
package/src/tools.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ToolDefinition } from './types';
|
|
2
|
+
import { BABY_FEEDING_CALCULATOR_TOOL } from './tool/baby-feeding-calculator/index';
|
|
3
|
+
import { BABY_SIZE_CONVERTER_TOOL } from './tool/baby-size-converter/index';
|
|
4
|
+
import { VACCINATION_CALENDAR_TOOL } from './tool/vaccination-calendar/index';
|
|
5
|
+
import { FERTILE_DAYS_ESTIMATOR_TOOL } from './tool/fertile-days-estimator/index';
|
|
6
|
+
import { BABY_PERCENTILE_CALCULATOR_TOOL } from './tool/baby-percentile-calculator/index';
|
|
7
|
+
import { PREGNANCY_CALCULATOR_TOOL } from './tool/pregnancy-calculator/index';
|
|
8
|
+
|
|
9
|
+
export const ALL_TOOLS: ToolDefinition[] = [
|
|
10
|
+
BABY_FEEDING_CALCULATOR_TOOL,
|
|
11
|
+
BABY_SIZE_CONVERTER_TOOL,
|
|
12
|
+
VACCINATION_CALENDAR_TOOL,
|
|
13
|
+
FERTILE_DAYS_ESTIMATOR_TOOL,
|
|
14
|
+
BABY_PERCENTILE_CALCULATOR_TOOL,
|
|
15
|
+
PREGNANCY_CALCULATOR_TOOL,
|
|
16
|
+
];
|
|
17
|
+
|