Skip to content

Commit d839d3c

Browse files
committed
App settings upgrade + new themes
1 parent 8b44001 commit d839d3c

File tree

10 files changed

+483
-224
lines changed

10 files changed

+483
-224
lines changed

src/app/api/documents/route.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { writeFile, readFile, readdir, mkdir } from 'fs/promises';
1+
import { writeFile, readFile, readdir, mkdir, unlink } from 'fs/promises';
22
import { NextRequest, NextResponse } from 'next/server';
33
import path from 'path';
44

@@ -81,3 +81,20 @@ export async function GET() {
8181
return NextResponse.json({ error: 'Failed to load documents' }, { status: 500 });
8282
}
8383
}
84+
85+
export async function DELETE() {
86+
try {
87+
await ensureDocsDir();
88+
const files = await readdir(DOCS_DIR);
89+
90+
for (const file of files) {
91+
const filePath = path.join(DOCS_DIR, file);
92+
await unlink(filePath);
93+
}
94+
95+
return NextResponse.json({ success: true });
96+
} catch (error) {
97+
console.error('Error deleting documents:', error);
98+
return NextResponse.json({ error: 'Failed to delete documents' }, { status: 500 });
99+
}
100+
}

src/app/globals.css

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
:root,
66
html.light {
77
--background: #ffffff;
8-
--foreground: #171717;
9-
--base: #f5f5f5;
10-
--offbase: #dbdbdb;
8+
--foreground: #2d3748;
9+
--base: #f7fafc;
10+
--offbase: #e2e8f0;
1111
--accent: #ef4444;
12-
--muted: #737373;
12+
--muted: #718096;
1313
}
1414

1515
html.dark {
@@ -21,6 +21,33 @@ html.dark {
2121
--muted: #a3a3a3;
2222
}
2323

24+
html.aqua {
25+
--background: #020617;
26+
--foreground: #e2e8f0;
27+
--base: #0f172a;
28+
--offbase: #1e293b;
29+
--accent: #38bdf8;
30+
--muted: #94a3b8;
31+
}
32+
33+
html.forest {
34+
--background: #0a0f0c;
35+
--foreground: #d4e8d0;
36+
--base: #111a15;
37+
--offbase: #1a2820;
38+
--accent: #4ade80;
39+
--muted: #7c8f85;
40+
}
41+
42+
html.vibrant {
43+
--background: #090420;
44+
--foreground: #ffffff;
45+
--base: #1a0942;
46+
--offbase: #2d1163;
47+
--accent: #ff3d81;
48+
--muted: #9d7dcc;
49+
}
50+
2451
/* Ensure background color is set before content loads */
2552
html {
2653
background-color: var(--background);

src/components/ConfirmDialog.tsx

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
'use client';
2+
3+
import { Fragment, useEffect } from 'react';
4+
import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
5+
6+
interface ConfirmDialogProps {
7+
isOpen: boolean;
8+
onClose: () => void;
9+
onConfirm: () => void;
10+
title: string;
11+
message: string;
12+
confirmText?: string;
13+
cancelText?: string;
14+
isDangerous?: boolean;
15+
}
16+
17+
export function ConfirmDialog({
18+
isOpen,
19+
onClose,
20+
onConfirm,
21+
title,
22+
message,
23+
confirmText = 'Confirm',
24+
cancelText = 'Cancel',
25+
isDangerous = false,
26+
}: ConfirmDialogProps) {
27+
useEffect(() => {
28+
if (!isOpen) return;
29+
30+
const handleKeyDown = (e: KeyboardEvent) => {
31+
if (e.key === 'Enter') {
32+
e.preventDefault();
33+
onConfirm();
34+
}
35+
};
36+
37+
window.addEventListener('keydown', handleKeyDown);
38+
return () => window.removeEventListener('keydown', handleKeyDown);
39+
}, [isOpen, onConfirm]);
40+
41+
return (
42+
<Transition appear show={isOpen} as={Fragment}>
43+
<Dialog as="div" className="relative z-50" onClose={onClose}>
44+
<TransitionChild
45+
as={Fragment}
46+
enter="ease-out duration-300"
47+
enterFrom="opacity-0"
48+
enterTo="opacity-100"
49+
leave="ease-in duration-200"
50+
leaveFrom="opacity-100"
51+
leaveTo="opacity-0"
52+
>
53+
<div className="fixed inset-0 bg-black/25 backdrop-blur-sm" />
54+
</TransitionChild>
55+
56+
<div className="fixed inset-0 overflow-y-auto">
57+
<div className="flex min-h-full items-center justify-center p-4 text-center">
58+
<TransitionChild
59+
as={Fragment}
60+
enter="ease-out duration-300"
61+
enterFrom="opacity-0 scale-95"
62+
enterTo="opacity-100 scale-100"
63+
leave="ease-in duration-200"
64+
leaveFrom="opacity-100 scale-100"
65+
leaveTo="opacity-0 scale-95"
66+
>
67+
<DialogPanel className="w-full max-w-md transform rounded-2xl bg-base p-6 text-left align-middle shadow-xl transition-all">
68+
<DialogTitle
69+
as="h3"
70+
className="text-lg font-semibold leading-6 text-foreground"
71+
>
72+
{title}
73+
</DialogTitle>
74+
<div className="mt-2">
75+
<p className="text-sm text-foreground/90">{message}</p>
76+
</div>
77+
78+
<div className="mt-6 flex justify-end space-x-3">
79+
<button
80+
type="button"
81+
className="inline-flex justify-center rounded-lg bg-background px-4 py-2 text-sm
82+
font-medium text-foreground hover:bg-background/90 focus:outline-none
83+
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
84+
transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
85+
onClick={onClose}
86+
>
87+
{cancelText}
88+
</button>
89+
<button
90+
type="button"
91+
className={`inline-flex justify-center rounded-lg px-4 py-2 text-sm
92+
font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2
93+
transform transition-transform duration-200 ease-in-out hover:scale-[1.04]
94+
${isDangerous
95+
? 'bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500 hover:text-white'
96+
: 'bg-accent text-white hover:bg-accent/90 focus-visible:ring-accent hover:text-background'
97+
}`}
98+
onClick={onConfirm}
99+
>
100+
{confirmText}
101+
</button>
102+
</div>
103+
</DialogPanel>
104+
</TransitionChild>
105+
</div>
106+
</div>
107+
</Dialog>
108+
</Transition>
109+
);
110+
}

src/components/DocumentList.tsx

Lines changed: 14 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import Link from 'next/link';
2-
import { Button, Dialog } from '@headlessui/react';
3-
import { Transition, TransitionChild, DialogPanel, DialogTitle } from '@headlessui/react';
4-
import { Fragment, useState } from 'react';
2+
import { Button } from '@headlessui/react';
3+
import { Transition } from '@headlessui/react';
4+
import { useState } from 'react';
55
import { useDocuments } from '@/contexts/DocumentContext';
66
import { PDFIcon, EPUBIcon } from '@/components/icons/Icons';
7+
import { ConfirmDialog } from '@/components/ConfirmDialog';
78

89
type DocumentToDelete = {
910
id: string;
@@ -21,7 +22,6 @@ export function DocumentList() {
2122
isEPUBLoading,
2223
} = useDocuments();
2324

24-
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
2525
const [documentToDelete, setDocumentToDelete] = useState<DocumentToDelete | null>(null);
2626

2727
const handleDelete = async () => {
@@ -33,7 +33,6 @@ export function DocumentList() {
3333
} else {
3434
await removeEPUB(documentToDelete.id);
3535
}
36-
setIsDeleteDialogOpen(false);
3736
setDocumentToDelete(null);
3837
} catch (err) {
3938
console.error('Failed to remove document:', err);
@@ -101,10 +100,7 @@ export function DocumentList() {
101100
</div>
102101
</Link>
103102
<Button
104-
onClick={() => {
105-
setDocumentToDelete({ id: doc.id, name: doc.name, type: doc.type });
106-
setIsDeleteDialogOpen(true);
107-
}}
103+
onClick={() => setDocumentToDelete({ id: doc.id, name: doc.name, type: doc.type })}
108104
className="ml-4 p-2 text-muted hover:text-red-500 rounded-lg hover:bg-red-50 transition-colors"
109105
aria-label="Delete document"
110106
>
@@ -117,70 +113,15 @@ export function DocumentList() {
117113
))}
118114
</div>
119115

120-
<Transition appear show={isDeleteDialogOpen} as={Fragment}>
121-
<Dialog
122-
as="div"
123-
className="relative z-50"
124-
onClose={() => setIsDeleteDialogOpen(false)}
125-
>
126-
<TransitionChild
127-
as={Fragment}
128-
enter="ease-out duration-300"
129-
enterFrom="opacity-0"
130-
enterTo="opacity-100"
131-
leave="ease-in duration-200"
132-
leaveFrom="opacity-100"
133-
leaveTo="opacity-0"
134-
>
135-
<div className="fixed inset-0 bg-black bg-opacity-25" />
136-
</TransitionChild>
137-
138-
<div className="fixed inset-0 overflow-y-auto">
139-
<div className="flex min-h-full items-center justify-center p-4 text-center">
140-
<TransitionChild
141-
as={Fragment}
142-
enter="ease-out duration-300"
143-
enterFrom="opacity-0 scale-95"
144-
enterTo="opacity-100 scale-100"
145-
leave="ease-in duration-200"
146-
leaveFrom="opacity-100 scale-100"
147-
leaveTo="opacity-0 scale-95"
148-
>
149-
<DialogPanel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-background p-6 text-left align-middle shadow-xl transition-all">
150-
<DialogTitle
151-
as="h3"
152-
className="text-lg font-medium leading-6 text-foreground"
153-
>
154-
Delete Document
155-
</DialogTitle>
156-
<div className="mt-2">
157-
<p className="text-sm text-muted">
158-
Are you sure you want to delete <span className='font-bold'>{documentToDelete?.name}</span>? This action cannot be undone.
159-
</p>
160-
</div>
161-
162-
<div className="mt-4 flex justify-end space-x-3">
163-
<Button
164-
type="button"
165-
className="inline-flex justify-center rounded-md border border-transparent bg-base px-4 py-2 text-sm font-medium text-foreground hover:bg-base/80 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2"
166-
onClick={() => setIsDeleteDialogOpen(false)}
167-
>
168-
Cancel
169-
</Button>
170-
<Button
171-
type="button"
172-
className="inline-flex justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2"
173-
onClick={handleDelete}
174-
>
175-
Delete
176-
</Button>
177-
</div>
178-
</DialogPanel>
179-
</TransitionChild>
180-
</div>
181-
</div>
182-
</Dialog>
183-
</Transition>
116+
<ConfirmDialog
117+
isOpen={documentToDelete !== null}
118+
onClose={() => setDocumentToDelete(null)}
119+
onConfirm={handleDelete}
120+
title="Delete Document"
121+
message={`Are you sure you want to delete ${documentToDelete?.name ? documentToDelete.name : 'this document'}? This action cannot be undone.`}
122+
confirmText="Delete"
123+
isDangerous={true}
124+
/>
184125
</div>
185126
);
186127
}

0 commit comments

Comments
 (0)