PageSourceSearch

https://ironlog-pwa-1ls.pages.dev/js/storage_v6.js

js ironlog-pwa-1ls.pages.dev collected 2026-10-03 05:18:59 UTC 3,170 bytes, 118 lines download raw bytes

1// Storage layer: localStorage-backed, zero-server, offline-first
2const Storage = {
3  _key: 'ironlog_data',
4  _data: null,
5
6  init() {
7    const raw = localStorage.getItem(this._key);
8    this._data = raw ? JSON.parse(raw) : {
9      workouts: [],
10      streak: 0,
11      lastWorkoutDate: null,
12      stats: { totalWorkouts: 0, totalSets: 0, totalVolume: 0 },
13      settings: { unit: 'kg', restTimer: true }
14    };
15    return this;
16  },
17
18  _save() {
19    localStorage.setItem(this._key, JSON.stringify(this._data));
20  },
21
22  // Save a completed workout
23  saveWorkout(workout) {
24    const today = new Date().toISOString().split('T')[0];
25    workout.date = today;
26    workout.time = new Date().toISOString();
27
28    // Calculate volume
29    let volume = 0, sets = 0;
30    workout.exercises.forEach(ex => {
31      ex.sets_data.forEach(s => {
32        if (s.weight && s.reps) volume += s.weight * s.reps;
33        sets++;
34      });
35    });
36    workout.volume = volume;
37    workout.sets = sets;
38
39    this._data.workouts.unshift(workout);
40
41    // Update streak
42    if (this._data.lastWorkoutDate !== today) {
43      const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0];
44      this._data.streak = (this._data.lastWorkoutDate === yesterday)
45        ? this._data.streak + 1 : 1;
46      this._data.lastWorkoutDate = today;
47    }
48
49    // Update stats
50    this._data.stats.totalWorkouts++;
51    this._data.stats.totalSets += sets;
52    this._data.stats.totalVolume += volume;
53
54    this._save();
55    return workout;
56  },
57
58  // Get recent workouts
59  getRecentWorkouts(limit = 10) {
60    return this._data.workouts.slice(0, limit);
61  },
62
63  // Get today's workout (if exists)
64  getTodayWorkout() {
65    const today = new Date().toISOString().split('T')[0];
66    return this._data.workouts.find(w => w.date === today);
67  },
68
69  // Get streak
70  getStreak() {
71    return this._data.streak;
72  },
73
74  // Get all stats
75  getStats() {
76    return { ...this._data.stats, streak: this._data.streak };
77  },
78
79  // Export CSV
80  exportCSV() {
81    const rows = [['Date', 'Template', 'Exercise', 'Sets', 'Weight(kg)', 'Reps']];
82    this._data.workouts.forEach(w => {
83      w.exercises.forEach(ex => {
84        ex.sets_data.forEach(s => {
85          rows.push([w.date, w.templateName, ex.nameCN, '', s.weight || '', s.reps || '']);
86        });
87      });
88    });
89    return rows.map(r => r.join(',')).join('\n');
90  },
91
92  // Get specific template's history (for "last time" auto-fill)
93  getLastExerciseData(templateId, exerciseTag) {
94    const prev = this._data.workouts.find(w =>
95      w.templateId === templateId &&
96      w.exercises.some(e => e.tag === exerciseTag)
97    );
98    if (!prev) return null;
99    const ex = prev.exercises.find(e => e.tag === exerciseTag);
100    return ex ? ex.sets_data : null;
101  },
102
103  // Check if user should be prompted for sync (3+ workouts)
104  shouldPromptSync() {
105    return this._data.workouts.length >= 3 && !this._data.syncPrompted;
106  },
107
108  markSyncPrompted() {
109    this._data.syncPrompted = true;
110    this._save();
111  },
112
113  // D1 sync (future)
114  async syncToCloud() {
115    // Placeholder for Cloudflare D1 sync
116    console.log('Cloud sync not yet implemented');
117  }
118};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.