-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexport-import.js
More file actions
184 lines (162 loc) · 6.96 KB
/
Copy pathexport-import.js
File metadata and controls
184 lines (162 loc) · 6.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// ══════════════════════════════════════════════════
// EXPORT / IMPORT
// ══════════════════════════════════════════════════
function exportJSON() {
const data = {
stages: state.stages,
subjects: state.subjects,
profile: {
name: userProfile.name,
email: userProfile.email,
createdDate: userProfile.createdDate,
},
exportedAt: new Date().toISOString(),
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `anka_backup_${(userProfile.name || 'user').replace(/\s+/g, '_')}_${Date.now()}.json`;
a.click();
URL.revokeObjectURL(url);
toast('✓ Backup exported');
}
async function importJSON(event) {
const file = event.target.files[0];
if (!file) return;
const text = await file.text();
try {
const data = JSON.parse(text);
if (!Array.isArray(data.stages) || !Array.isArray(data.subjects)) {
throw new Error('Invalid backup format');
}
window.pendingImportData = data;
const profileInfo = data.profile
? `<br><br><strong>Profile:</strong><br><i class="bi bi-person-circle"></i> ${escHtml(data.profile.name || 'User')}`
: '';
openModal('Restore Backup', `
<div class="confirm-box">
<div class="modal-title" style="text-align:center;border:none;margin-bottom:0.5rem;">Restore this backup?</div>
<p class="confirm-msg">This will replace all current data with ${data.stages.length} stage(s) and ${data.subjects.length} subject(s).${profileInfo}</p>
</div>
`);
setModalFooter([
{ label: 'Cancel', cls: 'btn-ghost', fn: 'closeModal()' },
{ label: 'Restore', cls: 'btn-danger', fn: 'confirmImport()' },
]);
} catch (err) {
toast('Invalid backup file');
}
event.target.value = '';
}
async function confirmImport() {
const data = window.pendingImportData;
if (!data) return;
// Clear existing
for (const s of state.stages) await dbDelete('stages', s.id);
for (const s of state.subjects) await dbDelete('subjects', s.id);
state.stages = data.stages;
state.subjects = data.subjects;
for (const s of state.stages) await dbPut('stages', s);
for (const s of state.subjects) await dbPut('subjects', s);
if (data.profile) {
saveUserProfile(data.profile.name || 'User', data.profile.email || '');
}
closeModal();
renderNavTabs();
showSection('dashboard');
toast('✓ Backup restored');
}
// ══════════════════════════════════════════════════
// CLOUD BACKUP / RESTORE (Firestore)
// ══════════════════════════════════════════════════
async function backupToCloud() {
if (!currentUser) { toast('Sign in first'); return; }
try {
await firestoreDB.collection('users').doc(currentUser.uid).set({
stages: state.stages,
subjects: state.subjects,
profile: {
name: userProfile.name,
email: userProfile.email,
createdDate: userProfile.createdDate,
},
updatedAt: new Date().toISOString(),
});
toast('✓ Backup saved to cloud');
} catch (err) {
toast('Cloud backup failed: ' + (err.message || err));
}
}
async function restoreFromCloud(silent) {
if (!currentUser) { if (!silent) toast('Sign in first'); return; }
try {
const doc = await firestoreDB.collection('users').doc(currentUser.uid).get();
if (!doc.exists) { if (!silent) toast('No cloud backup found'); return; }
const data = doc.data();
if (!Array.isArray(data.stages) || !Array.isArray(data.subjects)) {
if (!silent) toast('Cloud backup is invalid');
return;
}
window.pendingImportData = data;
if (silent) {
await confirmImport();
} else {
openModal('Restore from Cloud', `
<div class="confirm-box">
<div class="modal-title" style="text-align:center;border:none;margin-bottom:0.5rem;">Restore cloud backup?</div>
<p class="confirm-msg">This will replace all current data with ${data.stages.length} stage(s) and ${data.subjects.length} subject(s) from the cloud.</p>
</div>
`);
setModalFooter([
{ label: 'Cancel', cls: 'btn-ghost', fn: 'closeModal()' },
{ label: 'Restore', cls: 'btn-danger', fn: 'confirmImport()' },
]);
}
} catch (err) {
toast('Cloud restore failed: ' + (err.message || err));
}
}
// ══════════════════════════════════════════════════
// FACTORY RESET (local only — cloud untouched)
// ══════════════════════════════════════════════════
function confirmFactoryReset() {
openModal('Factory Reset', `
<div class="confirm-box">
<div class="modal-title" style="text-align:center;border:none;margin-bottom:0.5rem;color:var(--red);">
<i class="bi bi-exclamation-triangle-fill"></i> Erase all local data?
</div>
<p class="confirm-msg">
This deletes every stage, subject and your profile details from <strong>this device only</strong>
(${state.stages.length} stage(s), ${state.subjects.length} subject(s)).
${currentUser ? `Your cloud backup for <strong>${escHtml(currentUser.email || '')}</strong> will <strong>not</strong> be touched — restore it anytime with "Restore from Cloud".` : `You won't lose anything in the cloud since you're not signed in to any backup.`}
</p>
</div>
`);
setModalFooter([
{ label: 'Cancel', cls: 'btn-ghost', fn: 'closeModal()' },
{ label: 'Erase Everything', cls: 'btn-danger', fn: 'doFactoryReset()' },
]);
}
async function doFactoryReset() {
// wipe IndexedDB stores
for (const s of state.stages) await dbDelete('stages', s.id);
for (const s of state.subjects) await dbDelete('subjects', s.id);
state.stages = [];
state.subjects = [];
// wipe local profile fields
['userName', 'userEmail', 'userCreatedDate', 'userCreatedAt', 'userLastUpdated'].forEach(k => localStorage.removeItem(k));
userProfile.name = 'User';
userProfile.email = 'user@example.com';
userProfile.createdDate = new Date().toLocaleDateString('en-IN');
localStorage.setItem('userCreatedDate', userProfile.createdDate);
localStorage.setItem('userCreatedAt', new Date().toISOString());
// sign out so a reload doesn't auto-restore from cloud and undo the reset
if (currentUser && typeof auth !== 'undefined') {
try { await auth.signOut(); } catch (err) { /* ignore */ }
}
closeModal();
renderNavTabs();
showSection('dashboard');
toast('✓ Local data erased and signed out');
}