@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,181 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+ import type { PetRationUI } from './index';
4
+ import './style.css';
5
+
6
+ interface Props {
7
+ ui: PetRationUI;
8
+ }
9
+
10
+ const { ui } = Astro.props;
11
+ ---
12
+
13
+ <div class="pet-ration-calculator-final" id="calcWrapper" data-ui={JSON.stringify(ui)}>
14
+ <div class="calc-loader" id="calcLoader">
15
+ <Icon name="mdi:loading" class="loader-spinner" />
16
+ </div>
17
+
18
+ <div class="calc-container">
19
+ <div class="calc-section">
20
+ <h3 class="section-title">{ui.sectionProfile}</h3>
21
+
22
+ <div class="input-item">
23
+ <label class="item-label">{ui.labelAnimal}</label>
24
+ <div class="species-selector-v2">
25
+ <input type="radio" name="petType" id="pet-dog" value="dog" checked />
26
+ <label for="pet-dog" class="btn-species">
27
+ <Icon name="mdi:dog" class="icon-v2" /> {ui.speciesDog}
28
+ </label>
29
+ <input type="radio" name="petType" id="pet-cat" value="cat" />
30
+ <label for="pet-cat" class="btn-species">
31
+ <Icon name="mdi:cat" class="icon-v2" /> {ui.speciesCat}
32
+ </label>
33
+ <input type="radio" name="petType" id="pet-rabbit" value="rabbit" />
34
+ <label for="pet-rabbit" class="btn-species">
35
+ <Icon name="mdi:rabbit" class="icon-v2" /> {ui.speciesRabbit}
36
+ </label>
37
+ </div>
38
+ </div>
39
+
40
+ <div class="input-item">
41
+ <label class="item-label">{ui.labelStage}</label>
42
+ <div class="segmented-v2">
43
+ <input type="radio" name="lifeStage" id="stage-puppy" value="puppy" />
44
+ <label for="stage-puppy">{ui.stagePuppy}</label>
45
+ <input type="radio" name="lifeStage" id="stage-adult" value="adult" checked />
46
+ <label for="stage-adult">{ui.stageAdult}</label>
47
+ <input type="radio" name="lifeStage" id="stage-senior" value="senior" />
48
+ <label for="stage-senior">{ui.stageSenior}</label>
49
+ </div>
50
+ </div>
51
+
52
+ <div class="input-item">
53
+ <div class="label-row-v2">
54
+ <label class="item-label">{ui.labelWeight}</label>
55
+ <div class="val-badge"><span id="weightDisplay">10</span> kg</div>
56
+ </div>
57
+ <input type="range" id="weightRange" min="1" max="85" step="0.5" value="10" class="range-v2" />
58
+ </div>
59
+
60
+ <div class="input-item">
61
+ <label class="item-label">{ui.labelActivity}</label>
62
+ <div class="segmented-v2 activity-box">
63
+ <input type="radio" name="activity" id="act-low" value="low" />
64
+ <label for="act-low">{ui.activityLow}</label>
65
+ <input type="radio" name="activity" id="act-mod" value="mod" checked />
66
+ <label for="act-mod">{ui.activityMod}</label>
67
+ <input type="radio" name="activity" id="act-high" value="high" />
68
+ <label for="act-high">{ui.activityHigh}</label>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="calc-section">
74
+ <h3 class="section-title">{ui.sectionDiet}</h3>
75
+
76
+ <div class="input-item">
77
+ <label class="item-label">{ui.labelDietType}</label>
78
+ <div class="ratio-presets">
79
+ <input type="radio" name="dietRatioPreset" id="r-dry" value="0" checked />
80
+ <label for="r-dry">{ui.dietDry}</label>
81
+ <input type="radio" name="dietRatioPreset" id="r-mixed" value="25" />
82
+ <label for="r-mixed">{ui.dietMixed25}</label>
83
+ <input type="radio" name="dietRatioPreset" id="r-half" value="50" />
84
+ <label for="r-half">{ui.dietHalf}</label>
85
+ <input type="radio" name="dietRatioPreset" id="r-wet" value="100" />
86
+ <label for="r-wet">{ui.dietWet}</label>
87
+ </div>
88
+ <div class="custom-ratio-link" id="showManualSlider">{ui.manualAdjust}</div>
89
+ <div class="manual-slider-box" id="manualSliderBox" style="display: none;">
90
+ <input type="range" id="dietRatioRange" min="0" max="100" step="5" value="0" class="range-v2 ratio-slider" />
91
+ <div class="ratio-labels">
92
+ <span>{ui.wetPercentLabel}</span>
93
+ <strong id="ratioValDisplay">0%</strong>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ <div class="results-v2">
100
+ <div class="res-cards-v2">
101
+ <div class="res-card-v2 dry" id="dryResCard">
102
+ <span class="res-tag">{ui.tagDry}</span>
103
+ <span class="res-num"><span id="dryResVal">0</span><small>g</small></span>
104
+ </div>
105
+ <div class="res-card-v2 wet" id="wetResCard" style="display: none;">
106
+ <span class="res-tag">{ui.tagWet}</span>
107
+ <span class="res-num"><span id="wetResVal">0</span><small>g</small></span>
108
+ </div>
109
+ </div>
110
+ <div class="res-footer-v2">
111
+ <div class="total-info">
112
+ <Icon name="mdi:chart-donut" class="footer-icon" />
113
+ {ui.totalKcal}: <strong id="kcalTotalVal">0</strong> {ui.kcalUnit}
114
+ </div>
115
+ <div class="status-box-v2" id="statusBoxV2">{ui.statusDefault}</div>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+
121
+ <script>
122
+ import { getFormState, setRadioValue, setMaxWeightRange, getStoredConfig, applyPresetLogic, updateDisplay } from "./handlers";
123
+
124
+ function setupRationListeners(calculate: () => void, weightRange: HTMLInputElement, ratioRange: HTMLInputElement) {
125
+ document.getElementById("calcWrapper")?.addEventListener("input", calculate);
126
+ document.getElementById("calcWrapper")?.addEventListener("change", calculate);
127
+
128
+ const manualBox = document.getElementById("manualSliderBox");
129
+ const showManual = document.getElementById("showManualSlider");
130
+ showManual?.addEventListener("click", () => {
131
+ if (manualBox) manualBox.style.display = "block";
132
+ if (showManual) showManual.style.display = "none";
133
+ });
134
+
135
+ const presets = document.getElementsByName("dietRatioPreset") as NodeListOf<HTMLInputElement>;
136
+ presets.forEach((preset) => {
137
+ preset.addEventListener("change", () => {
138
+ ratioRange.value = preset.value;
139
+ calculate();
140
+ });
141
+ });
142
+
143
+ document.getElementsByName("petType").forEach((radio) => {
144
+ radio.addEventListener("change", () => {
145
+ setMaxWeightRange((radio as HTMLInputElement).value);
146
+ calculate();
147
+ });
148
+ });
149
+ }
150
+
151
+ function initPetRation() {
152
+ const wrapper = document.getElementById("calcWrapper");
153
+ const weightRange = document.getElementById("weightRange") as HTMLInputElement;
154
+ const ratioRange = document.getElementById("dietRatioRange") as HTMLInputElement;
155
+ if (!wrapper || !weightRange || !ratioRange) return;
156
+
157
+ const uiData = JSON.parse(wrapper.dataset.ui || "{}");
158
+ const config = getStoredConfig();
159
+
160
+ setRadioValue("petType", config.s);
161
+ setRadioValue("lifeStage", config.st);
162
+ setRadioValue("activity", config.a);
163
+ weightRange.value = config.w;
164
+ ratioRange.value = config.r;
165
+ setMaxWeightRange(config.s);
166
+ applyPresetLogic(config.r);
167
+
168
+ const calculate = () => updateDisplay(getFormState(), uiData);
169
+ setupRationListeners(calculate, weightRange, ratioRange);
170
+
171
+ calculate();
172
+ document.getElementById("calcLoader")?.classList.add("hidden");
173
+ }
174
+
175
+ if (document.readyState === "loading") {
176
+ document.addEventListener("DOMContentLoaded", initPetRation);
177
+ } else {
178
+ initPetRation();
179
+ }
180
+ document.addEventListener("astro:page-load", initPetRation);
181
+ </script>
@@ -0,0 +1,139 @@
1
+ import { calculateRER, calculateDER, calculateRation, getStatusKey, WEIGHT_RANGES } from "./logic";
2
+ import type { RationSpecies, LifeStage, ActivityLevel } from "./logic";
3
+
4
+ export interface RationState {
5
+ species: RationSpecies;
6
+ stage: LifeStage;
7
+ activity: ActivityLevel;
8
+ weight: number;
9
+ wetPct: number;
10
+ }
11
+
12
+ function getSelectedValue(name: string): string {
13
+ return (document.querySelector(`input[name="${name}"]:checked`) as HTMLInputElement)?.value || "";
14
+ }
15
+
16
+ function getInputValues() {
17
+ const weightRange = document.getElementById("weightRange") as HTMLInputElement;
18
+ const ratioRange = document.getElementById("dietRatioRange") as HTMLInputElement;
19
+ return {
20
+ weight: parseFloat(weightRange?.value || "0") || 0,
21
+ wetPct: parseInt(ratioRange?.value || "0") / 100,
22
+ };
23
+ }
24
+
25
+ export function getFormState(): RationState {
26
+ const { weight, wetPct } = getInputValues();
27
+ return {
28
+ species: (getSelectedValue("petType") || "dog") as RationSpecies,
29
+ stage: (getSelectedValue("lifeStage") || "adult") as LifeStage,
30
+ activity: (getSelectedValue("activity") || "mod") as ActivityLevel,
31
+ weight,
32
+ wetPct,
33
+ };
34
+ }
35
+
36
+ export function setRadioValue(name: string, value: string): void {
37
+ const el = document.querySelector(`input[name="${name}"][value="${value}"]`) as HTMLInputElement;
38
+ if (el) el.checked = true;
39
+ }
40
+
41
+ export function setMaxWeightRange(species: string): void {
42
+ const weightRange = document.getElementById("weightRange") as HTMLInputElement;
43
+ if (weightRange) {
44
+ weightRange.max = (WEIGHT_RANGES[species as RationSpecies] || 85).toString();
45
+ }
46
+ }
47
+
48
+ function getParamOrStored(param: string, storedKey: string, defaultVal: string) {
49
+ const params = new URLSearchParams(window.location.search);
50
+ const stored = JSON.parse(localStorage.getItem("pet_v2_state") || "{}");
51
+ return params.get(param) || stored[storedKey] || defaultVal;
52
+ }
53
+
54
+ export function getStoredConfig() {
55
+ const params = new URLSearchParams(window.location.search);
56
+ const stored = JSON.parse(localStorage.getItem("pet_v2_state") || "{}");
57
+ const defaultR = stored.wetPct ? (stored.wetPct * 100).toString() : "0";
58
+
59
+ return {
60
+ s: getParamOrStored("s", "species", "dog"),
61
+ st: getParamOrStored("st", "stage", "adult"),
62
+ a: getParamOrStored("a", "activity", "mod"),
63
+ w: getParamOrStored("w", "weight", "10"),
64
+ r: params.get("r") || defaultR,
65
+ };
66
+ }
67
+
68
+ export function applyPresetLogic(ratioValue: string): void {
69
+ const presets = document.getElementsByName("dietRatioPreset") as NodeListOf<HTMLInputElement>;
70
+ const manualBox = document.getElementById("manualSliderBox");
71
+ const showManual = document.getElementById("showManualSlider");
72
+
73
+ const preset = Array.from(presets).find((p) => p.value === ratioValue);
74
+ if (preset) {
75
+ preset.checked = true;
76
+ } else if (manualBox && ratioValue !== "0" && ratioValue !== "100") {
77
+ manualBox.style.display = "block";
78
+ if (showManual) showManual.style.display = "none";
79
+ }
80
+ }
81
+
82
+ interface CalcResult {
83
+ dryGrams: number;
84
+ wetGrams: number;
85
+ der: number;
86
+ }
87
+
88
+ function updateResultNumbers(state: RationState, result: CalcResult, uiData: Record<string, string>): void {
89
+ const weightDisplay = document.getElementById("weightDisplay");
90
+ if (weightDisplay) weightDisplay.innerText = state.weight.toString();
91
+
92
+ const ratioLabel = document.getElementById("ratioValDisplay");
93
+ if (ratioLabel) ratioLabel.innerText = `${Math.round(state.wetPct * 100)}%`;
94
+
95
+ const dryVal = document.getElementById("dryResVal");
96
+ if (dryVal) dryVal.innerText = result.dryGrams.toString();
97
+
98
+ const wetVal = document.getElementById("wetResVal");
99
+ if (wetVal) wetVal.innerText = result.wetGrams.toString();
100
+
101
+ const kcalTotal = document.getElementById("kcalTotalVal");
102
+ if (kcalTotal) kcalTotal.innerText = Math.round(result.der).toString();
103
+
104
+ const status = document.getElementById("statusBoxV2");
105
+ if (status) status.innerText = uiData[getStatusKey(state.species, state.stage, state.weight)] || "";
106
+ }
107
+
108
+ function updateCardVisibility(state: RationState): void {
109
+ const dryCard = document.getElementById("dryResCard");
110
+ if (dryCard) dryCard.style.display = (1 - state.wetPct) > 0 ? "flex" : "none";
111
+
112
+ const wetCard = document.getElementById("wetResCard");
113
+ if (wetCard) wetCard.style.display = state.wetPct > 0 ? "flex" : "none";
114
+ }
115
+
116
+ function updateStateAndUrl(state: RationState): void {
117
+ localStorage.setItem(
118
+ "pet_v2_state",
119
+ JSON.stringify({ species: state.species, stage: state.stage, activity: state.activity, weight: state.weight, wetPct: state.wetPct })
120
+ );
121
+
122
+ const url = new URL(window.location.href);
123
+ url.searchParams.set("w", state.weight.toString());
124
+ url.searchParams.set("s", state.species);
125
+ url.searchParams.set("st", state.stage);
126
+ url.searchParams.set("a", state.activity);
127
+ url.searchParams.set("r", document.getElementById("dietRatioRange")?.value || "0");
128
+ window.history.replaceState({}, "", url.toString());
129
+ }
130
+
131
+ export function updateDisplay(state: RationState, uiData: Record<string, string>): void {
132
+ const rer = calculateRER(state.weight);
133
+ const der = calculateDER(rer, state.species, state.stage, state.activity);
134
+ const { dryGrams, wetGrams } = calculateRation(der, state.wetPct);
135
+
136
+ updateResultNumbers(state, { dryGrams, wetGrams, der }, uiData);
137
+ updateCardVisibility(state);
138
+ updateStateAndUrl(state);
139
+ }
@@ -0,0 +1,161 @@
1
+ import type { WithContext, SoftwareApplication } from 'schema-dts';
2
+ import type { PetRationUI, PetRationLocaleContent } from '../index';
3
+
4
+ const slug = 'pet-daily-ration-calculator';
5
+ const title = 'Pet Daily Ration Calculator | Canine and Feline Nutrition';
6
+ const description = 'Calculate the exact amount of food your dog or cat needs. Tool based on veterinary RER and DER formulas for a healthy weight.';
7
+
8
+ const ui: PetRationUI = {
9
+ toolTitle: 'Daily Ration Calculator',
10
+ sectionProfile: '1. Pet Profile',
11
+ labelAnimal: 'Animal',
12
+ labelStage: 'Life Stage',
13
+ labelWeight: 'Body Weight',
14
+ labelActivity: 'Physical Activity',
15
+ sectionDiet: '2. Diet and Food',
16
+ labelDietType: 'Feeding Type',
17
+ speciesDog: 'Dog',
18
+ speciesCat: 'Cat',
19
+ speciesRabbit: 'Rabbit',
20
+ stagePuppy: 'Puppy',
21
+ stageAdult: 'Adult',
22
+ stageSenior: 'Senior',
23
+ activityLow: 'Low',
24
+ activityMod: 'Normal',
25
+ activityHigh: 'High',
26
+ dietDry: 'Dry Only',
27
+ dietMixed25: 'Mixed (25%)',
28
+ dietHalf: 'Half (50%)',
29
+ dietWet: 'Wet Only',
30
+ manualAdjust: 'Manual adjustment (1-99%)',
31
+ wetPercentLabel: '% wet food in diet',
32
+ tagDry: 'Dry',
33
+ tagWet: 'Wet',
34
+ totalKcal: 'Total',
35
+ kcalUnit: 'kcal/day',
36
+ statusPuppy: 'Rapid growth phase',
37
+ statusSenior: 'Mature metabolism',
38
+ statusLargeDog: 'Large breed: Joint care',
39
+ statusDefault: 'Balanced weight',
40
+ };
41
+
42
+ const faq: PetRationLocaleContent['faq'] = [
43
+ {
44
+ question: 'What is the RER and why is it important?',
45
+ answer: 'The RER (Resting Energy Requirement) is the minimum energy your pet needs to maintain its basic vital functions at rest. It is the scientific basis for calculating the daily ration, avoiding under or overfeeding.',
46
+ },
47
+ {
48
+ question: 'How does sterilisation affect my pet\'s ration?',
49
+ answer: 'Sterilised animals tend to have a basal metabolism about 15-20% lower. They may need a slightly smaller ration or an activity level adjusted to Low in the calculator to avoid weight gain.',
50
+ },
51
+ {
52
+ question: 'Can I use this calculator for puppies?',
53
+ answer: 'Growing puppies have much higher energy needs (up to 3 times the RER). This calculator is optimised for adult dogs and cats. For puppies, always consult your vet for a specific growth plan.',
54
+ },
55
+ {
56
+ question: 'How many times a day should I distribute the ration?',
57
+ answer: 'The ideal is to spread the total resulting quantity over 2 or 3 meals a day. This helps maintain stable glucose levels and improves digestion, especially in breeds prone to gastric torsion.',
58
+ },
59
+ ];
60
+
61
+ const howTo: PetRationLocaleContent['howTo'] = [
62
+ { name: 'Select the species', text: 'Indicate whether the calculation is for a dog or cat, as their metabolisms and weight limits vary.' },
63
+ { name: 'Adjust the weight', text: 'Move the slider to your pet\'s current weight in kilograms.' },
64
+ { name: 'Define their activity', text: 'Choose between low, normal or high activity based on the actual daily exercise your companion does.' },
65
+ { name: 'Enter the food calories', text: 'Look on the pet food packaging for the kcal/kg value so the gram calculation is 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: 'Principles of Veterinary Nutrition',
77
+ items: [
78
+ 'The RER formula is: 70 × (weight in kg)^0.75. It is the basis of all veterinary caloric calculation.',
79
+ 'DER multiplies RER by the animal\'s lifestyle factor.',
80
+ 'A neutered dog needs up to 20% fewer calories than an intact one.',
81
+ 'Obesity affects 50% of pets and significantly reduces their life expectancy.',
82
+ ],
83
+ },
84
+ { type: 'title', text: 'Complete Guide to Calculating Your Pet\'s Daily Ration', level: 2 },
85
+ {
86
+ type: 'paragraph',
87
+ html: 'Feeding a dog or cat correctly is not just a matter of filling their bowl each morning. Nutrition is the fundamental pillar of their health, longevity and emotional wellbeing. A <strong>daily ration calculator for pets</strong> is an essential tool for any responsible owner who wants to avoid the two most common problems in current veterinary nutrition: malnutrition from lack of nutrients and, especially, obesity, which affects more than 50% of pets in developed countries.',
88
+ },
89
+ {
90
+ type: 'paragraph',
91
+ html: 'In this guide we will explore the scientific foundations behind caloric calculations, how to interpret food labels and how factors such as age, physical activity level and physiological status influence the amount of food your companion needs each day.',
92
+ },
93
+ { type: 'title', text: 'The Science Behind the Calculations: RER and DER', level: 3 },
94
+ {
95
+ type: 'paragraph',
96
+ html: 'To determine how much an animal should eat, vets and nutritionists use two key concepts: the RER and the DER.',
97
+ },
98
+ {
99
+ type: 'stats',
100
+ items: [
101
+ { label: 'RER: Resting energy', value: '70 × kg^0.75' },
102
+ { label: 'Puppy factor', value: '×2.5 - ×3.5' },
103
+ { label: 'Active adult factor', value: '×1.6 - ×2.0' },
104
+ { label: 'Senior factor', value: '×1.0 - ×1.4' },
105
+ ],
106
+ },
107
+ { type: 'title', text: 'How to Read Your Pet Food Label', level: 3 },
108
+ {
109
+ type: 'paragraph',
110
+ html: 'For the calculator to give you exact grams, you need to know the <strong>caloric density</strong> of the product. This value is usually expressed as kcal/kg or kcal/100g.',
111
+ },
112
+ {
113
+ type: 'stats',
114
+ items: [
115
+ { label: 'Light kibble', value: '~3000 kcal/kg' },
116
+ { label: 'Adult kibble', value: '~3500 kcal/kg' },
117
+ { label: 'Puppy kibble', value: '~4000 kcal/kg' },
118
+ { label: 'Wet food', value: '~1000 kcal/kg' },
119
+ ],
120
+ },
121
+ {
122
+ type: 'tip',
123
+ title: 'Nutritional Note',
124
+ html: 'Every pet is an individual. The calculator offers a solid starting point, but you should always monitor body condition by feeling your pet\'s ribs. If you can feel them without pressing hard, the weight is correct.',
125
+ },
126
+ { type: 'title', text: 'Nutrition in Cats vs Dogs', level: 3 },
127
+ {
128
+ type: 'paragraph',
129
+ html: 'Cats are obligate carnivores and their protein metabolism is unique. In dogs, we must be careful with excess carbohydrates. Both benefit from a controlled ration that avoids excessive load on the joints and cardiovascular system.',
130
+ },
131
+ {
132
+ type: 'paragraph',
133
+ html: 'Spending a few minutes calculating the exact ration is one of the most effective forms of preventive medicine available to pet owners today. A healthy weight drastically reduces the risk of diabetes, heart problems, and joint disease. Your vet can confirm whether your animal\'s body condition is correct with a simple physical examination at any routine visit.',
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: 'Frequently Asked Questions',
151
+ bibliographyTitle: 'Bibliography',
152
+ slug,
153
+ title,
154
+ description,
155
+ ui,
156
+ seo,
157
+ faq,
158
+ bibliography,
159
+ howTo,
160
+ schemas,
161
+ };
@@ -0,0 +1,161 @@
1
+ import type { WithContext, SoftwareApplication } from 'schema-dts';
2
+ import type { PetRationUI, PetRationLocaleContent } from '../index';
3
+
4
+ const slug = 'calculadora-racion-diaria-mascotas';
5
+ const title = 'Calculadora de Ración Diaria para Mascotas | Nutrición Canina y Felina';
6
+ const description = 'Calcula la cantidad exacta de comida que necesita tu perro o gato. Herramienta basada en fórmulas veterinarias RER y DER para un peso saludable.';
7
+
8
+ const ui: PetRationUI = {
9
+ toolTitle: 'Calculadora de Ración Diaria',
10
+ sectionProfile: '1. Perfil de la Mascota',
11
+ labelAnimal: 'Animal',
12
+ labelStage: 'Etapa Vital',
13
+ labelWeight: 'Peso Corporal',
14
+ labelActivity: 'Actividad Física',
15
+ sectionDiet: '2. Dieta y Alimento',
16
+ labelDietType: 'Tipo de Alimentación',
17
+ speciesDog: 'Perro',
18
+ speciesCat: 'Gato',
19
+ speciesRabbit: 'Conejo',
20
+ stagePuppy: 'Cachorro',
21
+ stageAdult: 'Adulto',
22
+ stageSenior: 'Senior',
23
+ activityLow: 'Baja',
24
+ activityMod: 'Normal',
25
+ activityHigh: 'Alta',
26
+ dietDry: 'Solo Seco',
27
+ dietMixed25: 'Mixta (25%)',
28
+ dietHalf: 'Mitad (50%)',
29
+ dietWet: 'Solo Húmedo',
30
+ manualAdjust: 'Ajuste manual (1-99%)',
31
+ wetPercentLabel: '% de comida húmeda en la dieta',
32
+ tagDry: 'Seco',
33
+ tagWet: 'Húmedo',
34
+ totalKcal: 'Total',
35
+ kcalUnit: 'kcal/día',
36
+ statusPuppy: 'Fase de crecimiento rápido',
37
+ statusSenior: 'Metabolismo maduro',
38
+ statusLargeDog: 'Raza grande: Control articular',
39
+ statusDefault: 'Peso equilibrado',
40
+ };
41
+
42
+ const faq: PetRationLocaleContent['faq'] = [
43
+ {
44
+ question: '¿Qué es el RER y por qué es importante?',
45
+ answer: 'El RER (Resting Energy Requirement) es la energía mínima que tu mascota necesita para mantener sus funciones vitales básicas en reposo. Es la base científica para calcular la ración diaria evitando el infra o sobre-alimentación.',
46
+ },
47
+ {
48
+ question: '¿Cómo influye la esterilización en la ración de mi mascota?',
49
+ answer: 'Los animales esterilizados suelen tener un metabolismo basal un 15-20% más bajo. Por ello, es común que necesiten una ración ligeramente menor o un nivel de actividad ajustado a Baja en la calculadora para evitar el aumento de peso.',
50
+ },
51
+ {
52
+ question: '¿Puedo usar esta calculadora para cachorros?',
53
+ answer: 'Los cachorros en crecimiento tienen necesidades energéticas mucho más altas (hasta 3 veces el RER). Esta calculadora está optimizada para perros y gatos adultos. Para cachorros, consulta siempre con tu veterinario para un plan de crecimiento específico.',
54
+ },
55
+ {
56
+ question: '¿Cuántas veces al día debo repartir la ración?',
57
+ answer: 'Lo ideal es repartir la cantidad total resultante en 2 o 3 tomas al día. Esto ayuda a mantener niveles de glucosa estables y mejora la digestión, especialmente en razas propensas a la torsión gástrica.',
58
+ },
59
+ ];
60
+
61
+ const howTo: PetRationLocaleContent['howTo'] = [
62
+ { name: 'Selecciona la especie', text: 'Indica si el cálculo es para un perro o un gato, ya que sus metabolismos y límites de peso varían.' },
63
+ { name: 'Ajusta el peso', text: 'Mueve el deslizador hasta el peso actual de tu mascota en kilogramos.' },
64
+ { name: 'Define su actividad', text: 'Elige entre actividad baja, normal o alta basándote en el ejercicio diario real que realiza tu compañero.' },
65
+ { name: 'Introduce las calorías del alimento', text: 'Busca en el envase del pienso el valor de kcal/kg para que el cálculo en gramos sea exacto.' },
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: 'Principios de Nutrición Veterinaria',
77
+ items: [
78
+ 'La fórmula RER es: 70 × (peso en kg)^0.75. Es la base de todo cálculo calórico veterinario.',
79
+ 'El DER multiplica el RER por el factor de estilo de vida del animal.',
80
+ 'Un perro esterilizado necesita hasta un 20% menos de calorías que uno entero.',
81
+ 'La obesidad afecta al 50% de mascotas y reduce significativamente su esperanza de vida.',
82
+ ],
83
+ },
84
+ { type: 'title', text: 'Guía Completa para Calcular la Ración Diaria de tu Mascota', level: 2 },
85
+ {
86
+ type: 'paragraph',
87
+ html: 'Alimentar correctamente a un perro o un gato no es solo una cuestión de llenar su cuenco cada mañana. La nutrición es el pilar fundamental de su salud, longevidad y bienestar emocional. Una <strong>calculadora de ración diaria para mascotas</strong> es una herramienta esencial para cualquier propietario responsable que desee evitar los dos problemas más comunes en la nutrición veterinaria actual: la desnutrición por falta de nutrientes y, especialmente, la obesidad, que afecta a más del 50% de las mascotas en países desarrollados.',
88
+ },
89
+ {
90
+ type: 'paragraph',
91
+ html: 'En esta guía exploraremos los fundamentos científicos detrás de los cálculos calóricos, cómo interpretar las etiquetas de los alimentos y de qué manera factores como la edad, el nivel de actividad física y el estado fisiológico influyen en la cantidad de alimento que tu compañero necesita cada día.',
92
+ },
93
+ { type: 'title', text: 'La Ciencia Detrás de los Cálculos: RER y DER', level: 3 },
94
+ {
95
+ type: 'paragraph',
96
+ html: 'Para determinar cuánto debe comer un animal, los veterinarios y nutricionistas utilizan dos conceptos clave: el RER y el DER.',
97
+ },
98
+ {
99
+ type: 'stats',
100
+ items: [
101
+ { label: 'RER: Energía en reposo', value: '70 × kg^0.75' },
102
+ { label: 'Factor cachorro', value: '×2.5 - ×3.5' },
103
+ { label: 'Factor adulto activo', value: '×1.6 - ×2.0' },
104
+ { label: 'Factor senior', value: '×1.0 - ×1.4' },
105
+ ],
106
+ },
107
+ { type: 'title', text: 'Cómo Leer la Etiqueta de tu Pienso', level: 3 },
108
+ {
109
+ type: 'paragraph',
110
+ html: 'Para que la calculadora te dé los gramos exactos, necesitas conocer la <strong>densidad calórica</strong> del producto. Este valor suele expresarse como kcal/kg o kcal/100g.',
111
+ },
112
+ {
113
+ type: 'stats',
114
+ items: [
115
+ { label: 'Pienso Light', value: '~3000 kcal/kg' },
116
+ { label: 'Pienso Adulto', value: '~3500 kcal/kg' },
117
+ { label: 'Pienso Puppy', value: '~4000 kcal/kg' },
118
+ { label: 'Comida Húmeda', value: '~1000 kcal/kg' },
119
+ ],
120
+ },
121
+ {
122
+ type: 'tip',
123
+ title: 'Nota Nutricional',
124
+ html: 'Cada mascota es un individuo. La calculadora ofrece un punto de partida sólido, pero siempre debes monitorizar la condición corporal palpando las costillas de tu mascota. Si las notas sin tener que apretar, el peso es correcto.',
125
+ },
126
+ { type: 'title', text: 'Nutrición en Gatos vs Perros', level: 3 },
127
+ {
128
+ type: 'paragraph',
129
+ html: 'Los gatos son carnívoros obligados y su metabolismo proteico es único. En perros, debemos tener cuidado con los excesos de carbohidratos. Ambos se benefician de una ración controlada que evite la carga excesiva sobre las articulaciones y el sistema cardiovascular. Un peso saludable reduce el riesgo de diabetes y problemas cardíacos drásticamente.',
130
+ },
131
+ {
132
+ type: 'paragraph',
133
+ html: 'Invertir minutos en calcular la ración exacta es medicina preventiva. Un peso saludable reduce el riesgo de diabetes y problemas cardíacos drásticamente. Tu veterinario puede confirmar si la condición corporal de tu animal es la correcta con una simple exploración física.',
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
+ slug,
151
+ title,
152
+ description,
153
+ ui,
154
+ seo,
155
+ faqTitle: 'Preguntas Frecuentes',
156
+ faq,
157
+ bibliographyTitle: 'Referencias Bibliográficas',
158
+ bibliography,
159
+ howTo,
160
+ schemas,
161
+ };