@jjlmoya/utils-pets 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.
Files changed (41) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +48 -0
  3. package/src/category/i18n/es.ts +48 -0
  4. package/src/category/i18n/fr.ts +48 -0
  5. package/src/category/index.ts +13 -0
  6. package/src/category/seo.astro +14 -0
  7. package/src/components/PreviewNavSidebar.astro +115 -0
  8. package/src/components/PreviewToolbar.astro +142 -0
  9. package/src/data.ts +15 -0
  10. package/src/env.d.ts +4 -0
  11. package/src/index.ts +20 -0
  12. package/src/layouts/PreviewLayout.astro +116 -0
  13. package/src/pages/[locale]/[slug].astro +148 -0
  14. package/src/pages/[locale].astro +270 -0
  15. package/src/pages/index.astro +3 -0
  16. package/src/tests/faq_count.test.ts +23 -0
  17. package/src/tests/mocks/astro_mock.js +1 -0
  18. package/src/tests/seo_length.test.ts +54 -0
  19. package/src/tests/tool_validation.test.ts +139 -0
  20. package/src/tool/petAge/bibliography.astro +14 -0
  21. package/src/tool/petAge/component.astro +267 -0
  22. package/src/tool/petAge/handlers.ts +142 -0
  23. package/src/tool/petAge/i18n/en.ts +160 -0
  24. package/src/tool/petAge/i18n/es.ts +160 -0
  25. package/src/tool/petAge/i18n/fr.ts +160 -0
  26. package/src/tool/petAge/index.ts +64 -0
  27. package/src/tool/petAge/logic.ts +59 -0
  28. package/src/tool/petAge/seo.astro +61 -0
  29. package/src/tool/petAge/style.css +649 -0
  30. package/src/tool/petRation/bibliography.astro +14 -0
  31. package/src/tool/petRation/component.astro +181 -0
  32. package/src/tool/petRation/handlers.ts +139 -0
  33. package/src/tool/petRation/i18n/en.ts +161 -0
  34. package/src/tool/petRation/i18n/es.ts +161 -0
  35. package/src/tool/petRation/i18n/fr.ts +161 -0
  36. package/src/tool/petRation/index.ts +63 -0
  37. package/src/tool/petRation/logic.ts +44 -0
  38. package/src/tool/petRation/seo.astro +61 -0
  39. package/src/tool/petRation/style.css +427 -0
  40. package/src/tools.ts +13 -0
  41. package/src/types.ts +71 -0
@@ -0,0 +1,161 @@
1
+ import type { WithContext, SoftwareApplication } from 'schema-dts';
2
+ import type { PetRationUI, PetRationLocaleContent } from '../index';
3
+
4
+ const slug = 'calculatrice-ration-quotidienne-animaux';
5
+ const title = 'Calculatrice de Ration Quotidienne pour Animaux | Nutrition Canine et Féline';
6
+ const description = 'Calculez la quantité exacte de nourriture dont votre chien ou chat a besoin. Outil basé sur les formules vétérinaires RER et DER pour un poids sain.';
7
+
8
+ const ui: PetRationUI = {
9
+ toolTitle: 'Calculatrice de Ration Quotidienne',
10
+ sectionProfile: '1. Profil de l\'Animal',
11
+ labelAnimal: 'Animal',
12
+ labelStage: 'Stade de Vie',
13
+ labelWeight: 'Poids Corporel',
14
+ labelActivity: 'Activité Physique',
15
+ sectionDiet: '2. Régime et Aliment',
16
+ labelDietType: 'Type d\'Alimentation',
17
+ speciesDog: 'Chien',
18
+ speciesCat: 'Chat',
19
+ speciesRabbit: 'Lapin',
20
+ stagePuppy: 'Chiot',
21
+ stageAdult: 'Adulte',
22
+ stageSenior: 'Senior',
23
+ activityLow: 'Faible',
24
+ activityMod: 'Normale',
25
+ activityHigh: 'Élevée',
26
+ dietDry: 'Sec Uniquement',
27
+ dietMixed25: 'Mixte (25%)',
28
+ dietHalf: 'Moitié (50%)',
29
+ dietWet: 'Humide Uniquement',
30
+ manualAdjust: 'Ajustement manuel (1-99%)',
31
+ wetPercentLabel: '% de nourriture humide dans le régime',
32
+ tagDry: 'Sec',
33
+ tagWet: 'Humide',
34
+ totalKcal: 'Total',
35
+ kcalUnit: 'kcal/jour',
36
+ statusPuppy: 'Phase de croissance rapide',
37
+ statusSenior: 'Métabolisme mature',
38
+ statusLargeDog: 'Grande race : Contrôle articulaire',
39
+ statusDefault: 'Poids équilibré',
40
+ };
41
+
42
+ const faq: PetRationLocaleContent['faq'] = [
43
+ {
44
+ question: 'Qu\'est-ce que le RER et pourquoi est-il important ?',
45
+ answer: 'Le RER (Resting Energy Requirement) est l\'énergie minimale dont votre animal a besoin pour maintenir ses fonctions vitales de base au repos. C\'est la base scientifique pour calculer la ration quotidienne, en évitant la sous ou la suralimentation.',
46
+ },
47
+ {
48
+ question: 'Comment la stérilisation influence-t-elle la ration de mon animal ?',
49
+ answer: 'Les animaux stérilisés ont tendance à avoir un métabolisme basal environ 15-20% plus bas. Il peut leur falloir une ration légèrement réduite ou un niveau d\'activité ajusté à Faible dans la calculatrice pour éviter la prise de poids.',
50
+ },
51
+ {
52
+ question: 'Puis-je utiliser cette calculatrice pour les chiots ?',
53
+ answer: 'Les chiots en croissance ont des besoins énergétiques beaucoup plus élevés (jusqu\'à 3 fois le RER). Cette calculatrice est optimisée pour les chiens et chats adultes. Pour les chiots, consultez toujours votre vétérinaire pour un plan de croissance spécifique.',
54
+ },
55
+ {
56
+ question: 'Combien de fois par jour dois-je répartir la ration ?',
57
+ answer: 'L\'idéal est de répartir la quantité totale en 2 ou 3 repas par jour. Cela aide à maintenir des niveaux de glucose stables et améliore la digestion, notamment pour les races sujettes à la torsion gastrique.',
58
+ },
59
+ ];
60
+
61
+ const howTo: PetRationLocaleContent['howTo'] = [
62
+ { name: 'Sélectionnez l\'espèce', text: 'Indiquez si le calcul est pour un chien ou un chat, car leurs métabolismes et limites de poids varient.' },
63
+ { name: 'Ajustez le poids', text: 'Déplacez le curseur jusqu\'au poids actuel de votre animal en kilogrammes.' },
64
+ { name: 'Définissez son activité', text: 'Choisissez entre activité faible, normale ou élevée en vous basant sur l\'exercice quotidien réel de votre compagnon.' },
65
+ { name: 'Entrez les calories de l\'aliment', text: 'Cherchez sur l\'emballage de la croquette la valeur kcal/kg pour que le calcul en grammes soit exact.' },
66
+ ];
67
+
68
+ const bibliography: PetRationLocaleContent['bibliography'] = [
69
+ { name: 'WSAVA: Nutrition Toolkit and Calorie Recommendations', url: 'https://wsava.org/global-guidelines/global-nutrition-guidelines/' },
70
+ { name: 'FEDIAF: Nutritional Guidelines for Complete and Complementary Pet Food', url: 'https://fediaf.org/self-regulation/nutrition.html' },
71
+ ];
72
+
73
+ const seo: PetRationLocaleContent['seo'] = [
74
+ {
75
+ type: 'summary',
76
+ title: 'Principes de Nutrition Vétérinaire',
77
+ items: [
78
+ 'La formule RER est : 70 × (poids en kg)^0.75. C\'est la base de tout calcul calorique vétérinaire.',
79
+ 'Le DER multiplie le RER par le facteur de style de vie de l\'animal.',
80
+ 'Un chien stérilisé a besoin de jusqu\'à 20% de calories en moins qu\'un entier.',
81
+ 'L\'obésité touche 50% des animaux et réduit significativement leur espérance de vie.',
82
+ ],
83
+ },
84
+ { type: 'title', text: 'Guide Complet pour Calculer la Ration Quotidienne de votre Animal', level: 2 },
85
+ {
86
+ type: 'paragraph',
87
+ html: 'Nourrir correctement un chien ou un chat n\'est pas seulement une question de remplir sa gamelle chaque matin. La nutrition est le pilier fondamental de sa santé, longévité et bien-être émotionnel. Une <strong>calculatrice de ration quotidienne pour animaux</strong> est un outil essentiel pour tout propriétaire responsable souhaitant éviter les deux problèmes les plus courants en nutrition vétérinaire : la malnutrition par manque de nutriments et, surtout, l\'obésité, qui touche plus de 50% des animaux dans les pays développés.',
88
+ },
89
+ {
90
+ type: 'paragraph',
91
+ html: 'Dans ce guide, nous explorerons les fondements scientifiques des calculs caloriques, comment interpréter les étiquettes des aliments et de quelle manière des facteurs comme l\'âge, le niveau d\'activité physique et l\'état physiologique influencent la quantité d\'aliment dont votre compagnon a besoin chaque jour.',
92
+ },
93
+ { type: 'title', text: 'La Science derrière les Calculs : RER et DER', level: 3 },
94
+ {
95
+ type: 'paragraph',
96
+ html: 'Pour déterminer combien doit manger un animal, les vétérinaires et nutritionnistes utilisent deux concepts clés : le RER et le DER.',
97
+ },
98
+ {
99
+ type: 'stats',
100
+ items: [
101
+ { label: 'RER : Énergie de repos', value: '70 × kg^0.75' },
102
+ { label: 'Facteur chiot', value: '×2.5 - ×3.5' },
103
+ { label: 'Facteur adulte actif', value: '×1.6 - ×2.0' },
104
+ { label: 'Facteur senior', value: '×1.0 - ×1.4' },
105
+ ],
106
+ },
107
+ { type: 'title', text: 'Comment Lire l\'Étiquette de votre Croquette', level: 3 },
108
+ {
109
+ type: 'paragraph',
110
+ html: 'Pour que la calculatrice vous donne les grammes exacts, vous devez connaître la <strong>densité calorique</strong> du produit. Cette valeur est généralement exprimée en kcal/kg ou kcal/100g.',
111
+ },
112
+ {
113
+ type: 'stats',
114
+ items: [
115
+ { label: 'Croquettes Light', value: '~3000 kcal/kg' },
116
+ { label: 'Croquettes Adulte', value: '~3500 kcal/kg' },
117
+ { label: 'Croquettes Chiot', value: '~4000 kcal/kg' },
118
+ { label: 'Nourriture Humide', value: '~1000 kcal/kg' },
119
+ ],
120
+ },
121
+ {
122
+ type: 'tip',
123
+ title: 'Note Nutritionnelle',
124
+ html: 'Chaque animal est un individu. La calculatrice offre un bon point de départ, mais vous devez toujours surveiller la condition corporelle en palpant les côtes de votre animal. Si vous les sentez sans appuyer, le poids est correct.',
125
+ },
126
+ { type: 'title', text: 'Nutrition chez les Chats vs les Chiens', level: 3 },
127
+ {
128
+ type: 'paragraph',
129
+ html: 'Les chats sont des carnivores obligatoires et leur métabolisme protéique est unique. Chez les chiens, il faut faire attention aux excès de glucides. Les deux bénéficient d\'une ration contrôlée qui évite la surcharge sur les articulations et le système cardiovasculaire. Un poids sain réduit drastiquement le risque de diabète et de problèmes cardiaques.',
130
+ },
131
+ {
132
+ type: 'paragraph',
133
+ html: 'Passer quelques minutes à calculer la ration exacte, c\'est de la médecine préventive. Votre vétérinaire peut confirmer si la condition corporelle de votre animal est correcte avec un simple examen physique.',
134
+ },
135
+ ];
136
+
137
+ const schemas: PetRationLocaleContent['schemas'] = [
138
+ {
139
+ '@context': 'https://schema.org',
140
+ '@type': 'SoftwareApplication',
141
+ name: title,
142
+ description: description,
143
+ applicationCategory: 'UtilityApplication',
144
+ operatingSystem: 'Web',
145
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
146
+ } as WithContext<SoftwareApplication>,
147
+ ];
148
+
149
+ export const content: PetRationLocaleContent = {
150
+ faqTitle: 'Questions Fréquentes',
151
+ bibliographyTitle: 'Références Bibliographiques',
152
+ slug,
153
+ title,
154
+ description,
155
+ ui,
156
+ seo,
157
+ faq,
158
+ bibliography,
159
+ howTo,
160
+ schemas,
161
+ };
@@ -0,0 +1,63 @@
1
+ import type { PetToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
2
+ import PetRationCalculator from './component.astro';
3
+ import PetRationSEO from './seo.astro';
4
+ import PetRationBibliography from './bibliography.astro';
5
+
6
+ export interface PetRationUI {
7
+ [key: string]: string;
8
+ toolTitle: string;
9
+ sectionProfile: string;
10
+ labelAnimal: string;
11
+ labelStage: string;
12
+ labelWeight: string;
13
+ labelActivity: string;
14
+ sectionDiet: string;
15
+ labelDietType: string;
16
+ speciesDog: string;
17
+ speciesCat: string;
18
+ speciesRabbit: string;
19
+ stagePuppy: string;
20
+ stageAdult: string;
21
+ stageSenior: string;
22
+ activityLow: string;
23
+ activityMod: string;
24
+ activityHigh: string;
25
+ dietDry: string;
26
+ dietMixed25: string;
27
+ dietHalf: string;
28
+ dietWet: string;
29
+ manualAdjust: string;
30
+ wetPercentLabel: string;
31
+ tagDry: string;
32
+ tagWet: string;
33
+ totalKcal: string;
34
+ kcalUnit: string;
35
+ statusPuppy: string;
36
+ statusSenior: string;
37
+ statusLargeDog: string;
38
+ statusDefault: string;
39
+ }
40
+
41
+ export type PetRationLocaleContent = ToolLocaleContent<PetRationUI>;
42
+
43
+ export const petRation: PetToolEntry<PetRationUI> = {
44
+ id: 'pet-ration',
45
+ icons: {
46
+ bg: 'mdi:dog',
47
+ fg: 'mdi:scale',
48
+ },
49
+ i18n: {
50
+ es: () => import('./i18n/es').then((m) => m.content),
51
+ en: () => import('./i18n/en').then((m) => m.content),
52
+ fr: () => import('./i18n/fr').then((m) => m.content),
53
+ },
54
+ };
55
+
56
+ export { PetRationCalculator, PetRationSEO, PetRationBibliography };
57
+
58
+ export const PET_RATION_TOOL: ToolDefinition = {
59
+ entry: petRation,
60
+ Component: PetRationCalculator,
61
+ SEOComponent: PetRationSEO,
62
+ BibliographyComponent: PetRationBibliography,
63
+ };
@@ -0,0 +1,44 @@
1
+ export type RationSpecies = 'dog' | 'cat' | 'rabbit';
2
+ export type LifeStage = 'puppy' | 'adult' | 'senior';
3
+ export type ActivityLevel = 'low' | 'mod' | 'high';
4
+
5
+ type MultiplierMap = Record<RationSpecies, Record<LifeStage, Record<ActivityLevel, number>>>;
6
+
7
+ export const MULTIPLIERS: MultiplierMap = {
8
+ dog: { puppy: { low: 2.5, mod: 3.0, high: 3.5 }, adult: { low: 1.2, mod: 1.6, high: 2.0 }, senior: { low: 1.0, mod: 1.2, high: 1.4 } },
9
+ cat: { puppy: { low: 2.0, mod: 2.5, high: 3.0 }, adult: { low: 1.0, mod: 1.2, high: 1.4 }, senior: { low: 0.9, mod: 1.0, high: 1.1 } },
10
+ rabbit: { puppy: { low: 1.5, mod: 2.0, high: 2.5 }, adult: { low: 1.0, mod: 1.2, high: 1.4 }, senior: { low: 0.8, mod: 1.0, high: 1.2 } },
11
+ };
12
+
13
+ export const CALORIC_STANDARDS = { kDry: 3500, kWet: 1000 };
14
+
15
+ export const WEIGHT_RANGES: Record<RationSpecies, number> = {
16
+ dog: 85,
17
+ cat: 20,
18
+ rabbit: 10,
19
+ };
20
+
21
+ export function calculateRER(weightKg: number): number {
22
+ return 70 * Math.pow(weightKg, 0.75);
23
+ }
24
+
25
+ export function calculateDER(rer: number, species: RationSpecies, stage: LifeStage, activity: ActivityLevel): number {
26
+ return rer * (MULTIPLIERS[species][stage][activity] || 1.6);
27
+ }
28
+
29
+ export function calculateRation(der: number, wetPct: number): { dryGrams: number; wetGrams: number } {
30
+ const dryPct = 1 - wetPct;
31
+ return {
32
+ dryGrams: Math.round((der * dryPct / CALORIC_STANDARDS.kDry) * 1000),
33
+ wetGrams: Math.round((der * wetPct / CALORIC_STANDARDS.kWet) * 1000),
34
+ };
35
+ }
36
+
37
+ export type StatusKey = 'statusPuppy' | 'statusSenior' | 'statusLargeDog' | 'statusDefault';
38
+
39
+ export function getStatusKey(species: RationSpecies, stage: LifeStage, weightKg: number): StatusKey {
40
+ if (stage === 'puppy') return 'statusPuppy';
41
+ if (stage === 'senior') return 'statusSenior';
42
+ if (weightKg > 40 && species === 'dog') return 'statusLargeDog';
43
+ return 'statusDefault';
44
+ }
@@ -0,0 +1,61 @@
1
+ ---
2
+ import {
3
+ SEOTitle,
4
+ SEOCard,
5
+ SEOStats,
6
+ SEOTip,
7
+ SEOArticle,
8
+ SEOTable,
9
+ SEOProsCons,
10
+ SEOSummary,
11
+ SEOGlossary,
12
+ SEODiagnostic,
13
+ } from '@jjlmoya/utils-shared';
14
+ import { petRation } from './index';
15
+ import type { KnownLocale } from '../../types';
16
+
17
+ interface Props {
18
+ locale?: KnownLocale;
19
+ }
20
+
21
+ const { locale = 'es' } = Astro.props;
22
+ const content = await petRation.i18n[locale]?.();
23
+ if (!content) return null;
24
+
25
+ const { seo } = content;
26
+ ---
27
+
28
+ <SEOArticle>
29
+ {seo.map((section: any) => {
30
+ switch (section.type) {
31
+ case 'summary':
32
+ return <SEOSummary title={section.title} items={section.items} />;
33
+ case 'title':
34
+ return <SEOTitle title={section.text} level={section.level || 2} />;
35
+ case 'paragraph':
36
+ return <p set:html={section.html} />;
37
+ case 'stats':
38
+ return <SEOStats stats={section.items} columns={section.columns} />;
39
+ case 'card':
40
+ return <SEOCard title={section.title} icon={section.icon}><Fragment set:html={section.html} /></SEOCard>;
41
+ case 'tip':
42
+ return <SEOTip title={section.title}><Fragment set:html={section.html} /></SEOTip>;
43
+ case 'table':
44
+ return (
45
+ <SEOTable headers={section.headers}>
46
+ {section.rows.map((row: string[]) => (
47
+ <tr>{row.map((cell: string) => <td set:html={cell} />)}</tr>
48
+ ))}
49
+ </SEOTable>
50
+ );
51
+ case 'proscons':
52
+ return <SEOProsCons title={section.title} items={section.items} />;
53
+ case 'diagnostic':
54
+ return <SEODiagnostic title={section.title} icon={section.icon} type={section.variant} badge={section.badge}><Fragment set:html={section.html} /></SEODiagnostic>;
55
+ case 'glossary':
56
+ return <SEOGlossary items={section.items} />;
57
+ default:
58
+ return null;
59
+ }
60
+ })}
61
+ </SEOArticle>