@jjlmoya/utils-chrono 1.5.0 → 1.6.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 (36) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -0
  3. package/src/entries.ts +4 -1
  4. package/src/index.ts +1 -0
  5. package/src/tests/locale_completeness.test.ts +1 -1
  6. package/src/tests/tool_validation.test.ts +1 -1
  7. package/src/tool/service-interval-tracker/bibliography.astro +16 -0
  8. package/src/tool/service-interval-tracker/bibliography.ts +12 -0
  9. package/src/tool/service-interval-tracker/client.ts +233 -0
  10. package/src/tool/service-interval-tracker/component.astro +50 -0
  11. package/src/tool/service-interval-tracker/components/AddEditModal.astro +75 -0
  12. package/src/tool/service-interval-tracker/components/DashboardHeader.astro +14 -0
  13. package/src/tool/service-interval-tracker/components/EmptyState.astro +26 -0
  14. package/src/tool/service-interval-tracker/entry.ts +56 -0
  15. package/src/tool/service-interval-tracker/helpers.ts +82 -0
  16. package/src/tool/service-interval-tracker/i18n/de.ts +117 -0
  17. package/src/tool/service-interval-tracker/i18n/en.ts +170 -0
  18. package/src/tool/service-interval-tracker/i18n/es.ts +117 -0
  19. package/src/tool/service-interval-tracker/i18n/fr.ts +98 -0
  20. package/src/tool/service-interval-tracker/i18n/id.ts +89 -0
  21. package/src/tool/service-interval-tracker/i18n/it.ts +88 -0
  22. package/src/tool/service-interval-tracker/i18n/ja.ts +88 -0
  23. package/src/tool/service-interval-tracker/i18n/ko.ts +88 -0
  24. package/src/tool/service-interval-tracker/i18n/nl.ts +88 -0
  25. package/src/tool/service-interval-tracker/i18n/pl.ts +88 -0
  26. package/src/tool/service-interval-tracker/i18n/pt.ts +88 -0
  27. package/src/tool/service-interval-tracker/i18n/ru.ts +88 -0
  28. package/src/tool/service-interval-tracker/i18n/sv.ts +88 -0
  29. package/src/tool/service-interval-tracker/i18n/tr.ts +88 -0
  30. package/src/tool/service-interval-tracker/i18n/zh.ts +88 -0
  31. package/src/tool/service-interval-tracker/index.ts +11 -0
  32. package/src/tool/service-interval-tracker/renderer.ts +91 -0
  33. package/src/tool/service-interval-tracker/seo.astro +16 -0
  34. package/src/tool/service-interval-tracker/service-interval-tracker.css +767 -0
  35. package/src/tool/service-interval-tracker/utils.ts +58 -0
  36. package/src/tools.ts +2 -0
@@ -0,0 +1,88 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { ServiceIntervalTrackerUI } from '../entry';
3
+ import { bibliography } from '../bibliography';
4
+
5
+ export const content: ToolLocaleContent<ServiceIntervalTrackerUI> = {
6
+ slug: 'klockservice-intervalls-tracker',
7
+ title: 'Klockservice Intervalls Tracker',
8
+ description: 'Registrera och hantera serviceintervaller för dina klockor. Se direkt när det är dags för underhåll eller täthetsservice.',
9
+ ui: {
10
+ title: 'Service tracker',
11
+ addWatch: 'Lägg till klocka',
12
+ editWatch: 'Redigera klocka',
13
+ cancel: 'Avbryt',
14
+ save: 'Spara',
15
+ deleteWatch: 'Ta bort',
16
+ confirmDelete: 'Ta bort klockan från samlingen?',
17
+ emptyTitle: 'Din samling är tom',
18
+ emptyDesc: 'Håll reda på när dina klockor behöver service för att hålla dem i perfekt skick.',
19
+ emptyAction: 'Lägg till din första klocka',
20
+ healthy: 'ok',
21
+ due: 'snart dags',
22
+ overdue: 'försenad',
23
+ nameLabel: 'Klockans namn',
24
+ namePlaceholder: 't.ex. Rolex Submariner',
25
+ movementLabel: 'Urverkstyp',
26
+ movementAuto: 'Automatisk',
27
+ movementManual: 'Manuell',
28
+ movementQuartz: 'Quartz',
29
+ movementKinetic: 'Kinetic',
30
+ dateLabel: 'Senaste servicedatum',
31
+ neverServiced: 'Ny eller aldrig servad',
32
+ lastServiceLabel: 'Senaste service',
33
+ nextServiceLabel: 'Nästa service',
34
+ serviced: 'Servad den',
35
+ newWatch: 'Ny',
36
+ years: 'år',
37
+ collectionHealth: 'Samlingens status',
38
+ },
39
+ seo: [
40
+ { type: 'title', text: 'Service Tracker för Armbandsur: Håll koll på oljor och packningar', level: 2 },
41
+ ],
42
+ faq: [
43
+ {
44
+ question: 'Hur ofta ska en automatisk klocka servas?',
45
+ answer: 'De flesta tillverkare rekommenderar service vart 3:e till 5:e år. I verkligheten går många klockor fint i 5-7 år innan tidhållningen påverkas.',
46
+ }
47
+ ],
48
+ bibliography,
49
+ howTo: [
50
+ { name: 'Registrera klocka', text: 'Ange klockans modell, urverk samt senaste servicedatum.' }
51
+ ],
52
+ schemas: [
53
+ {
54
+ '@context': 'https://schema.org',
55
+ '@type': 'FAQPage',
56
+ 'mainEntity': [
57
+ {
58
+ '@type': 'Question',
59
+ 'name': 'Hur ofta ska en automatisk klocka servas?',
60
+ 'acceptedAnswer': {
61
+ '@type': 'Answer',
62
+ 'text': 'De flesta tillverkare rekommenderar service vart 3:e till 5:e år.'
63
+ }
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ '@context': 'https://schema.org',
69
+ '@type': 'SoftwareApplication',
70
+ 'name': 'Klockservice Intervalls Tracker',
71
+ 'operatingSystem': 'All',
72
+ 'applicationCategory': 'LifestyleApplication',
73
+ 'browserRequirements': 'Requires HTML5. Requires JavaScript.'
74
+ } as any,
75
+ {
76
+ '@context': 'https://schema.org',
77
+ '@type': 'HowTo',
78
+ 'name': 'Klockservice Intervalls Tracker',
79
+ 'step': [
80
+ {
81
+ '@type': 'HowToStep',
82
+ 'name': 'Registrera klocka',
83
+ 'text': 'Ange klockans modell, urverk samt senaste servicedatum.'
84
+ }
85
+ ]
86
+ }
87
+ ]
88
+ };
@@ -0,0 +1,88 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { ServiceIntervalTrackerUI } from '../entry';
3
+ import { bibliography } from '../bibliography';
4
+
5
+ export const content: ToolLocaleContent<ServiceIntervalTrackerUI> = {
6
+ slug: 'saat-bakim-takip',
7
+ title: 'Saat Bakım ve Servis Zamanı Takip Aracı',
8
+ description: 'Saat koleksiyonunuzun bakım aralıklarını takip edin ve yönetin. Hangi saatin bakım zamanının geldiğini bir bakışta görün.',
9
+ ui: {
10
+ title: 'Bakım Takip',
11
+ addWatch: 'Saat Ekle',
12
+ editWatch: 'Saati Düzenle',
13
+ cancel: 'İptal',
14
+ save: 'Kaydet',
15
+ deleteWatch: 'Kaldır',
16
+ confirmDelete: 'Bu saati koleksiyonunuzdan kaldırmak istiyor musunuz?',
17
+ emptyTitle: 'Koleksiyonunuz boş',
18
+ emptyDesc: 'Saatlerinizin bakım zamanlarını kaydedin ve periyodik servis sürelerini kaçırmayın.',
19
+ emptyAction: 'İlk saatinizi ekleyin',
20
+ healthy: 'sorunsuz',
21
+ due: 'yaklaşıyor',
22
+ overdue: 'gecikmiş',
23
+ nameLabel: 'Saat adı',
24
+ namePlaceholder: 'örn. Rolex Submariner',
25
+ movementLabel: 'Mekanizma türü',
26
+ movementAuto: 'Otomatik',
27
+ movementManual: 'Kurmalı',
28
+ movementQuartz: 'Pilli',
29
+ movementKinetic: 'Kinetik',
30
+ dateLabel: 'Son bakım tarihi',
31
+ neverServiced: 'Yeni veya hiç bakım görmemiş',
32
+ lastServiceLabel: 'Son bakım',
33
+ nextServiceLabel: 'Sonraki bakım',
34
+ serviced: 'Bakım yapıldı',
35
+ newWatch: 'Yeni',
36
+ years: 'yıl',
37
+ collectionHealth: 'Koleksiyon sağlığı',
38
+ },
39
+ seo: [
40
+ { type: 'title', text: 'Saat Servis Takip Programı: Saatlerinizi uzun ömürlü kullanın', level: 2 },
41
+ ],
42
+ faq: [
43
+ {
44
+ question: 'Otomatik bir saat ne sıklıkla bakıma götürülmelidir?',
45
+ answer: 'Üreticiler genellikle otomatik saatler için 3 ila 5 yılda bir bakım önerir. Pratikte birçok saat 5-7 yıl sorunsuz çalışabilir.',
46
+ }
47
+ ],
48
+ bibliography,
49
+ howTo: [
50
+ { name: 'Saat ekleyin', text: 'Saatin adını, mekanizma türünü ve son bakım tarihini girerek kaydı tamamlayın.' }
51
+ ],
52
+ schemas: [
53
+ {
54
+ '@context': 'https://schema.org',
55
+ '@type': 'FAQPage',
56
+ 'mainEntity': [
57
+ {
58
+ '@type': 'Question',
59
+ 'name': 'Otomatik bir saat ne sıklıkla bakıma götürülmelidir?',
60
+ 'acceptedAnswer': {
61
+ '@type': 'Answer',
62
+ 'text': 'Üreticiler genellikle otomatik saatler için 3 ila 5 yılda bir bakım önerir.'
63
+ }
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ '@context': 'https://schema.org',
69
+ '@type': 'SoftwareApplication',
70
+ 'name': 'Saat Bakım ve Servis Zamanı Takip Aracı',
71
+ 'operatingSystem': 'All',
72
+ 'applicationCategory': 'LifestyleApplication',
73
+ 'browserRequirements': 'Requires HTML5. Requires JavaScript.'
74
+ } as any,
75
+ {
76
+ '@context': 'https://schema.org',
77
+ '@type': 'HowTo',
78
+ 'name': 'Saat Bakım ve Servis Zamanı Takip Aracı',
79
+ 'step': [
80
+ {
81
+ '@type': 'HowToStep',
82
+ 'name': 'Saat ekleyin',
83
+ 'text': 'Saatin adını, mekanizma türünü ve son bakım tarihini girin.'
84
+ }
85
+ ]
86
+ }
87
+ ]
88
+ };
@@ -0,0 +1,88 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { ServiceIntervalTrackerUI } from '../entry';
3
+ import { bibliography } from '../bibliography';
4
+
5
+ export const content: ToolLocaleContent<ServiceIntervalTrackerUI> = {
6
+ slug: 'service-interval-tracker',
7
+ title: '手表保养周期与维护记录工具',
8
+ description: '追踪与管理您手表收藏的保养周期。一目了然得知每只手表何时需要进行洗油与维护。',
9
+ ui: {
10
+ title: '保养记录器',
11
+ addWatch: '添加腕表',
12
+ editWatch: '编辑腕表',
13
+ cancel: '取消',
14
+ save: '保存',
15
+ deleteWatch: '删除',
16
+ confirmDelete: '确定要从收藏中删除这只腕表吗?',
17
+ emptyTitle: '您的收藏为空',
18
+ emptyDesc: '记录您手表的洗油和保养时间,避免因机芯缺油造成齿轮磨损。',
19
+ emptyAction: '添加您的第一只表',
20
+ healthy: '状态良好',
21
+ due: '临近保养',
22
+ overdue: '逾期未保养',
23
+ nameLabel: '腕表名称',
24
+ namePlaceholder: '例如:劳力士水鬼',
25
+ movementLabel: '机芯类型',
26
+ movementAuto: '自动机械',
27
+ movementManual: '手动机械',
28
+ movementQuartz: '石英',
29
+ movementKinetic: '人动电能',
30
+ dateLabel: '上次保养日期',
31
+ neverServiced: '全新或从未保养过',
32
+ lastServiceLabel: '上次保养',
33
+ nextServiceLabel: '下次保养',
34
+ serviced: '已完成保养',
35
+ newWatch: '全新',
36
+ years: '年',
37
+ collectionHealth: '收藏整体状况',
38
+ },
39
+ seo: [
40
+ { type: 'title', text: '手表洗油与保养周期管理:延长爱表使用寿命的秘诀', level: 2 },
41
+ ],
42
+ faq: [
43
+ {
44
+ question: '机械表一般几年需要进行一次保养洗油?',
45
+ answer: '大多数手表品牌建议每隔3至5年进行一次机芯完全保养。在实际使用中,许多腕表在运行5-7年之后才需要重新清洗注油。如果误差明显变大,建议提前送保养。',
46
+ }
47
+ ],
48
+ bibliography,
49
+ howTo: [
50
+ { name: '添加手表', text: '输入手表名称、选择机芯类别以及上次保养时间即可自动计算下次保养的截至日期。' }
51
+ ],
52
+ schemas: [
53
+ {
54
+ '@context': 'https://schema.org',
55
+ '@type': 'FAQPage',
56
+ 'mainEntity': [
57
+ {
58
+ '@type': 'Question',
59
+ 'name': '机械表一般几年需要进行一次保养洗油?',
60
+ 'acceptedAnswer': {
61
+ '@type': 'Answer',
62
+ 'text': '大多数手表品牌建议每隔3至5年进行一次机芯完全保养。在实际使用中,许多腕表在运行5-7年之后才需要重新清洗注油。'
63
+ }
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ '@context': 'https://schema.org',
69
+ '@type': 'SoftwareApplication',
70
+ 'name': '手表保养周期与维护记录工具',
71
+ 'operatingSystem': 'All',
72
+ 'applicationCategory': 'LifestyleApplication',
73
+ 'browserRequirements': 'Requires HTML5. Requires JavaScript.'
74
+ } as any,
75
+ {
76
+ '@context': 'https://schema.org',
77
+ '@type': 'HowTo',
78
+ 'name': '手表保养周期与维护记录工具',
79
+ 'step': [
80
+ {
81
+ '@type': 'HowToStep',
82
+ 'name': '添加手表',
83
+ 'text': '输入手表名称、选择机芯类别以及上次保养时间。'
84
+ }
85
+ ]
86
+ }
87
+ ]
88
+ };
@@ -0,0 +1,11 @@
1
+ import type { ToolDefinition } from '../../types';
2
+ import { serviceIntervalTracker } from './entry';
3
+
4
+ export * from './entry';
5
+
6
+ export const SERVICE_INTERVAL_TRACKER_TOOL: ToolDefinition = {
7
+ entry: serviceIntervalTracker,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,91 @@
1
+ import {
2
+ type Watch,
3
+ state,
4
+ fmtFull,
5
+ fmtShort,
6
+ nextDate,
7
+ nextLabel,
8
+ pct,
9
+ esc,
10
+ getMovementIcon,
11
+ } from './helpers';
12
+
13
+ function getStatusBadgeClass(s: string): string {
14
+ return `svc-badge-${s}`;
15
+ }
16
+
17
+ function getCardStatusText(s: string, nl: { text: string } | null, UI: Record<string, string>): string {
18
+ if (s === 'healthy') {
19
+ return UI.healthy || 'Healthy';
20
+ }
21
+ if (s === 'due') {
22
+ return nl ? nl.text : (UI.due || 'Due Soon');
23
+ }
24
+ return nl ? nl.text : (UI.overdue || 'Overdue');
25
+ }
26
+
27
+ function dateStr(w: Watch, UI: Record<string, string>): string {
28
+ return w.lastService ? fmtShort(w.lastService) : (UI.newWatch || 'New');
29
+ }
30
+
31
+ function renderCardHeader(w: Watch, icon: string, UI: Record<string, string>): string {
32
+ return `
33
+ <div class="svc-card-header">
34
+ <div class="svc-card-icon-container">${icon}</div>
35
+ <div class="svc-card-title-group">
36
+ <span class="svc-card-name">${esc(w.name)}</span>
37
+ <span class="svc-card-movement">${w.movement}</span>
38
+ </div>
39
+ <button class="svc-card-del" data-id="${w.id}" title="${UI.deleteWatch || 'Remove'}">
40
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
41
+ </button>
42
+ </div>
43
+ `;
44
+ }
45
+
46
+ function renderCardProgress(w: Watch, s: string, statusText: string): string {
47
+ return `
48
+ <div class="svc-card-progress-section">
49
+ <div class="svc-card-progress-info">
50
+ <span class="svc-card-progress-pct">${pct(w)}%</span>
51
+ <span class="svc-card-status-badge ${getStatusBadgeClass(s)}">${statusText}</span>
52
+ </div>
53
+ <div class="svc-card-progress-track">
54
+ <div class="svc-card-progress-bar" style="width: ${pct(w)}%"></div>
55
+ </div>
56
+ </div>
57
+ `;
58
+ }
59
+
60
+ function renderCardInfo(w: Watch, dateText: string, ndStr: string, UI: Record<string, string>): string {
61
+ return `
62
+ <div class="svc-card-info-grid">
63
+ <div class="svc-card-info-item">
64
+ <span class="svc-card-info-lbl">${UI.lastServiceLabel || 'Last service'}</span>
65
+ <span class="svc-card-info-val">${dateText}</span>
66
+ </div>
67
+ <div class="svc-card-info-item">
68
+ <span class="svc-card-info-lbl">${UI.nextServiceLabel || 'Next service'}</span>
69
+ <span class="svc-card-info-val">${ndStr || '\u2014'}</span>
70
+ </div>
71
+ </div>
72
+ `;
73
+ }
74
+
75
+ export function entryHTML(w: Watch, UI: Record<string, string>): string {
76
+ const s = state(w);
77
+ const nl = nextLabel(w);
78
+ const nd = nextDate(w);
79
+ const ndStr = nd ? fmtFull(nd.toISOString()) : '';
80
+ const dateText = dateStr(w, UI);
81
+ const statusText = getCardStatusText(s, nl, UI);
82
+ const icon = getMovementIcon(w.movement);
83
+
84
+ return `
85
+ <div class="svc-card svc-card-${s}" data-id="${w.id}" tabindex="0" role="button" title="${UI.editWatch || 'Edit'}">
86
+ ${renderCardHeader(w, icon, UI)}
87
+ ${renderCardProgress(w, s, statusText)}
88
+ ${renderCardInfo(w, dateText, ndStr, UI)}
89
+ </div>
90
+ `;
91
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { serviceIntervalTracker } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const loader = serviceIntervalTracker.i18n[locale] || serviceIntervalTracker.i18n.en;
12
+ const content = await loader?.();
13
+ if (!content) return null;
14
+ ---
15
+
16
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}