Skip to content

Commit 175f4e3

Browse files
committed
clear all data & import backups from json
1 parent 2f7fb87 commit 175f4e3

5 files changed

Lines changed: 190 additions & 9 deletions

File tree

src/components/Settings.tsx

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
import { useEffect, useState } from "react";
3-
import { Settings as SettingsIcon, Moon, Sun, Monitor, Type, Eye, Contrast } from "lucide-react";
3+
import { Settings as SettingsIcon, Moon, Sun, Monitor, Type, Eye, Contrast, DatabaseIcon } from "lucide-react";
44
import { Button } from "@/components/ui/button";
55
import {
66
Sheet,
@@ -16,6 +16,9 @@ import {
1616
SelectTrigger,
1717
SelectValue,
1818
} from "@/components/ui/select";
19+
import { indexedDBStorage } from "@/utils/indexedDBStorage";
20+
import { toast } from "@/hooks/use-toast";
21+
import { Note, Task } from "@/pages/Index";
1922

2023
interface SettingsProps {
2124
isOpen: boolean;
@@ -27,6 +30,8 @@ export const Settings = ({ isOpen, onClose }: SettingsProps) => {
2730
const [fontSize, setFontSize] = useState("medium");
2831
const [highContrast, setHighContrast] = useState(false);
2932
const [reducedMotion, setReducedMotion] = useState(false);
33+
const [dataCleared, setDataCleared] = useState(false);
34+
const [isImportingData, setIsImportingData] = useState(false);
3035

3136
useEffect(() => {
3237
const storedTheme = localStorage.getItem("current-theme");
@@ -35,6 +40,94 @@ export const Settings = ({ isOpen, onClose }: SettingsProps) => {
3540
}
3641
}, []);
3742

43+
useEffect(() => {
44+
const checkData = async () => {
45+
try {
46+
const [notes, tasks] = await Promise.all([
47+
indexedDBStorage.loadNotes(),
48+
indexedDBStorage.loadTasks(),
49+
]);
50+
setDataCleared(notes.length === 0 && tasks.length === 0);
51+
} catch (error) {
52+
console.error("Error checking data:", error);
53+
setDataCleared(false);
54+
}
55+
};
56+
checkData();
57+
}, []);
58+
59+
60+
useEffect(() => {
61+
const handleDataCleared = () => setDataCleared(true);
62+
window.addEventListener("data-cleared", handleDataCleared);
63+
return () => window.removeEventListener("data-cleared", handleDataCleared);
64+
}, []);
65+
66+
const handleImportData = () => {
67+
// Open file browser and look for json files
68+
const input = document.createElement('input');
69+
input.type = 'file';
70+
input.accept = '.json';
71+
input.onchange = (event) => {
72+
const file = (event.target as HTMLInputElement).files?.[0];
73+
if (file) {
74+
const reader = new FileReader();
75+
reader.onload = (e) => {
76+
const data = e.target?.result;
77+
if (typeof data === 'string') {
78+
try {
79+
const parsedData = JSON.parse(data);
80+
if (parsedData && (Array.isArray(parsedData.notes) || Array.isArray(parsedData.tasks))) {
81+
const notes = Array.isArray(parsedData.notes) ? parsedData.notes : null;
82+
const tasks = Array.isArray(parsedData.tasks) ? parsedData.tasks : null;
83+
84+
// Import notes if present
85+
const notesPromise = notes ? indexedDBStorage.saveNotes(notes) : Promise.resolve();
86+
// Import tasks if present
87+
const tasksPromise = tasks ? indexedDBStorage.saveTasks(tasks) : Promise.resolve();
88+
89+
Promise.all([notesPromise, tasksPromise])
90+
.then(() => {
91+
toast({
92+
title: "Data imported successfully!",
93+
description: "Your notes and/or tasks have been imported. Please refresh the page to see the changes.",
94+
variant: "success"
95+
});
96+
})
97+
.catch((error) => {
98+
console.error("Error importing data:", error);
99+
toast({
100+
title: "Import Error",
101+
description: "There was an error importing your data.",
102+
variant: "destructive"
103+
});
104+
});
105+
} else {
106+
alert("Invalid JSON structure. Please ensure the file contains 'notes' and/or 'tasks' arrays.");
107+
}
108+
} catch (error) {
109+
console.error("Error parsing JSON data:", error);
110+
alert("Invalid JSON file. Please upload a valid file.");
111+
}
112+
}
113+
};
114+
reader.readAsText(file);
115+
}
116+
};
117+
input.click();
118+
};
119+
120+
const clearData = () => {
121+
indexedDBStorage.clearAllData().then(() => {
122+
setDataCleared(true);
123+
// Optionally, dispatch event if other components need to know
124+
window.dispatchEvent(new Event("data-cleared"));
125+
}).catch((error) => {
126+
console.error("Error clearing data:", error);
127+
alert("Failed to clear data. Please try again.");
128+
});
129+
};
130+
38131
const handleThemeChange = (newTheme: string) => {
39132
setTheme(newTheme);
40133
localStorage.setItem("current-theme", newTheme);
@@ -138,7 +231,7 @@ export const Settings = ({ isOpen, onClose }: SettingsProps) => {
138231
{/* Accessibility Options */}
139232
<div className="space-y-4">
140233
<h4 className="text-sm font-medium text-muted-foreground">Accessibility</h4>
141-
234+
142235
<div className="flex items-center justify-between">
143236
<div className="flex items-center space-x-2">
144237
<Contrast className="h-4 w-4" />
@@ -168,9 +261,38 @@ export const Settings = ({ isOpen, onClose }: SettingsProps) => {
168261
{reducedMotion ? "On" : "Off"}
169262
</Button>
170263
</div>
264+
<div className="flex items-center justify-between">
265+
<div className="flex items-center space-x-2">
266+
<DatabaseIcon className="h-4 w-4" />
267+
<span className="text-sm">Data Management</span>
268+
</div>
269+
<Button
270+
variant={dataCleared ? "ghost" : "outline"}
271+
size="sm"
272+
onClick={() => {
273+
clearData();
274+
setTimeout(() => {
275+
window.location.reload();
276+
}, 1000);
277+
}}
278+
className={`h-6 w-20 ${dataCleared ? "text-neon-purple" : "text-red-600"} text-xs`}
279+
disabled={dataCleared}
280+
>
281+
{dataCleared ? "No Data" : "Clear Data"}
282+
</Button>
283+
<Button
284+
variant="outline"
285+
size="sm"
286+
onClick={handleImportData}
287+
className="h-6 w-24 text-xs text-neon-green"
288+
>
289+
Import Data
290+
</Button>
291+
</div>
171292
</div>
172293
</div>
173294
</SheetContent>
174295
</Sheet>
175296
);
176297
};
298+

src/components/ShareExport.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const ShareExport = ({ isOpen, onClose, notes, tasks }: ShareExportProps)
5252
break;
5353

5454
case 'json':
55-
content = JSON.stringify(notes, null, 2);
55+
content = JSON.stringify({ notes }, null, 2);
5656
filename = 'noted-notes.json';
5757
mimeType = 'application/json';
5858
break;
@@ -98,7 +98,7 @@ export const ShareExport = ({ isOpen, onClose, notes, tasks }: ShareExportProps)
9898
break;
9999

100100
case 'json':
101-
content = JSON.stringify(tasks, null, 2);
101+
content = JSON.stringify({ tasks }, null, 2);
102102
filename = 'noted-tasks.json';
103103
mimeType = 'application/json';
104104
break;
@@ -181,7 +181,8 @@ export const ShareExport = ({ isOpen, onClose, notes, tasks }: ShareExportProps)
181181

182182
toast({
183183
title: "Export successful",
184-
description: `${filename} has been downloaded.`
184+
description: `${filename} has been downloaded.`,
185+
variant: "success"
185186
});
186187

187188
onClose();

src/components/ui/toast.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const toastVariants = cva(
3030
default: "border bg-background text-foreground",
3131
destructive:
3232
"destructive group border-destructive bg-destructive text-destructive-foreground",
33+
success: "border-green-600 bg-green-50 text-green-900",
3334
},
3435
},
3536
defaultVariants: {

src/utils/indexedDBStorage.ts

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class IndexedDBStorage {
5353
notes.forEach(note => {
5454
store.add({
5555
...note,
56-
createdAt: note.createdAt.toISOString()
56+
// createdAt: note.createdAt.toISOString()
5757
});
5858
});
5959

@@ -71,7 +71,7 @@ class IndexedDBStorage {
7171
const request = store.getAll();
7272

7373
request.onsuccess = () => {
74-
const notes = request.result.map((note: any) => ({
74+
const notes = request.result.map((note: Note) => ({
7575
...note,
7676
createdAt: new Date(note.createdAt)
7777
}));
@@ -96,7 +96,7 @@ class IndexedDBStorage {
9696
tasks.forEach(task => {
9797
store.add({
9898
...task,
99-
completedAt: task.completedAt ? task.completedAt.toISOString() : undefined
99+
// completedAt: task.completedAt ? task.completedAt.toISOString() : undefined
100100
});
101101
});
102102

@@ -114,7 +114,7 @@ class IndexedDBStorage {
114114
const request = store.getAll();
115115

116116
request.onsuccess = () => {
117-
const tasks = request.result.map((task: any) => ({
117+
const tasks = request.result.map((task: Task) => ({
118118
...task,
119119
completedAt: task.completedAt ? new Date(task.completedAt) : undefined
120120
}));
@@ -124,6 +124,59 @@ class IndexedDBStorage {
124124
request.onerror = () => reject(request.error);
125125
});
126126
}
127+
128+
129+
130+
async clearAllData(): Promise<void> {
131+
if (!this.db) await this.init();
132+
133+
return new Promise((resolve, reject) => {
134+
const transaction = this.db!.transaction([NOTES_STORE, TASKS_STORE], 'readwrite');
135+
const notesStore = transaction.objectStore(NOTES_STORE);
136+
const tasksStore = transaction.objectStore(TASKS_STORE);
137+
138+
const notesClearRequest = notesStore.clear();
139+
const tasksClearRequest = tasksStore.clear();
140+
141+
notesClearRequest.onsuccess = () => {
142+
tasksClearRequest.onsuccess = () => resolve();
143+
};
144+
145+
notesClearRequest.onerror = () => reject(notesClearRequest.error);
146+
tasksClearRequest.onerror = () => reject(tasksClearRequest.error);
147+
});
148+
}
149+
150+
async importData(notes: Note[], tasks: Task[]): Promise<void> {
151+
if (!this.db) await this.init();
152+
153+
return new Promise((resolve, reject) => {
154+
const transaction = this.db!.transaction([NOTES_STORE, TASKS_STORE], 'readwrite');
155+
const notesStore = transaction.objectStore(NOTES_STORE);
156+
const tasksStore = transaction.objectStore(TASKS_STORE);
157+
158+
// Add new notes
159+
notes.forEach(note => {
160+
notesStore.add({
161+
...note,
162+
createdAt: note.createdAt.toISOString()
163+
});
164+
});
165+
166+
// Add new tasks
167+
tasks.forEach(task => {
168+
tasksStore.add({
169+
...task,
170+
completedAt: task.completedAt ? task.completedAt.toISOString() : undefined
171+
});
172+
});
173+
174+
transaction.oncomplete = () => resolve();
175+
transaction.onerror = () => reject(transaction.error);
176+
});
177+
}
178+
179+
127180
}
128181

129182
export const indexedDBStorage = new IndexedDBStorage();

tailwind.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export default {
4141
DEFAULT: 'hsl(var(--destructive))',
4242
foreground: 'hsl(var(--destructive-foreground))'
4343
},
44+
success: {
45+
DEFAULT: 'hsl(var(--success))',
46+
foreground: 'hsl(var(--success-foreground))'
47+
},
4448
muted: {
4549
DEFAULT: 'hsl(var(--muted))',
4650
foreground: 'hsl(var(--muted-foreground))'

0 commit comments

Comments
 (0)