@jjlmoya/utils-pets 1.26.0 → 1.28.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 +1 -1
- package/src/category/index.ts +2 -1
- package/src/data.ts +2 -0
- package/src/entries.ts +4 -1
- package/src/index.ts +1 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +12 -10
- package/src/tool/petAge/component.astro +1 -1
- package/src/tool/petMedicationSchedulePlanner/bibliography.astro +6 -0
- package/src/tool/petMedicationSchedulePlanner/bibliography.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/component.astro +40 -0
- package/src/tool/petMedicationSchedulePlanner/controller.ts +95 -0
- package/src/tool/petMedicationSchedulePlanner/dom-views.ts +57 -0
- package/src/tool/petMedicationSchedulePlanner/entry.ts +26 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/content.ts +58 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/de.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/en.ts +7 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/es.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/fr.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/id.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/it.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/ja.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/ko.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/nl.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/pl.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/pt.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/ru.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/sv.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/tr.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/i18n/zh.ts +6 -0
- package/src/tool/petMedicationSchedulePlanner/index.ts +12 -0
- package/src/tool/petMedicationSchedulePlanner/logic.test.ts +24 -0
- package/src/tool/petMedicationSchedulePlanner/logic.ts +118 -0
- package/src/tool/petMedicationSchedulePlanner/pet-medication-schedule-planner.css +368 -0
- package/src/tool/petMedicationSchedulePlanner/seo.astro +11 -0
- package/src/tool/petMedicationSchedulePlanner/storage.ts +32 -0
- package/src/tool/petMedicationSchedulePlanner/ui.ts +37 -0
- package/src/tools.ts +3 -0
package/package.json
CHANGED
package/src/category/index.ts
CHANGED
|
@@ -5,10 +5,11 @@ import { petGestation } from '../tool/petGestation/entry';
|
|
|
5
5
|
import { petToxicity } from '../tool/petToxicity/entry';
|
|
6
6
|
import { petWaterIntake } from '../tool/petWaterIntake/entry';
|
|
7
7
|
import { petCarrierCrateSizePlanner } from '../tool/petCarrierCrateSizePlanner/entry';
|
|
8
|
+
import { petMedicationSchedulePlanner } from '../tool/petMedicationSchedulePlanner/entry';
|
|
8
9
|
|
|
9
10
|
export const petsCategory: PetCategoryEntry = {
|
|
10
11
|
icon: 'mdi:paw',
|
|
11
|
-
tools: [petAge, petRation, petGestation, petToxicity, petWaterIntake, petCarrierCrateSizePlanner],
|
|
12
|
+
tools: [petAge, petRation, petGestation, petToxicity, petWaterIntake, petCarrierCrateSizePlanner, petMedicationSchedulePlanner],
|
|
12
13
|
i18n: {
|
|
13
14
|
en: () => import('./i18n/en').then((m) => m.content),
|
|
14
15
|
es: () => import('./i18n/es').then((m) => m.content),
|
package/src/data.ts
CHANGED
|
@@ -3,11 +3,13 @@ export { petAge } from './tool/petAge';
|
|
|
3
3
|
export { petRation } from './tool/petRation';
|
|
4
4
|
export { petGestation } from './tool/petGestation';
|
|
5
5
|
export { petToxicity } from './tool/petToxicity';
|
|
6
|
+
export { petMedicationSchedulePlanner } from './tool/petMedicationSchedulePlanner';
|
|
6
7
|
|
|
7
8
|
export type { PetAgeUI, PetAgeLocaleContent } from './tool/petAge';
|
|
8
9
|
export type { PetRationUI, PetRationLocaleContent } from './tool/petRation';
|
|
9
10
|
export type { PetGestationUI, PetGestationLocaleContent } from './tool/petGestation';
|
|
10
11
|
export type { PetToxicityUI, PetToxicityLocaleContent } from './tool/petToxicity';
|
|
12
|
+
export type { PetMedicationSchedulePlannerUI, PetMedicationSchedulePlannerLocaleContent } from './tool/petMedicationSchedulePlanner';
|
|
11
13
|
|
|
12
14
|
export type {
|
|
13
15
|
KnownLocale,
|
package/src/entries.ts
CHANGED
|
@@ -8,6 +8,8 @@ export { petWaterIntake } from './tool/petWaterIntake/entry';
|
|
|
8
8
|
export type { PetWaterIntakeUI, PetWaterIntakeLocaleContent } from './tool/petWaterIntake/entry';
|
|
9
9
|
export { petCarrierCrateSizePlanner } from './tool/petCarrierCrateSizePlanner/entry';
|
|
10
10
|
export type { PetCarrierCrateSizePlannerUI, PetCarrierCrateSizePlannerLocaleContent } from './tool/petCarrierCrateSizePlanner/entry';
|
|
11
|
+
export { petMedicationSchedulePlanner } from './tool/petMedicationSchedulePlanner/entry';
|
|
12
|
+
export type { PetMedicationSchedulePlannerUI, PetMedicationSchedulePlannerLocaleContent } from './tool/petMedicationSchedulePlanner/entry';
|
|
11
13
|
export { petsCategory } from './category';
|
|
12
14
|
import { petAge } from './tool/petAge/entry';
|
|
13
15
|
import { petRation } from './tool/petRation/entry';
|
|
@@ -15,4 +17,5 @@ import { petGestation } from './tool/petGestation/entry';
|
|
|
15
17
|
import { petToxicity } from './tool/petToxicity/entry';
|
|
16
18
|
import { petWaterIntake } from './tool/petWaterIntake/entry';
|
|
17
19
|
import { petCarrierCrateSizePlanner } from './tool/petCarrierCrateSizePlanner/entry';
|
|
18
|
-
|
|
20
|
+
import { petMedicationSchedulePlanner } from './tool/petMedicationSchedulePlanner/entry';
|
|
21
|
+
export const ALL_ENTRIES = [petAge, petRation, petGestation, petToxicity, petWaterIntake, petCarrierCrateSizePlanner, petMedicationSchedulePlanner];
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { petAge, PET_AGE_TOOL } from './tool/petAge';
|
|
|
2
2
|
export { petRation, PET_RATION_TOOL } from './tool/petRation';
|
|
3
3
|
export { petGestation, PET_GESTATION_TOOL } from './tool/petGestation';
|
|
4
4
|
export { petCarrierCrateSizePlanner, PET_CARRIER_CRATE_SIZE_PLANNER_TOOL } from './tool/petCarrierCrateSizePlanner';
|
|
5
|
+
export { petMedicationSchedulePlanner, PET_MEDICATION_SCHEDULE_PLANNER_TOOL } from './tool/petMedicationSchedulePlanner';
|
|
5
6
|
|
|
6
7
|
export { petsCategory } from './category';
|
|
7
8
|
export const PetsCategorySEO = () => import('./category/seo.astro').then((m) => m.default);
|
|
@@ -33,12 +33,14 @@ function extractSectionText(section: any): string {
|
|
|
33
33
|
return text;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function countWords(text: string): number {
|
|
37
|
-
|
|
38
|
-
.replace(/<[^>]*>/g, '')
|
|
39
|
-
.trim()
|
|
40
|
-
.split(/\s+/)
|
|
41
|
-
.filter((w) => w.length > 0).length;
|
|
36
|
+
function countWords(text: string): number {
|
|
37
|
+
const words = text
|
|
38
|
+
.replace(/<[^>]*>/g, '')
|
|
39
|
+
.trim()
|
|
40
|
+
.split(/\s+/)
|
|
41
|
+
.filter((w) => w.length > 0).length;
|
|
42
|
+
const cjkCharacters = (text.match(/[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uac00-\ud7af]/g) ?? []).length;
|
|
43
|
+
return Math.max(words, cjkCharacters);
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
describe('Tool Validation Suite', () => {
|
|
@@ -71,7 +73,7 @@ describe('Tool Validation Suite', () => {
|
|
|
71
73
|
expect(content.slug).toMatch(/^[a-z0-9]+(-[a-z0-9]+)*$/);
|
|
72
74
|
|
|
73
75
|
if (locale === 'es') {
|
|
74
|
-
const validSlugs = ['calculadora-edad-mascotas', 'calculadora-racion-diaria-mascotas', 'calculadora-gestacion-mascotas', 'buscador-alimentos-toxicos-perros-gatos', 'calculadora-agua-diaria-perros-gatos', 'planificador-dimensiones-transportin-mascotas'];
|
|
76
|
+
const validSlugs = ['calculadora-edad-mascotas', 'calculadora-racion-diaria-mascotas', 'calculadora-gestacion-mascotas', 'buscador-alimentos-toxicos-perros-gatos', 'calculadora-agua-diaria-perros-gatos', 'planificador-dimensiones-transportin-mascotas', 'planificador-horario-medicacion-mascota'];
|
|
75
77
|
expect(validSlugs).toContain(content.slug);
|
|
76
78
|
}
|
|
77
79
|
});
|
|
@@ -96,12 +98,12 @@ describe('Tool Validation Suite', () => {
|
|
|
96
98
|
});
|
|
97
99
|
|
|
98
100
|
describe('Library Registration', () => {
|
|
99
|
-
it('should have
|
|
100
|
-
expect(ALL_TOOLS.length).toBe(
|
|
101
|
+
it('should have 7 tools in ALL_TOOLS', () => {
|
|
102
|
+
expect(ALL_TOOLS.length).toBe(7);
|
|
101
103
|
});
|
|
102
104
|
|
|
103
105
|
it('should have all tools in petsCategory', () => {
|
|
104
|
-
expect(petsCategory.tools.length).toBe(
|
|
106
|
+
expect(petsCategory.tools.length).toBe(7);
|
|
105
107
|
ALL_TOOLS.forEach(({ entry }) => {
|
|
106
108
|
const exists = petsCategory.tools.some((t: any) => t.id === entry.id);
|
|
107
109
|
expect(exists).toBe(true);
|
|
@@ -254,7 +254,7 @@ const { ui } = Astro.props;
|
|
|
254
254
|
const url = generateShareUrl(state);
|
|
255
255
|
try {
|
|
256
256
|
await navigator.clipboard.writeText(url);
|
|
257
|
-
if (els.shareText) els.shareText.textContent = uiData.shareSuccess;
|
|
257
|
+
if (els.shareText) els.shareText.textContent = uiData.shareSuccess || "";
|
|
258
258
|
if (els.shareBtn) els.shareBtn.classList.add("age-btn-success");
|
|
259
259
|
setTimeout(() => (window.location.href = url), 800);
|
|
260
260
|
} catch {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{ name: 'FDA: Medications for Your Pet - Questions for Your Vet', url: 'https://www.fda.gov/animal-veterinary/animal-health-literacy/medications-your-pet-questions-your-vet' },
|
|
5
|
+
{ name: 'FDA: Pet Meds', url: 'https://www.fda.gov/animal-veterinary/animal-health-literacy/pet-meds' },
|
|
6
|
+
];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { PetMedicationSchedulePlannerUI } from './index';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: PetMedicationSchedulePlannerUI;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<div class="pet-medication-schedule-planner" data-schedule-root>
|
|
12
|
+
<script is:inline type="application/json" data-medication-ui set:html={JSON.stringify(ui)}></script>
|
|
13
|
+
<div class="medication-workbench">
|
|
14
|
+
<form class="medication-form" onsubmit="return false">
|
|
15
|
+
<div class="form-heading"><span>01</span><div><p>{ui.scheduleTitle}</p><h2>{ui.medicationNameLabel}</h2></div></div>
|
|
16
|
+
<label class="full-field"><span>{ui.medicationNameLabel}</span><input data-input data-medication-name type="text" placeholder={ui.medicationNamePlaceholder} autocomplete="off" /></label>
|
|
17
|
+
<div class="two-fields"><label><span>{ui.startDateLabel}</span><input data-input data-start-date type="date" /></label><label><span>{ui.startTimeLabel}</span><input data-input data-start-time type="time" /></label></div>
|
|
18
|
+
<fieldset class="mode-field"><legend>{ui.scheduleModeLabel}</legend><div class="mode-buttons"><button type="button" data-mode="interval" aria-pressed="true">{ui.intervalMode}</button><button type="button" data-mode="times" aria-pressed="false">{ui.timesMode}</button></div></fieldset>
|
|
19
|
+
<label data-interval-wrap><span>{ui.intervalHoursLabel}</span><input data-input data-interval-hours type="number" min="1" max="24" step="1" /></label>
|
|
20
|
+
<label data-times-wrap hidden><span>{ui.timesLabel}<small>{ui.timesHint}</small></span><input data-input data-daily-times type="text" /></label>
|
|
21
|
+
<label><span>{ui.durationLabel}</span><span class="with-suffix"><input data-input data-duration-days type="number" min="1" max="30" step="1" /><b>{ui.durationUnit}</b></span></label>
|
|
22
|
+
<label><span>{ui.instructionsLabel}</span><textarea data-input data-instructions rows="3" placeholder={ui.instructionsPlaceholder}></textarea></label>
|
|
23
|
+
<p class="form-error" data-error role="alert"></p>
|
|
24
|
+
<button class="reset-button" type="button" data-reset>{ui.reset}</button>
|
|
25
|
+
</form>
|
|
26
|
+
<section class="medication-result" data-result aria-live="polite">
|
|
27
|
+
<div class="result-top"><div><h2>{ui.scheduleTitle}</h2></div><div class="next-dose"><span>{ui.nextDoseLabel}</span><strong data-next-dose>{ui.noNextDose}</strong></div></div>
|
|
28
|
+
<div class="progress-line"><span data-completed-count></span><span class="progress-rule"></span></div>
|
|
29
|
+
<div class="schedule-list" data-schedule-list aria-label={ui.scheduleIllustration}></div>
|
|
30
|
+
<p class="empty-schedule" data-empty-schedule>{ui.emptySchedule}</p>
|
|
31
|
+
</section>
|
|
32
|
+
</div>
|
|
33
|
+
<aside class="medication-safety"><strong>{ui.safetyTitle}</strong><span>{ui.safetyText}</span></aside>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<script>
|
|
37
|
+
import { initMedicationSchedulePlanner } from './controller';
|
|
38
|
+
const root = document.querySelector<HTMLElement>('[data-schedule-root]');
|
|
39
|
+
if (root) initMedicationSchedulePlanner(root);
|
|
40
|
+
</script>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { generateSchedule, type ScheduleMode } from './logic';
|
|
2
|
+
import { clearMedicationState, loadMedicationState, saveMedicationState, type MedicationStorageState } from './storage';
|
|
3
|
+
import { renderSchedule } from './dom-views';
|
|
4
|
+
import type { PetMedicationSchedulePlannerUI } from './ui';
|
|
5
|
+
|
|
6
|
+
function todayValue(): string {
|
|
7
|
+
const now = new Date();
|
|
8
|
+
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function defaultState(): MedicationStorageState {
|
|
12
|
+
return { medicationName: 'Example medicine', startDate: todayValue(), startTime: '08:00', mode: 'interval', intervalHours: 8, dailyTimes: '08:00, 16:00, 00:00', durationDays: 5, instructions: 'Give exactly as prescribed by your veterinarian.', completedIds: [] };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function readUI(root: HTMLElement): PetMedicationSchedulePlannerUI {
|
|
16
|
+
const script = root.querySelector<HTMLScriptElement>('[data-medication-ui]');
|
|
17
|
+
return JSON.parse(script?.textContent || '{}') as PetMedicationSchedulePlannerUI;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function mergeState(stored: Partial<MedicationStorageState>): MedicationStorageState {
|
|
21
|
+
const state = { ...defaultState(), ...stored, completedIds: Array.isArray(stored.completedIds) ? stored.completedIds : [] };
|
|
22
|
+
if (!state.medicationName.trim()) state.medicationName = defaultState().medicationName;
|
|
23
|
+
return state;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function readForm(root: HTMLElement, state: MedicationStorageState): void {
|
|
27
|
+
const read = (selector: string): string => root.querySelector<HTMLInputElement | HTMLTextAreaElement>(selector)?.value ?? '';
|
|
28
|
+
state.medicationName = read('[data-medication-name]');
|
|
29
|
+
state.startDate = read('[data-start-date]');
|
|
30
|
+
state.startTime = read('[data-start-time]');
|
|
31
|
+
state.intervalHours = Number(read('[data-interval-hours]'));
|
|
32
|
+
state.dailyTimes = read('[data-daily-times]');
|
|
33
|
+
state.durationDays = Number(read('[data-duration-days]'));
|
|
34
|
+
state.instructions = read('[data-instructions]');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function syncForm(root: HTMLElement, state: MedicationStorageState): void {
|
|
38
|
+
const set = (selector: string, value: string | number): void => { const input = root.querySelector<HTMLInputElement | HTMLTextAreaElement>(selector); if (input) input.value = String(value); };
|
|
39
|
+
set('[data-medication-name]', state.medicationName);
|
|
40
|
+
set('[data-start-date]', state.startDate);
|
|
41
|
+
set('[data-start-time]', state.startTime);
|
|
42
|
+
set('[data-interval-hours]', state.intervalHours);
|
|
43
|
+
set('[data-daily-times]', state.dailyTimes);
|
|
44
|
+
set('[data-duration-days]', state.durationDays);
|
|
45
|
+
set('[data-instructions]', state.instructions);
|
|
46
|
+
root.querySelectorAll<HTMLButtonElement>('[data-mode]').forEach((button) => { const active = button.dataset.mode === state.mode; button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', String(active)); });
|
|
47
|
+
const interval = root.querySelector<HTMLElement>('[data-interval-wrap]');
|
|
48
|
+
const times = root.querySelector<HTMLElement>('[data-times-wrap]');
|
|
49
|
+
if (interval) interval.hidden = state.mode !== 'interval';
|
|
50
|
+
if (times) times.hidden = state.mode !== 'times';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function renderEmptyState(root: HTMLElement, error: HTMLElement | null): void {
|
|
54
|
+
if (error) error.textContent = '';
|
|
55
|
+
root.querySelector<HTMLElement>('[data-result]')?.classList.remove('is-ready');
|
|
56
|
+
root.querySelector<HTMLElement>('[data-empty-schedule]')?.removeAttribute('hidden');
|
|
57
|
+
root.querySelector<HTMLElement>('[data-schedule-list]')?.replaceChildren();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function renderValidState(root: HTMLElement, state: MedicationStorageState, ui: PetMedicationSchedulePlannerUI, error: HTMLElement | null): void {
|
|
61
|
+
try {
|
|
62
|
+
const doses = generateSchedule(state);
|
|
63
|
+
saveMedicationState(state);
|
|
64
|
+
renderSchedule(root, doses, { completedIds: state.completedIds, now: new Date() }, ui);
|
|
65
|
+
if (error) error.textContent = '';
|
|
66
|
+
root.querySelector<HTMLElement>('[data-result]')?.classList.add('is-ready');
|
|
67
|
+
} catch {
|
|
68
|
+
if (error) error.textContent = ui.invalidInput;
|
|
69
|
+
root.querySelector<HTMLElement>('[data-result]')?.classList.remove('is-ready');
|
|
70
|
+
const empty = root.querySelector<HTMLElement>('[data-empty-schedule]');
|
|
71
|
+
if (empty) empty.hidden = false;
|
|
72
|
+
root.querySelector<HTMLElement>('[data-schedule-list]')?.replaceChildren();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function render(root: HTMLElement, state: MedicationStorageState, ui: PetMedicationSchedulePlannerUI): void {
|
|
77
|
+
readForm(root, state);
|
|
78
|
+
const error = root.querySelector<HTMLElement>('[data-error]');
|
|
79
|
+
if (!state.medicationName.trim()) {
|
|
80
|
+
renderEmptyState(root, error);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
renderValidState(root, state, ui, error);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function initMedicationSchedulePlanner(root: HTMLElement): void {
|
|
87
|
+
const ui = readUI(root);
|
|
88
|
+
const state = mergeState(loadMedicationState());
|
|
89
|
+
syncForm(root, state);
|
|
90
|
+
root.querySelectorAll<HTMLButtonElement>('[data-mode]').forEach((button) => button.addEventListener('click', () => { state.mode = (button.dataset.mode || 'interval') as ScheduleMode; syncForm(root, state); render(root, state, ui); }));
|
|
91
|
+
root.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>('[data-input]').forEach((input) => input.addEventListener('input', () => { state.completedIds = []; render(root, state, ui); }));
|
|
92
|
+
root.querySelector<HTMLElement>('[data-schedule-list]')?.addEventListener('click', (event) => { const button = (event.target as HTMLElement).closest<HTMLButtonElement>('[data-dose-id]'); if (!button) return; const id = button.dataset.doseId; if (!id) return; state.completedIds = state.completedIds.includes(id) ? state.completedIds.filter((item) => item !== id) : [...state.completedIds, id]; render(root, state, ui); });
|
|
93
|
+
root.querySelector<HTMLButtonElement>('[data-reset]')?.addEventListener('click', () => { clearMedicationState(); Object.assign(state, defaultState()); syncForm(root, state); render(root, state, ui); });
|
|
94
|
+
render(root, state, ui);
|
|
95
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { formatDay, formatTime, type ScheduledDose } from './logic';
|
|
2
|
+
import type { PetMedicationSchedulePlannerUI } from './ui';
|
|
3
|
+
|
|
4
|
+
export interface RenderState {
|
|
5
|
+
completedIds: string[];
|
|
6
|
+
now: Date;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function setText(root: HTMLElement, selector: string, value: string): void {
|
|
10
|
+
const element = root.querySelector<HTMLElement>(selector);
|
|
11
|
+
if (element) element.textContent = value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function createDoseButton(dose: ScheduledDose, state: RenderState, ui: PetMedicationSchedulePlannerUI): HTMLButtonElement {
|
|
15
|
+
const completed = state.completedIds.includes(dose.id);
|
|
16
|
+
const button = document.createElement('button');
|
|
17
|
+
button.type = 'button';
|
|
18
|
+
button.className = `dose-row${completed ? ' is-complete' : ''}`;
|
|
19
|
+
button.dataset.doseId = dose.id;
|
|
20
|
+
button.setAttribute('aria-pressed', String(completed));
|
|
21
|
+
button.innerHTML = '<span class="dose-check" aria-hidden="true"></span><span class="dose-time"></span><span class="dose-state"></span>';
|
|
22
|
+
const time = button.querySelector<HTMLElement>('.dose-time');
|
|
23
|
+
if (time) time.textContent = formatTime(dose.date, ui.dateLocale);
|
|
24
|
+
const status = button.querySelector<HTMLElement>('.dose-state');
|
|
25
|
+
if (status) {
|
|
26
|
+
if (completed) status.textContent = ui.completed;
|
|
27
|
+
else if (dose.date <= state.now) status.textContent = ui.due;
|
|
28
|
+
else status.textContent = ui.upcoming;
|
|
29
|
+
}
|
|
30
|
+
button.title = completed ? ui.markUndone : ui.markDone;
|
|
31
|
+
return button;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function renderSchedule(root: HTMLElement, doses: ScheduledDose[], state: RenderState, ui: PetMedicationSchedulePlannerUI): void {
|
|
35
|
+
const list = root.querySelector<HTMLElement>('[data-schedule-list]');
|
|
36
|
+
if (!list) return;
|
|
37
|
+
list.replaceChildren();
|
|
38
|
+
const groups = new Map<string, ScheduledDose[]>();
|
|
39
|
+
doses.forEach((dose) => groups.set(dose.dateKey, [...(groups.get(dose.dateKey) ?? []), dose]));
|
|
40
|
+
groups.forEach((group) => {
|
|
41
|
+
const first = group[0];
|
|
42
|
+
if (!first) return;
|
|
43
|
+
const section = document.createElement('section');
|
|
44
|
+
section.className = 'dose-day';
|
|
45
|
+
const heading = document.createElement('h3');
|
|
46
|
+
heading.textContent = formatDay(first.date, ui.dateLocale);
|
|
47
|
+
section.append(heading);
|
|
48
|
+
group.forEach((dose) => section.append(createDoseButton(dose, state, ui)));
|
|
49
|
+
list.append(section);
|
|
50
|
+
});
|
|
51
|
+
const completed = doses.filter((dose) => state.completedIds.includes(dose.id)).length;
|
|
52
|
+
setText(root, '[data-completed-count]', ui.completedCount.replace('{done}', String(completed)).replace('{total}', String(doses.length)));
|
|
53
|
+
const next = doses.find((dose) => !state.completedIds.includes(dose.id) && dose.date >= state.now) ?? doses.find((dose) => !state.completedIds.includes(dose.id));
|
|
54
|
+
setText(root, '[data-next-dose]', next ? `${formatDay(next.date, ui.dateLocale)} · ${formatTime(next.date, ui.dateLocale)}` : ui.noNextDose);
|
|
55
|
+
const empty = root.querySelector<HTMLElement>('[data-empty-schedule]');
|
|
56
|
+
if (empty) empty.hidden = doses.length > 0;
|
|
57
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PetToolEntry } from '../../types';
|
|
2
|
+
import type { PetMedicationSchedulePlannerUI } from './ui';
|
|
3
|
+
|
|
4
|
+
export type { PetMedicationSchedulePlannerLocaleContent, PetMedicationSchedulePlannerUI } from './ui';
|
|
5
|
+
|
|
6
|
+
export const petMedicationSchedulePlanner: PetToolEntry<PetMedicationSchedulePlannerUI> = {
|
|
7
|
+
id: 'pet-medication-schedule-planner',
|
|
8
|
+
icons: { bg: 'mdi:paw', fg: 'mdi:calendar-check' },
|
|
9
|
+
i18n: {
|
|
10
|
+
de: () => import('./i18n/de').then((module) => module.content),
|
|
11
|
+
en: () => import('./i18n/en').then((module) => module.content),
|
|
12
|
+
es: () => import('./i18n/es').then((module) => module.content),
|
|
13
|
+
fr: () => import('./i18n/fr').then((module) => module.content),
|
|
14
|
+
id: () => import('./i18n/id').then((module) => module.content),
|
|
15
|
+
it: () => import('./i18n/it').then((module) => module.content),
|
|
16
|
+
ja: () => import('./i18n/ja').then((module) => module.content),
|
|
17
|
+
ko: () => import('./i18n/ko').then((module) => module.content),
|
|
18
|
+
nl: () => import('./i18n/nl').then((module) => module.content),
|
|
19
|
+
pl: () => import('./i18n/pl').then((module) => module.content),
|
|
20
|
+
pt: () => import('./i18n/pt').then((module) => module.content),
|
|
21
|
+
ru: () => import('./i18n/ru').then((module) => module.content),
|
|
22
|
+
sv: () => import('./i18n/sv').then((module) => module.content),
|
|
23
|
+
tr: () => import('./i18n/tr').then((module) => module.content),
|
|
24
|
+
zh: () => import('./i18n/zh').then((module) => module.content),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { PetMedicationSchedulePlannerLocaleContent, PetMedicationSchedulePlannerUI } from '../ui';
|
|
4
|
+
|
|
5
|
+
type MedicationUIFields = Pick<PetMedicationSchedulePlannerUI, 'dateLocale' | 'medicationNameLabel' | 'medicationNamePlaceholder' | 'startDateLabel' | 'startTimeLabel' | 'scheduleModeLabel' | 'intervalMode' | 'timesMode' | 'intervalHoursLabel' | 'timesLabel' | 'timesHint' | 'durationLabel' | 'durationUnit' | 'instructionsLabel' | 'instructionsPlaceholder' | 'reset' | 'scheduleTitle' | 'nextDoseLabel' | 'noNextDose' | 'completedCount' | 'markDone' | 'markUndone' | 'completed' | 'upcoming' | 'due' | 'emptySchedule' | 'invalidInput' | 'safetyTitle' | 'safetyText' | 'scheduleIllustration'>;
|
|
6
|
+
|
|
7
|
+
export interface MedicationCopy extends MedicationUIFields {
|
|
8
|
+
slug: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
summary: string[];
|
|
12
|
+
seoTitle1: string;
|
|
13
|
+
seoIntro: string;
|
|
14
|
+
seoTitle2: string;
|
|
15
|
+
seoMethod: string;
|
|
16
|
+
seoTitle3: string;
|
|
17
|
+
seoSafety: string;
|
|
18
|
+
tipTitle: string;
|
|
19
|
+
tipText: string;
|
|
20
|
+
methodText: string;
|
|
21
|
+
faq: { question: string; answer: string }[];
|
|
22
|
+
howTo: { name: string; text: string }[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createContent(copy: MedicationCopy): PetMedicationSchedulePlannerLocaleContent {
|
|
26
|
+
const ui = copy as unknown as PetMedicationSchedulePlannerUI;
|
|
27
|
+
const faq = copy.faq;
|
|
28
|
+
const howTo = copy.howTo;
|
|
29
|
+
let slug = copy.slug;
|
|
30
|
+
if (['ja-JP', 'ko-KR', 'zh-CN'].includes(copy.dateLocale)) slug = 'pet-medication-schedule-planner';
|
|
31
|
+
const schemas = [
|
|
32
|
+
{ '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: copy.title, description: copy.description, applicationCategory: 'LifestyleApplication', operatingSystem: 'Web', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' } } as WithContext<SoftwareApplication>,
|
|
33
|
+
{ '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) } as WithContext<FAQPage>,
|
|
34
|
+
{ '@context': 'https://schema.org', '@type': 'HowTo', name: copy.title, step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) } as WithContext<HowTo>,
|
|
35
|
+
];
|
|
36
|
+
return {
|
|
37
|
+
slug,
|
|
38
|
+
title: copy.title,
|
|
39
|
+
description: copy.description,
|
|
40
|
+
ui,
|
|
41
|
+
faq,
|
|
42
|
+
howTo,
|
|
43
|
+
bibliography,
|
|
44
|
+
schemas,
|
|
45
|
+
seo: [
|
|
46
|
+
{ type: 'summary', title: copy.title, items: copy.summary },
|
|
47
|
+
{ type: 'title', text: copy.seoTitle1, level: 2 },
|
|
48
|
+
{ type: 'paragraph', html: copy.seoIntro },
|
|
49
|
+
{ type: 'title', text: copy.seoTitle2, level: 2 },
|
|
50
|
+
{ type: 'paragraph', html: copy.seoMethod },
|
|
51
|
+
{ type: 'title', text: copy.seoTitle3, level: 2 },
|
|
52
|
+
{ type: 'paragraph', html: copy.seoSafety },
|
|
53
|
+
{ type: 'tip', title: copy.tipTitle, html: copy.tipText },
|
|
54
|
+
{ type: 'title', text: copy.scheduleTitle, level: 2 },
|
|
55
|
+
{ type: 'paragraph', html: [...copy.howTo.map((step) => `${step.name}: ${step.text}`), ...copy.faq.map((item) => `${item.question} ${item.answer}`), copy.methodText, copy.safetyText].join(' ') },
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
import { copy as en } from './en';
|
|
3
|
+
|
|
4
|
+
const copy: MedicationCopy = { ...en, slug: 'medikamentenplan-fuer-haustiere', title: 'Medikamentenplan für Haustiere', description: 'Machen Sie aus einer tierärztlich verordneten Behandlung einen klaren lokalen Zeitplan mit nächsten Gaben und Checkliste.', dateLocale: 'de', medicationNameLabel: 'Medikament', medicationNamePlaceholder: 'Zum Beispiel: verschriebenes Antibiotikum', startDateLabel: 'Datum der ersten Gabe', startTimeLabel: 'Uhrzeit der ersten Gabe', scheduleModeLabel: 'Wie ist der Rhythmus verordnet?', intervalMode: 'Alle paar Stunden', timesMode: 'Feste Uhrzeiten täglich', intervalHoursLabel: 'Stunden zwischen den Gaben', timesLabel: 'Tägliche Uhrzeiten', timesHint: '24-Stunden-Zeiten durch Kommas trennen, etwa 08:00, 16:00, 00:00.', durationLabel: 'Behandlungsdauer', durationUnit: 'Tage', instructionsLabel: 'Hinweise aus der Verordnung', instructionsPlaceholder: 'Futter, Verabreichung oder andere Hinweise der Tierarztpraxis', reset: 'Plan löschen', scheduleTitle: 'Ihr Gabeplan', nextDoseLabel: 'Nächste offene Gabe', noNextDose: 'Alle aufgeführten Gaben sind abgehakt', completedCount: '{done} von {total} abgehakt', markDone: 'Gabe als erledigt markieren', markUndone: 'Gabe wieder offen markieren', completed: 'Erledigt', upcoming: 'Kommend', due: 'Prüfen', emptySchedule: 'Füllen Sie die Felder aus, um den Gabeplan zu sehen.', invalidInput: 'Prüfen Sie Name, Datum, Uhrzeit, Dauer und Rhythmus.', safetyTitle: 'Die Verordnung genau befolgen', safetyText: 'Dies ist eine Erinnerung und Dokumentation. Es wählt kein Medikament, ändert keine Dosis und ersetzt keine tierärztliche Beratung. Bei verspäteter, vergessener oder erbrochener Gabe bitte die verordnende Praxis fragen.', methodText: 'Bei Intervallen beginnt der Plan mit Datum und Uhrzeit der ersten Gabe und addiert die gewählten Stunden. Tägliche Uhrzeiten werden an jedem Behandlungstag wiederholt. Die Angaben bleiben auf diesem Gerät, bis Sie sie löschen.', scheduleIllustration: 'Handgezeichneter Tagesplan neben einer ruhigen Katze und einem Hund', summary: ['Eine vorhandene Verordnung in einen lesbaren Plan übertragen.', 'Intervalle oder feste Uhrzeiten für jeden Tag wählen.', 'Gaben abhaken, ohne Tierdaten zu versenden.', 'Medikamentenname und Hinweise neben dem Ablauf behalten.'], seoTitle1: 'Eine praktische Erinnerung für eine bestehende tierärztliche Verordnung', seoIntro: 'Nach einem Praxisbesuch müssen Halter oft Etikett, Entlassungsbogen und mehrere Gaben zu Hause zusammenführen. Welche Behandlung nötig ist, entscheidet die Tierärztin oder der Tierarzt; dieser Planer ordnet nur die bereits vorliegenden Anweisungen. Name, erster lokaler Zeitpunkt, Rhythmus, Dauer und optionale Hinweise ergeben eine chronologische Liste für hektische Morgen, nächtliche Gaben oder die Übergabe zwischen Betreuungspersonen.', seoTitle2: 'Zwei Arten, den Rhythmus einzutragen', seoMethod: 'Wählen Sie ein Intervall, wenn die Verordnung etwa alle acht Stunden sagt. Der Plan läuft dabei über Mitternacht weiter. Wählen Sie tägliche Uhrzeiten, wenn morgens und abends oder konkrete Uhrzeiten genannt werden. Am ersten Tag wird keine frühere Zeit stillschweigend ergänzt. Die Dauer begrenzt nur den Planungszeitraum; sie interpretiert die Verordnung nicht. Das Ergebnis enthält Uhrzeiten, keine Medikamentenmengen.', seoTitle3: 'Eine Checkliste ist kein medizinischer Rat', seoSafety: 'Bewahren Sie Packung, Praxisunterlagen und Kontaktdaten auf. Die Seite kennt Tier, Konzentration, Verabreichungsweg, Wechselwirkungen und Reaktion nicht. Bei Unsicherheit zu einer verpassten Gabe immer die Praxis kontaktieren. Das Abhaken kann trotzdem Doppelgaben vermeiden und die nächste Aufgabe für mehrere Betreuungspersonen sichtbar machen. Der lokale Speicher ist nur eine Hilfe und jederzeit löschbar.', tipTitle: 'Vor dem Start', tipText: 'Übernehmen Sie Häufigkeit und Dauer aus der Verordnung, statt sie zu schätzen. Prüfen Sie den ersten erzeugten Tag noch einmal gegen das Etikett.', faq: [{ question: 'Berechnet der Planer die Medikamentenmenge?', answer: 'Nein. Er ordnet nur Uhrzeiten, die Sie aus einer bestehenden tierärztlichen Verordnung übernehmen. Mengen werden nicht berechnet, geändert oder empfohlen.' }, { question: 'Werden Gaben nach Mitternacht unterstützt?', answer: 'Ja. Intervalle laufen am lokalen Folgetag weiter; bei täglichen Uhrzeiten ist auch 00:00 möglich. Die Gruppierung folgt dem lokalen Browserdatum.' }, { question: 'Was tun bei einer verspäteten oder vergessenen Gabe?', answer: 'Fragen Sie die verordnende Tierarztpraxis. Die Liste kann den Stand dokumentieren, entscheidet aber nicht über Nachholen, Auslassen oder Verschieben.' }], howTo: [{ name: 'Verordnung übertragen', text: 'Name, ersten Zeitpunkt und Dauer genau nach der Anweisung der Tierarztpraxis eintragen.' }, { name: 'Rhythmus wählen', text: 'Ein Stundenintervall oder die verordneten täglichen Uhrzeiten auswählen.' }, { name: 'Plan prüfen', text: 'Ersten Tag und nächste offene Gabe kontrollieren, bevor der Plan als Haushaltserinnerung dient.' }, { name: 'Gaben abhaken', text: 'Nach der Gabe die Zeile antippen und die ursprünglichen Hinweise bereithalten.' }] };
|
|
5
|
+
|
|
6
|
+
export const content = createContent(copy);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
|
|
3
|
+
export const copy: MedicationCopy = {
|
|
4
|
+
slug: 'pet-medication-schedule-planner', title: 'Pet Medication Schedule Planner', description: 'Turn a veterinarian-prescribed pet medication plan into a clear local schedule with upcoming doses and completion check-offs.', dateLocale: 'en', medicationNameLabel: 'Medication name', medicationNamePlaceholder: 'For example: prescribed antibiotic', startDateLabel: 'First dose date', startTimeLabel: 'First dose time', scheduleModeLabel: 'How is the prescription timed?', intervalMode: 'Every few hours', timesMode: 'Set times each day', intervalHoursLabel: 'Hours between doses', timesLabel: 'Daily dose times', timesHint: 'Use 24-hour times separated by commas, such as 08:00, 16:00, 00:00.', durationLabel: 'Treatment length', durationUnit: 'days', instructionsLabel: 'Prescription notes', instructionsPlaceholder: 'Food, handling, or other instructions from your veterinarian', reset: 'Clear this schedule', scheduleTitle: 'Your dose board', nextDoseLabel: 'Next unchecked dose', noNextDose: 'All listed doses are checked', completedCount: '{done} of {total} checked', markDone: 'Mark dose as given', markUndone: 'Mark dose as not given', completed: 'Checked', upcoming: 'Upcoming', due: 'Review', emptySchedule: 'Complete the fields above to see the dose board.', invalidInput: 'Check the medication name, date, time, duration, and timing rule.', safetyTitle: 'Use the prescription exactly as provided', safetyText: 'This is a reminder and record-keeping aid. Do not use it to choose a medicine, change a dose, or replace advice from your veterinarian. If a dose is late, missed, vomited, or disputed, contact the prescribing practice for instructions.', methodText: 'Interval plans begin at the first date and time, then add the selected number of hours until the treatment window ends. Daily-time plans repeat the times you enter on each treatment day. Everything stays on this device unless you choose to clear it.', scheduleIllustration: 'A hand-drawn daily timetable beside a calm cat and dog', summary: ['Build a short, readable timetable from an existing prescription.', 'Choose fixed daily times or a simple interval between doses.', 'Mark each dose as checked without sending pet information anywhere.', 'Keep the medicine name and notes visible beside the routine.'], seoTitle1: 'A practical reminder for an existing veterinary plan', seoIntro: 'Caregivers often leave a clinic with a label, a verbal instruction, or several medicines to coordinate at home. The hard part is not deciding what the animal should receive: that decision belongs to the prescribing veterinarian. The hard part is turning the instructions already in hand into a routine that is easy to scan during a busy morning, overnight wake-up, or multi-day recovery. This planner accepts the medicine name, the first local date and time, a timing pattern, the number of treatment days, and optional notes. It creates a chronological checklist that can be reviewed on the same device where it was entered.', seoTitle2: 'Two ways to describe the timing', seoMethod: 'Use an interval when the label says something like every eight hours. The planner starts from the first dose and adds that interval, including doses that cross midnight. Use daily times when the prescription gives moments such as morning and evening or a set of clock times. The first day respects the first-dose time, so an earlier daily time is not silently added. The treatment length is a bounded planning window; it is not an interpretation of the medicine label. The result is a schedule of times, not a dose calculator.', seoTitle3: 'A checklist is not medical advice', seoSafety: 'Keep the original label, discharge sheet, and veterinary contact details available. This page does not know the animal, medicine, concentration, route, interactions, or clinical response. Never use a missed-dose decision from this page as a substitute for professional advice. Record keeping can still help: checking a dose reduces uncertainty between caregivers, and the visible next unchecked dose makes handovers easier. The local storage feature is only a convenience and can be cleared at any time.', tipTitle: 'Before you begin', tipText: 'Copy the timing and duration from the prescription rather than estimating them. Add practical notes only when they came from the veterinary team, and verify the first generated day against the label before relying on the checklist.', faq: [{ question: 'Does this planner calculate the amount of medicine?', answer: 'No. It only lays out a timing plan that you enter from an existing veterinary prescription. It does not calculate, adjust, or recommend an amount.' }, { question: 'Can it handle doses that cross midnight?', answer: 'Yes. An interval schedule continues across local calendar days, and a daily-time schedule can include a time such as 00:00. The list is grouped by the local date shown in your browser.' }, { question: 'What should I do if a dose is late or missed?', answer: 'Contact the prescribing veterinarian or practice for instructions. The checklist can record what happened, but it cannot decide whether to give, skip, or move a dose.' }], howTo: [{ name: 'Copy the prescription details', text: 'Enter the medicine name, first dose date and time, and the treatment length exactly as provided by the veterinary team.' }, { name: 'Choose the timing pattern', text: 'Select an interval in hours or enter the daily clock times named by the prescription.' }, { name: 'Read the generated board', text: 'Review the first day and the next unchecked dose before using the list as a household reminder.' }, { name: 'Check off each dose', text: 'Tap a row after the dose has been given and keep the original instructions nearby for any uncertainty.' }],
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const content = createContent(copy);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
import { copy as en } from './en';
|
|
3
|
+
|
|
4
|
+
const copy: MedicationCopy = { ...en, slug: 'planificador-horario-medicacion-mascota', title: 'Planificador de medicación para mascotas', description: 'Convierte una pauta veterinaria prescrita en un horario local claro, con próximas tomas y casillas de seguimiento.', dateLocale: 'es', medicationNameLabel: 'Nombre del medicamento', medicationNamePlaceholder: 'Por ejemplo: antibiótico prescrito', startDateLabel: 'Fecha de la primera toma', startTimeLabel: 'Hora de la primera toma', scheduleModeLabel: '¿Cómo está indicada la pauta?', intervalMode: 'Cada ciertas horas', timesMode: 'Horas fijas cada día', intervalHoursLabel: 'Horas entre tomas', timesLabel: 'Horas diarias', timesHint: 'Usa horas de 24 h separadas por comas, por ejemplo 08:00, 16:00, 00:00.', durationLabel: 'Duración del tratamiento', durationUnit: 'días', instructionsLabel: 'Notas de la receta', instructionsPlaceholder: 'Comida, forma de administración u otras indicaciones veterinarias', reset: 'Borrar este horario', scheduleTitle: 'Panel de tomas', nextDoseLabel: 'Siguiente toma sin marcar', noNextDose: 'Todas las tomas están marcadas', completedCount: '{done} de {total} marcadas', markDone: 'Marcar toma administrada', markUndone: 'Marcar toma como pendiente', completed: 'Hecha', upcoming: 'Próxima', due: 'Revisar', emptySchedule: 'Completa los campos para ver el panel de tomas.', invalidInput: 'Revisa el nombre, la fecha, la hora, la duración y la regla de horario.', safetyTitle: 'Usa exactamente la pauta prescrita', safetyText: 'Es una ayuda para recordar y registrar. No elige medicamentos, cambia dosis ni sustituye al veterinario. Si una toma se retrasa, se olvida o se vomita, contacta con la clínica que la prescribió.', methodText: 'Las pautas por intervalo parten de la primera fecha y hora y suman las horas elegidas hasta terminar el periodo. Las pautas por horas diarias repiten las horas introducidas en cada día. Todo queda en este dispositivo salvo que decidas borrarlo.', scheduleIllustration: 'Calendario dibujado a mano junto a un gato y un perro tranquilos', summary: ['Crea un horario legible a partir de una receta existente.', 'Elige intervalos de horas o momentos fijos del día.', 'Marca cada toma sin enviar datos de tu mascota.', 'Mantén visibles el nombre y las notas de la pauta.'], seoTitle1: 'Un recordatorio práctico para una pauta veterinaria existente', seoIntro: 'Después de una consulta, la dificultad suele ser convertir una etiqueta o varias instrucciones en una rutina doméstica que se entienda de un vistazo. Esta herramienta no decide qué debe recibir el animal: esa decisión corresponde al veterinario que lo ha examinado. Solo pide el nombre, la primera fecha y hora, la forma de repetir la pauta, los días de tratamiento y las notas que quieras conservar. El resultado es una lista cronológica para consultar durante la mañana, una noche con varias personas cuidadoras o una recuperación de varios días.', seoTitle2: 'Dos formas de describir las horas', seoMethod: 'Elige un intervalo cuando la etiqueta indique algo como cada ocho horas. La siguiente toma se calcula sumando ese intervalo, también al pasar de un día al siguiente. Elige horas diarias cuando la pauta diga mañana y noche o enumere horas concretas. El primer día respeta la primera toma y no añade en silencio una hora anterior. La duración limita el periodo de planificación; no interpreta ni modifica la receta. El resultado son horas, no cantidades de medicamento.', seoTitle3: 'Una lista de control no es consejo médico', seoSafety: 'Conserva cerca la etiqueta original, el informe de la clínica y su teléfono. La página no conoce el animal, la concentración, la vía, las interacciones ni su respuesta. Ante una toma olvidada o dudosa, pregunta al equipo veterinario antes de decidir. Marcar las tomas sí puede reducir confusiones entre cuidadores y hacer más clara la siguiente acción. El almacenamiento local es solo una comodidad y puede borrarse en cualquier momento.', tipTitle: 'Antes de empezar', tipText: 'Copia de la receta la frecuencia y la duración, sin estimarlas. Añade solo notas procedentes del equipo veterinario y comprueba el primer día generado con la etiqueta antes de confiar en la lista.', faq: [{ question: '¿La herramienta calcula la cantidad de medicamento?', answer: 'No. Solo organiza horarios que introduces a partir de una receta veterinaria existente. No calcula, ajusta ni recomienda cantidades.' }, { question: '¿Puede mostrar tomas que pasan de medianoche?', answer: 'Sí. Un intervalo continúa en el día local siguiente y las horas diarias pueden incluir 00:00. La lista se agrupa según la fecha local del navegador.' }, { question: '¿Qué hago si una toma se retrasa o se olvida?', answer: 'Contacta con el veterinario o la clínica que indicó la pauta. La lista puede registrar lo ocurrido, pero no decide si debes dar, saltar o mover una toma.' }], howTo: [{ name: 'Copia los datos de la receta', text: 'Introduce el nombre, la primera fecha y hora y la duración exactamente como los indicó el equipo veterinario.' }, { name: 'Elige el patrón horario', text: 'Selecciona un intervalo en horas o escribe las horas diarias de la receta.' }, { name: 'Revisa el panel', text: 'Comprueba el primer día y la siguiente toma sin marcar antes de usarlo como recordatorio doméstico.' }, { name: 'Marca cada toma', text: 'Toca una fila después de administrar la toma y conserva cerca las instrucciones originales.' }] };
|
|
5
|
+
|
|
6
|
+
export const content = createContent(copy);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
import { copy as en } from './en';
|
|
3
|
+
|
|
4
|
+
const copy: MedicationCopy = { ...en, slug: 'planning-traitement-animal', title: 'Planning des médicaments pour animaux', description: 'Transformez une prescription vétérinaire en calendrier local lisible avec prochaines prises et cases de suivi.', dateLocale: 'fr', medicationNameLabel: 'Nom du médicament', medicationNamePlaceholder: 'Par exemple: antibiotique prescrit', startDateLabel: 'Date de la première prise', startTimeLabel: 'Heure de la première prise', scheduleModeLabel: 'Comment la prescription est-elle rythmée ?', intervalMode: 'Toutes les quelques heures', timesMode: 'Heures fixes chaque jour', intervalHoursLabel: 'Heures entre les prises', timesLabel: 'Heures quotidiennes', timesHint: 'Utilisez des heures sur 24 h séparées par des virgules: 08:00, 16:00, 00:00.', durationLabel: 'Durée du traitement', durationUnit: 'jours', instructionsLabel: 'Notes de prescription', instructionsPlaceholder: 'Alimentation, administration ou autre consigne du vétérinaire', reset: 'Effacer ce planning', scheduleTitle: 'Tableau des prises', nextDoseLabel: 'Prochaine prise non cochée', noNextDose: 'Toutes les prises sont cochées', completedCount: '{done} sur {total} cochées', markDone: 'Marquer la prise donnée', markUndone: 'Marquer la prise non donnée', completed: 'Faite', upcoming: 'À venir', due: 'À vérifier', emptySchedule: 'Remplissez les champs pour afficher le tableau.', invalidInput: 'Vérifiez le nom, la date, l heure, la durée et le rythme.', safetyTitle: 'Respectez la prescription telle quelle', safetyText: 'Cet outil sert de rappel et de suivi. Il ne choisit pas un médicament, ne modifie pas une dose et ne remplace pas le vétérinaire. En cas de retard, d oubli ou de vomissement, appelez la clinique prescriptrice.', methodText: 'Un intervalle part de la première date et heure puis ajoute le nombre d heures choisi. Les heures quotidiennes se répètent chaque jour de traitement. Les informations restent sur cet appareil jusqu à leur effacement.', scheduleIllustration: 'Calendrier dessiné à la main près d un chat et d un chien calmes', summary: ['Transformez une prescription existante en tableau lisible.', 'Choisissez un intervalle ou des heures fixes.', 'Cochez chaque prise sans transmettre de données.', 'Gardez le nom et les notes visibles pendant la routine.'], seoTitle1: 'Un rappel pratique pour un traitement déjà prescrit', seoIntro: 'Après une consultation, il faut souvent coordonner une étiquette, une feuille de sortie et plusieurs prises dans la maison. Le choix du médicament appartient au vétérinaire ; la difficulté consiste à rendre les instructions déjà reçues faciles à relire. Ce planning demande seulement le nom, le premier moment local, le rythme, la durée et des notes facultatives. Il transforme ces informations en liste chronologique pour les matins chargés, les nuits ou les relais entre proches.', seoTitle2: 'Décrire les horaires de deux façons', seoMethod: 'Choisissez un intervalle si l étiquette dit par exemple toutes les huit heures. Le calcul poursuit naturellement la liste après minuit. Choisissez des heures quotidiennes si la prescription indique matin et soir ou des heures précises. Le premier jour respecte la première prise et n ajoute pas une heure antérieure. La durée est une fenêtre de planification, jamais une interprétation de la prescription. Le résultat affiche des horaires, pas des quantités.', seoTitle3: 'Une checklist ne remplace pas un avis médical', seoSafety: 'Gardez la boîte, la feuille de la clinique et son numéro à portée de main. La page ne connaît ni l animal, ni la concentration, ni les interactions, ni sa réaction. Pour une prise manquée ou incertaine, demandez conseil à l équipe vétérinaire. Cocher les prises réduit néanmoins les doublons entre aidants et rend le prochain geste évident. Le stockage local est optionnel et peut être supprimé à tout moment.', tipTitle: 'Avant de commencer', tipText: 'Recopiez la fréquence et la durée de la prescription, sans les deviner. Ajoutez uniquement les notes données par le vétérinaire et vérifiez le premier jour affiché avec l étiquette.', faq: [{ question: 'Le planning calcule-t-il la quantité ?', answer: 'Non. Il organise uniquement des horaires saisis depuis une prescription existante. Il ne calcule, ne modifie et ne recommande aucune quantité.' }, { question: 'Les prises après minuit sont-elles gérées ?', answer: 'Oui. Un intervalle continue sur le jour local suivant et les heures quotidiennes peuvent contenir 00:00. Les prises sont groupées selon la date locale du navigateur.' }, { question: 'Que faire si une prise est en retard ou oubliée ?', answer: 'Appelez le vétérinaire ou la clinique prescriptrice. La checklist peut noter la situation, mais elle ne décide pas de donner, sauter ou déplacer une prise.' }], howTo: [{ name: 'Recopier la prescription', text: 'Saisissez le nom, la première date et heure et la durée exactement comme indiqués par l équipe vétérinaire.' }, { name: 'Choisir le rythme', text: 'Sélectionnez un intervalle en heures ou saisissez les heures quotidiennes prescrites.' }, { name: 'Relire le tableau', text: 'Vérifiez le premier jour et la prochaine prise non cochée avant de l utiliser comme rappel.' }, { name: 'Cocher chaque prise', text: 'Touchez une ligne après administration et gardez les consignes originales à proximité.' }] };
|
|
5
|
+
|
|
6
|
+
export const content = createContent(copy);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
import { copy as en } from './en';
|
|
3
|
+
|
|
4
|
+
const copy: MedicationCopy = { ...en, slug: 'jadwal-obat-hewan', title: 'Jadwal Obat Hewan Peliharaan', description: 'Ubah resep dokter hewan menjadi jadwal lokal yang jelas dengan dosis berikutnya dan daftar centang.', dateLocale: 'id-ID', medicationNameLabel: 'Nama obat', medicationNamePlaceholder: 'Contoh: antibiotik resep', startDateLabel: 'Tanggal pemberian pertama', startTimeLabel: 'Waktu pemberian pertama', scheduleModeLabel: 'Bagaimana jadwalnya ditulis?', intervalMode: 'Setiap beberapa jam', timesMode: 'Waktu tetap setiap hari', intervalHoursLabel: 'Jam di antara pemberian', timesLabel: 'Waktu harian', timesHint: 'Gunakan format 24 jam dan pisahkan dengan koma: 08:00, 16:00, 00:00.', durationLabel: 'Lama pengobatan', durationUnit: 'hari', instructionsLabel: 'Catatan resep', instructionsPlaceholder: 'Makanan, cara pemberian, atau petunjuk dokter hewan lain', reset: 'Hapus jadwal', scheduleTitle: 'Papan pemberian', nextDoseLabel: 'Pemberian berikutnya yang belum dicentang', noNextDose: 'Semua pemberian yang tampil sudah dicentang', completedCount: '{done} dari {total} dicentang', markDone: 'Tandai sudah diberikan', markUndone: 'Tandai belum diberikan', completed: 'Selesai', upcoming: 'Berikutnya', due: 'Periksa', emptySchedule: 'Lengkapi kolom untuk melihat jadwal.', invalidInput: 'Periksa nama, tanggal, waktu, lama, dan aturan jadwal.', safetyTitle: 'Ikuti resep apa adanya', safetyText: 'Ini hanya alat pengingat dan pencatatan. Alat ini tidak memilih obat, mengubah dosis, atau menggantikan dokter hewan. Hubungi klinik jika pemberian terlambat, terlupa, atau dimuntahkan.', methodText: 'Jadwal interval dimulai dari tanggal dan waktu pertama, lalu menambahkan jam yang dipilih. Waktu harian diulang pada setiap hari pengobatan. Data tetap di perangkat ini sampai Anda menghapusnya.', scheduleIllustration: 'Jadwal harian bergambar tangan di samping kucing dan anjing yang tenang', summary: ['Jadikan resep yang ada sebagai jadwal yang mudah dibaca.', 'Pilih interval atau waktu tetap setiap hari.', 'Centang pemberian tanpa mengirim data hewan.', 'Simpan nama obat dan catatan di dekat rutinitas.'], seoTitle1: 'Pengingat praktis untuk resep dokter hewan yang sudah ada', seoIntro: 'Setelah kunjungan klinik, pemilik sering harus menyatukan label, lembar pulang, dan beberapa pemberian di rumah. Keputusan pengobatan dibuat oleh dokter hewan; alat ini hanya merapikan instruksi yang sudah diterima. Nama obat, waktu pertama, pola, lama hari, dan catatan membentuk daftar kronologis untuk pagi yang sibuk, malam, atau pergantian pengasuh.', seoTitle2: 'Dua cara menuliskan waktu', seoMethod: 'Pilih interval jika label mengatakan, misalnya, setiap delapan jam. Daftar akan berlanjut melewati tengah malam. Pilih waktu harian jika resep menyebut pagi dan malam atau jam tertentu. Hari pertama mengikuti pemberian pertama dan tidak menambahkan waktu lebih awal. Lama pengobatan adalah jendela perencanaan, bukan tafsiran resep. Hasilnya berisi waktu, bukan jumlah obat.', seoTitle3: 'Daftar centang bukan nasihat medis', seoSafety: 'Simpan kemasan, dokumen klinik, dan nomor teleponnya. Halaman ini tidak mengetahui hewan, konsentrasi, cara pemberian, interaksi, atau responsnya. Tanyakan klinik sebelum mengambil keputusan tentang pemberian yang terlewat. Pencatatan tetap dapat mencegah pemberian ganda dan memperjelas tugas berikutnya. Penyimpanan lokal dapat dihapus kapan saja.', tipTitle: 'Sebelum mulai', tipText: 'Salin frekuensi dan lama pengobatan dari resep, jangan menebak. Bandingkan hari pertama yang dibuat dengan label.', faq: [{ question: 'Apakah alat ini menghitung jumlah obat?', answer: 'Tidak. Alat ini hanya menyusun waktu yang Anda masukkan dari resep yang ada dan tidak menghitung, mengubah, atau menyarankan jumlah.' }, { question: 'Apakah waktu setelah tengah malam didukung?', answer: 'Ya. Interval berlanjut ke hari lokal berikutnya dan waktu harian dapat berisi 00:00.' }, { question: 'Apa yang dilakukan jika pemberian terlambat atau terlewat?', answer: 'Hubungi dokter hewan atau klinik yang memberi resep. Daftar mencatat keadaan, tetapi tidak menentukan apakah harus mengejar, melewati, atau memindahkan pemberian.' }], howTo: [{ name: 'Salin resep', text: 'Masukkan nama, waktu pertama, dan lama pengobatan sesuai petunjuk dokter hewan.' }, { name: 'Pilih pola waktu', text: 'Gunakan interval jam atau waktu harian dari resep.' }, { name: 'Periksa papan', text: 'Pastikan hari pertama dan pemberian berikutnya yang belum dicentang.' }, { name: 'Centang setiap pemberian', text: 'Ketuk baris setelah obat diberikan dan simpan petunjuk asli di dekat Anda.' }] };
|
|
5
|
+
|
|
6
|
+
export const content = createContent(copy);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
import { copy as en } from './en';
|
|
3
|
+
|
|
4
|
+
const copy: MedicationCopy = { ...en, slug: 'agenda-medicinali-animali', title: 'Agenda dei medicinali per animali', description: 'Trasforma una terapia prescritta dal veterinario in un calendario locale chiaro, con prossime somministrazioni e spunte.', dateLocale: 'it', medicationNameLabel: 'Nome del medicinale', medicationNamePlaceholder: 'Per esempio: antibiotico prescritto', startDateLabel: 'Data della prima somministrazione', startTimeLabel: 'Ora della prima somministrazione', scheduleModeLabel: 'Come è indicata la frequenza?', intervalMode: 'Ogni alcune ore', timesMode: 'Orari fissi ogni giorno', intervalHoursLabel: 'Ore tra le somministrazioni', timesLabel: 'Orari giornalieri', timesHint: 'Usa orari sulle 24 ore separati da virgole: 08:00, 16:00, 00:00.', durationLabel: 'Durata della terapia', durationUnit: 'giorni', instructionsLabel: 'Note della prescrizione', instructionsPlaceholder: 'Cibo, modalità o altre istruzioni del veterinario', reset: 'Cancella questo calendario', scheduleTitle: 'Tabella delle somministrazioni', nextDoseLabel: 'Prossima somministrazione non spuntata', noNextDose: 'Tutte le somministrazioni sono spuntate', completedCount: '{done} di {total} spuntate', markDone: 'Segna come somministrata', markUndone: 'Segna come non somministrata', completed: 'Fatta', upcoming: 'In arrivo', due: 'Da controllare', emptySchedule: 'Completa i campi per vedere la tabella.', invalidInput: 'Controlla nome, data, ora, durata e frequenza.', safetyTitle: 'Segui la prescrizione senza modificarla', safetyText: 'È un aiuto per ricordare e registrare. Non sceglie medicinali, non cambia dosi e non sostituisce il veterinario. Per una somministrazione in ritardo, dimenticata o vomitata, contatta la clinica.', methodText: 'Con un intervallo il calendario parte dal primo momento e aggiunge le ore indicate. Con orari giornalieri ripete gli orari inseriti per ogni giorno. I dati restano su questo dispositivo finché non li cancelli.', scheduleIllustration: 'Calendario disegnato a mano accanto a un gatto e un cane tranquilli', summary: ['Rendi leggibile una prescrizione già disponibile.', 'Scegli intervalli oppure orari fissi della giornata.', 'Spunta le somministrazioni senza inviare dati.', 'Tieni nome e note accanto alla routine.'], seoTitle1: 'Un promemoria pratico per una terapia già prescritta', seoIntro: 'Dopo la visita, il compito difficile è coordinare etichetta, foglio della clinica e più somministrazioni in casa. La scelta del farmaco spetta al veterinario: questa agenda organizza soltanto le istruzioni che hai già ricevuto. Inserisci nome, primo momento locale, frequenza, durata e note facoltative per ottenere una lista cronologica facile da leggere al mattino, di notte o durante il passaggio di consegne.', seoTitle2: 'Due modi per indicare gli orari', seoMethod: 'Scegli un intervallo se la prescrizione dice, per esempio, ogni otto ore. La sequenza continua correttamente oltre la mezzanotte. Scegli gli orari giornalieri se sono indicati mattina e sera o orari precisi. Nel primo giorno non viene aggiunto in automatico un orario precedente alla prima somministrazione. La durata è una finestra di pianificazione, non un interpretazione della ricetta. Il risultato mostra orari, non quantità.', seoTitle3: 'Una checklist non è un consiglio medico', seoSafety: 'Conserva confezione, foglio della clinica e recapiti. La pagina non conosce animale, concentrazione, via di somministrazione, interazioni o risposta. Per una dose dimenticata chiedi alla clinica prima di decidere. Spuntare le righe può comunque evitare doppie somministrazioni e rendere chiara la prossima azione. Il salvataggio locale è una comodità che puoi cancellare.', tipTitle: 'Prima di iniziare', tipText: 'Copia frequenza e durata dalla prescrizione senza stimarle. Controlla il primo giorno generato insieme all etichetta.', faq: [{ question: 'Calcola la quantità del medicinale?', answer: 'No. Organizza solo gli orari inseriti da una prescrizione esistente e non calcola, modifica o consiglia quantità.' }, { question: 'Gestisce gli orari dopo mezzanotte?', answer: 'Sì. Gli intervalli passano al giorno locale successivo e gli orari giornalieri possono includere 00:00.' }, { question: 'Cosa faccio se una somministrazione è in ritardo?', answer: 'Contatta il veterinario o la clinica che ha prescritto la terapia. La checklist registra lo stato, ma non decide se recuperare, saltare o spostare.' }], howTo: [{ name: 'Trascrivi la prescrizione', text: 'Inserisci nome, primo momento e durata esattamente come indicato dal veterinario.' }, { name: 'Scegli la frequenza', text: 'Usa un intervallo in ore oppure gli orari giornalieri prescritti.' }, { name: 'Controlla il quadro', text: 'Verifica il primo giorno e la prossima riga non spuntata.' }, { name: 'Spunta le righe', text: 'Tocca una riga dopo la somministrazione e conserva le istruzioni originali.' }] };
|
|
5
|
+
|
|
6
|
+
export const content = createContent(copy);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
import { copy as en } from './en';
|
|
3
|
+
|
|
4
|
+
const copy: MedicationCopy = { ...en, slug: 'pet-medication-schedule-planner-ja', title: 'ペットの投薬スケジュール作成', description: '獣医師から処方された投薬内容を、次の投薬と確認欄のある見やすい端末内スケジュールに整理します。', dateLocale: 'ja-JP', medicationNameLabel: '薬の名前', medicationNamePlaceholder: '例:処方された抗生剤', startDateLabel: '最初に飲ませる日', startTimeLabel: '最初に飲ませる時刻', scheduleModeLabel: '処方の時間指定方法', intervalMode: '数時間ごと', timesMode: '毎日の固定時刻', intervalHoursLabel: '投薬間隔(時間)', timesLabel: '毎日の時刻', timesHint: '24時間表記でカンマ区切りにします。例:08:00, 16:00, 00:00', durationLabel: '治療期間', durationUnit: '日', instructionsLabel: '処方時のメモ', instructionsPlaceholder: '食事、与え方、その他の獣医師の指示', reset: '予定を消去', scheduleTitle: '投薬ボード', nextDoseLabel: '次の未確認の投薬', noNextDose: '表示された投薬はすべて確認済みです', completedCount: '{done} / {total} 件を確認', markDone: '投薬済みにする', markUndone: '未投薬に戻す', completed: '確認済み', upcoming: '次回', due: '確認', emptySchedule: '項目を入力すると投薬ボードが表示されます。', invalidInput: '薬名、日付、時刻、期間、時間設定を確認してください。', safetyTitle: '処方内容をそのまま使う', safetyText: 'これは記録とリマインダーの補助ツールです。薬や量を選んだり変更したり、獣医師の助言に代わったりしません。遅れ、飲み忘れ、吐き戻しがあれば処方した動物病院に相談してください。', methodText: '間隔指定では最初の日付と時刻から指定時間を順に加えます。毎日の時刻指定では入力した時刻を治療期間の各日に繰り返します。消去するまで情報はこの端末に保存されます。', scheduleIllustration: '穏やかな猫と犬のそばに描かれた一日の投薬予定表', summary: ['手元の処方内容を読みやすい予定表に整理します。', '時間間隔または毎日の固定時刻を選べます。', 'ペットの情報を送信せず、投薬を確認できます。', '薬名と注意事項を予定表の横に残せます。'], seoTitle1: 'すでにある獣医師の処方を確認しやすくする予定表', seoIntro: '動物病院から帰った後は、薬のラベルや説明書、複数回の投薬を家庭で管理する必要があります。何を使うかを決めるのは獣医師です。このツールは、すでに受け取った内容を日付順のチェックリストに整理します。薬名、最初の日時、間隔または時刻、日数、メモを入力すれば、朝の忙しい時間や夜間、家族間の引き継ぎでも確認しやすくなります。', seoTitle2: '時間の入力方法は二つ', seoMethod: '「8時間ごと」のような指示なら間隔を選びます。日付が変わっても次の予定を続けて表示します。「朝と夜」や具体的な時刻なら毎日の時刻を選びます。最初の日は最初の投薬時刻より前の予定を自動で追加しません。期間は表示範囲を決めるだけで、処方内容を解釈するものではありません。表示するのは時刻であり、薬の量ではありません。', seoTitle3: 'チェックリストは医療助言ではありません', seoSafety: '薬の箱、病院の説明書、連絡先を手元に置いてください。このページは動物の状態、濃度、投与方法、相互作用、反応を判断できません。飲み忘れなどで迷ったときは、決める前に病院へ連絡してください。確認欄は二重投与を防ぎ、次の担当者に状況を伝える助けになります。端末内の保存はいつでも消去できます。', tipTitle: '始める前に', tipText: '頻度と期間は処方からそのまま写し、推測しないでください。作成された最初の日をラベルと照合してください。', faq: [{ question: '投薬量を計算できますか?', answer: 'いいえ。既存の処方から入力した時刻を整理するだけで、量を計算、変更、提案することはありません。' }, { question: '日付をまたぐ投薬に対応していますか?', answer: 'はい。間隔指定は地域の日付をまたいで続き、毎日の時刻には00:00も入力できます。' }, { question: '投薬が遅れたり抜けたりしたらどうしますか?', answer: '処方した獣医師または動物病院に相談してください。このリストは状況を記録しますが、追加、スキップ、変更を判断しません。' }], howTo: [{ name: '処方内容を入力する', text: '獣医師の指示どおりに薬名、最初の日時、期間を入力します。' }, { name: '時間設定を選ぶ', text: '時間間隔か、処方にある毎日の時刻を選びます。' }, { name: '最初の日を確認する', text: '最初の日と次の未確認の投薬を、使い始める前に確認します。' }, { name: '投薬を確認する', text: '与えた後に行をタップし、元の説明書も近くに置いてください。' }] };
|
|
5
|
+
|
|
6
|
+
export const content = createContent(copy);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
import { copy as en } from './en';
|
|
3
|
+
|
|
4
|
+
const copy: MedicationCopy = { ...en, slug: 'pet-medication-schedule-planner-ko', title: '반려동물 투약 일정표', description: '수의사가 처방한 투약 계획을 다음 투약 시간과 확인 표시가 있는 보기 쉬운 일정으로 정리합니다.', dateLocale: 'ko-KR', medicationNameLabel: '약 이름', medicationNamePlaceholder: '예: 처방받은 항생제', startDateLabel: '첫 투약 날짜', startTimeLabel: '첫 투약 시간', scheduleModeLabel: '처방 시간은 어떻게 정해져 있나요?', intervalMode: '몇 시간마다', timesMode: '매일 정해진 시간', intervalHoursLabel: '투약 사이 시간', timesLabel: '매일의 시간', timesHint: '24시간 형식으로 쉼표를 사용하세요. 예: 08:00, 16:00, 00:00', durationLabel: '치료 기간', durationUnit: '일', instructionsLabel: '처방 메모', instructionsPlaceholder: '식사, 투여 방법 또는 수의사의 다른 지시', reset: '일정 지우기', scheduleTitle: '투약 보드', nextDoseLabel: '아직 확인하지 않은 다음 투약', noNextDose: '표시된 투약을 모두 확인했습니다', completedCount: '{total}개 중 {done}개 확인', markDone: '투약 완료로 표시', markUndone: '미완료로 되돌리기', completed: '완료', upcoming: '예정', due: '확인', emptySchedule: '항목을 입력하면 투약 보드가 나타납니다.', invalidInput: '약 이름, 날짜, 시간, 기간과 시간 규칙을 확인하세요.', safetyTitle: '처방 내용을 그대로 따르세요', safetyText: '이 도구는 기록과 알림을 돕습니다. 약을 선택하거나 용량을 바꾸지 않으며 수의사의 조언을 대신하지 않습니다. 늦었거나 잊었거나 토한 투약은 처방한 병원에 문의하세요.', methodText: '간격 방식은 첫 날짜와 시간에서 시작해 선택한 시간을 더합니다. 매일의 시간 방식은 치료 기간 동안 입력한 시간을 반복합니다. 삭제하기 전까지 정보는 이 기기에 남습니다.', scheduleIllustration: '차분한 고양이와 강아지 옆에 그려진 하루 투약표', summary: ['기존 처방을 읽기 쉬운 일정으로 정리합니다.', '시간 간격이나 매일의 고정 시간을 선택합니다.', '반려동물 정보를 보내지 않고 투약을 확인합니다.', '약 이름과 메모를 일정 옆에 보관합니다.'], seoTitle1: '이미 받은 수의사 처방을 위한 실용적인 알림', seoIntro: '동물병원에 다녀온 뒤에는 약 라벨, 안내문, 여러 번의 투약을 집에서 맞춰야 합니다. 어떤 치료를 할지는 수의사가 결정하며, 이 도구는 이미 받은 지시만 순서대로 정리합니다. 약 이름, 첫 시간, 반복 방식, 기간과 메모를 입력하면 바쁜 아침, 밤중, 보호자 교대 때 확인할 수 있는 시간순 목록이 만들어집니다.', seoTitle2: '시간을 입력하는 두 가지 방법', seoMethod: '라벨에 8시간마다처럼 적혀 있으면 간격 방식을 사용합니다. 자정이 지나도 다음 날짜로 이어집니다. 아침과 저녁 또는 특정 시각이 적혀 있으면 매일의 시간을 사용합니다. 첫날에는 첫 투약보다 이른 시간이 자동으로 추가되지 않습니다. 기간은 계획 범위를 정할 뿐 처방을 해석하지 않으며, 결과에는 약의 양이 아니라 시간이 표시됩니다.', seoTitle3: '체크리스트는 의료 조언이 아닙니다', seoSafety: '약 포장, 병원 안내문과 연락처를 가까이 두세요. 이 페이지는 동물의 상태, 농도, 투여 경로, 상호작용이나 반응을 알 수 없습니다. 투약을 놓쳤다면 먼저 병원에 문의하세요. 확인 표시는 중복 투약을 줄이고 다음 담당자에게 상황을 알려주는 데 도움이 됩니다. 기기 내 저장 내용은 언제든 삭제할 수 있습니다.', tipTitle: '시작하기 전에', tipText: '횟수와 기간은 처방에서 그대로 옮기고 추측하지 마세요. 만들어진 첫날을 약 라벨과 대조하세요.', faq: [{ question: '투약량을 계산하나요?', answer: '아니요. 기존 처방에서 입력한 시간을 정리할 뿐이며 양을 계산, 변경 또는 추천하지 않습니다.' }, { question: '자정을 넘기는 투약도 표시하나요?', answer: '네. 간격 방식은 다음 지역 날짜로 이어지고, 매일의 시간에는 00:00을 넣을 수 있습니다.' }, { question: '투약이 늦거나 빠졌다면 어떻게 하나요?', answer: '처방한 수의사나 병원에 문의하세요. 목록은 상태를 기록하지만 추가 투약, 건너뛰기 또는 변경을 결정하지 않습니다.' }], howTo: [{ name: '처방을 입력하기', text: '수의사의 지시대로 약 이름, 첫 시간과 기간을 입력합니다.' }, { name: '시간 방식을 선택하기', text: '시간 간격 또는 처방에 적힌 매일의 시간을 선택합니다.' }, { name: '첫날 확인하기', text: '사용하기 전에 첫날과 다음 미확인 투약을 살펴봅니다.' }, { name: '투약을 표시하기', text: '투약한 뒤 행을 누르고 원래 안내문을 곁에 둡니다.' }] };
|
|
5
|
+
|
|
6
|
+
export const content = createContent(copy);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContent, type MedicationCopy } from './content';
|
|
2
|
+
import { copy as en } from './en';
|
|
3
|
+
|
|
4
|
+
const copy: MedicationCopy = { ...en, slug: 'medicatieschema-huisdier', title: 'Medicatieschema voor huisdieren', description: 'Maak van een voorgeschreven dierenartsbehandeling een duidelijk lokaal schema met volgende giften en afvinklijst.', dateLocale: 'nl', medicationNameLabel: 'Naam van het medicijn', medicationNamePlaceholder: 'Bijvoorbeeld: voorgeschreven antibioticum', startDateLabel: 'Datum eerste gift', startTimeLabel: 'Tijd eerste gift', scheduleModeLabel: 'Hoe is het ritme voorgeschreven?', intervalMode: 'Elke paar uur', timesMode: 'Vaste tijden per dag', intervalHoursLabel: 'Uren tussen giften', timesLabel: 'Dagelijkse tijden', timesHint: 'Gebruik 24-uurs tijden met komma s ertussen, zoals 08:00, 16:00, 00:00.', durationLabel: 'Duur van de behandeling', durationUnit: 'dagen', instructionsLabel: 'Notities van het voorschrift', instructionsPlaceholder: 'Voer, toediening of andere aanwijzingen van de dierenarts', reset: 'Schema wissen', scheduleTitle: 'Jouw doseerbord', nextDoseLabel: 'Volgende niet-afgevinkte gift', noNextDose: 'Alle getoonde giften zijn afgevinkt', completedCount: '{done} van {total} afgevinkt', markDone: 'Gift als gegeven markeren', markUndone: 'Gift weer open markeren', completed: 'Gedaan', upcoming: 'Binnenkort', due: 'Controleren', emptySchedule: 'Vul de velden in om het doseerbord te zien.', invalidInput: 'Controleer naam, datum, tijd, duur en ritme.', safetyTitle: 'Volg het voorschrift precies', safetyText: 'Dit is een hulpmiddel voor herinneren en registreren. Het kiest geen medicijn, verandert geen dosis en vervangt geen dierenarts. Bel de voorschrijvende praktijk bij een late, vergeten of uitgebraakte gift.', methodText: 'Bij intervallen begint het schema op de eerste datum en tijd en worden de gekozen uren toegevoegd. Dagelijkse tijden worden op elke behandeldag herhaald. Alles blijft op dit apparaat tot je het wist.', scheduleIllustration: 'Met de hand getekend dagschema naast een rustige kat en hond', summary: ['Maak een bestaand voorschrift overzichtelijk.', 'Kies intervallen of vaste tijden per dag.', 'Vink giften af zonder gegevens te versturen.', 'Houd naam en opmerkingen naast de routine.'], seoTitle1: 'Een praktisch overzicht voor een bestaand dierenartsvoorschrift', seoIntro: 'Na een bezoek aan de praktijk moeten verzorgers vaak een etiket, ontslaginformatie en meerdere giften thuis combineren. De dierenarts bepaalt de behandeling; deze planner zet alleen bestaande instructies in een leesbare volgorde. Met naam, eerste lokaal moment, ritme, duur en optionele notities ontstaat een chronologische lijst voor drukke ochtenden, nachten en overdrachten tussen verzorgers.', seoTitle2: 'Twee manieren om tijden te beschrijven', seoMethod: 'Gebruik een interval als het etiket bijvoorbeeld iedere acht uur zegt. De lijst loopt dan over middernacht door. Gebruik dagelijkse tijden als ochtend en avond of concrete kloktijden zijn voorgeschreven. Op de eerste dag wordt geen eerdere tijd stil toegevoegd. De duur is een planningsvenster en geen interpretatie van het voorschrift. Het resultaat bevat tijden, geen hoeveelheden.', seoTitle3: 'Een checklist is geen medisch advies', seoSafety: 'Bewaar verpakking, praktijkinformatie en telefoonnummer in de buurt. Deze pagina kent het dier, de concentratie, toedieningsweg, interacties en reactie niet. Vraag de praktijk om advies bij een gemiste of twijfelachtige gift. Afvinken kan wel dubbele giften voorkomen en maakt de volgende taak zichtbaar. Lokale opslag is alleen gemak en kan altijd worden gewist.', tipTitle: 'Voor je begint', tipText: 'Neem frequentie en duur letterlijk over van het voorschrift. Controleer de eerste gegenereerde dag naast het etiket.', faq: [{ question: 'Berekent deze planner de hoeveelheid?', answer: 'Nee. Hij ordent alleen tijden uit een bestaand voorschrift en berekent, verandert of adviseert geen hoeveelheden.' }, { question: 'Werkt het schema over middernacht?', answer: 'Ja. Intervallen gaan door op de volgende lokale dag en dagelijkse tijden mogen 00:00 bevatten.' }, { question: 'Wat doe ik bij een gemiste gift?', answer: 'Neem contact op met de dierenarts of praktijk die het voorschrift gaf. De checklist noteert de status, maar beslist niet over inhalen, overslaan of verschuiven.' }], howTo: [{ name: 'Neem het voorschrift over', text: 'Vul naam, eerste moment en duur precies in zoals de praktijk ze gaf.' }, { name: 'Kies het ritme', text: 'Kies een interval in uren of de voorgeschreven dagelijkse tijden.' }, { name: 'Controleer het bord', text: 'Bekijk de eerste dag en de volgende open gift voordat je het als herinnering gebruikt.' }, { name: 'Vink giften af', text: 'Tik de rij aan nadat je de gift hebt gegeven en houd de originele instructies erbij.' }] };
|
|
5
|
+
|
|
6
|
+
export const content = createContent(copy);
|