Skip to content

Commit 28811ca

Browse files
committed
Enhance rich text editor with formatting options
1 parent f971427 commit 28811ca

4 files changed

Lines changed: 111 additions & 60 deletions

File tree

app/page.tsx

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
import { useEffect, useState } from 'react';
44
import Link from 'next/link';
5-
import { wishlistsApi, itemsApi, settingsApi, type Wishlist, type Settings } from '@/lib/api';
5+
import { wishlistsApi, settingsApi, type Wishlist, type Settings } from '@/lib/api';
66
import Header from '@/components/header';
77
import Footer from '@/components/footer';
88
import PasswordLockGuard from '@/components/password-lock-guard';
99
import ShareButton from '@/components/share-button';
1010

1111
export default function Home() {
1212
const [wishlists, setWishlists] = useState<Wishlist[]>([]);
13-
const [itemCounts, setItemCounts] = useState<Record<string, number>>({});
1413
const [isLoading, setIsLoading] = useState(true);
1514
const [settings, setSettings] = useState<Settings>({ siteTitle: 'Wishlist', homepageSubtext: 'Browse and explore available wishlists' });
1615

@@ -32,16 +31,7 @@ export default function Home() {
3231
try {
3332
const data = await wishlistsApi.getAllPublic();
3433
setWishlists(data);
35-
36-
// Fetch item counts for each wishlist
37-
const counts: Record<string, number> = {};
38-
await Promise.all(
39-
data.map(async (w) => {
40-
const items = await itemsApi.getAll(w.id);
41-
counts[w.id] = items.length;
42-
})
43-
);
44-
setItemCounts(counts);
34+
// Item counts removed - requires authentication
4535
} catch (error) {
4636
console.error('Failed to fetch wishlists:', error);
4737
} finally {
@@ -101,9 +91,6 @@ export default function Home() {
10191
{wishlist.description}
10292
</p>
10393
)}
104-
<p className="text-base text-gray-500 dark:text-gray-400 font-medium">
105-
{itemCounts[wishlist.id] || 0} items
106-
</p>
10794
</div>
10895
</div>
10996
</Link>

components/RichTextEditor.tsx

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
1010
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
1111
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
1212
import { $generateHtmlFromNodes, $generateNodesFromDOM } from '@lexical/html';
13-
import { $getRoot, $insertNodes, $getSelection, $isRangeSelection } from 'lexical';
13+
import { $getRoot, $insertNodes, $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical';
1414
import { ListNode, ListItemNode } from '@lexical/list';
1515
import { LinkNode, AutoLinkNode, $createLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
16-
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
16+
import { HeadingNode, QuoteNode, $createHeadingNode } from '@lexical/rich-text';
17+
import { $setBlocksType } from '@lexical/selection';
1718
import { useEffect, useState } from 'react';
1819

1920
// Toolbar Component
@@ -24,11 +25,24 @@ function ToolbarPlugin() {
2425
const [linkText, setLinkText] = useState('');
2526

2627
const formatBold = () => {
27-
editor.dispatchCommand({ type: 'FORMAT_TEXT_COMMAND', payload: 'bold' } as any, undefined);
28+
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold');
2829
};
2930

3031
const formatItalic = () => {
31-
editor.dispatchCommand({ type: 'FORMAT_TEXT_COMMAND', payload: 'italic' } as any, undefined);
32+
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic');
33+
};
34+
35+
const formatUnderline = () => {
36+
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline');
37+
};
38+
39+
const formatHeading = (level: 'h2' | 'h3') => {
40+
editor.update(() => {
41+
const selection = $getSelection();
42+
if ($isRangeSelection(selection)) {
43+
$setBlocksType(selection, () => $createHeadingNode(level));
44+
}
45+
});
3246
};
3347

3448
const formatBulletList = () => {
@@ -77,7 +91,7 @@ function ToolbarPlugin() {
7791

7892
return (
7993
<>
80-
<div className="flex items-center gap-1 p-2 border-b border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 rounded-t-lg">
94+
<div className="flex items-center gap-1 p-2 border-b border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 rounded-t-lg flex-wrap">
8195
<button
8296
type="button"
8397
onClick={formatBold}
@@ -94,6 +108,31 @@ function ToolbarPlugin() {
94108
>
95109
I
96110
</button>
111+
<button
112+
type="button"
113+
onClick={formatUnderline}
114+
className="px-3 py-1.5 text-sm underline hover:bg-gray-200 dark:hover:bg-gray-700 rounded transition-colors"
115+
title="Underline"
116+
>
117+
U
118+
</button>
119+
<div className="w-px h-6 bg-gray-300 dark:bg-gray-600 mx-1" />
120+
<button
121+
type="button"
122+
onClick={() => formatHeading('h2')}
123+
className="px-3 py-1.5 text-sm font-bold hover:bg-gray-200 dark:hover:bg-gray-700 rounded transition-colors"
124+
title="Heading 2"
125+
>
126+
H2
127+
</button>
128+
<button
129+
type="button"
130+
onClick={() => formatHeading('h3')}
131+
className="px-3 py-1.5 text-sm font-semibold hover:bg-gray-200 dark:hover:bg-gray-700 rounded transition-colors"
132+
title="Heading 3"
133+
>
134+
H3
135+
</button>
97136
<div className="w-px h-6 bg-gray-300 dark:bg-gray-600 mx-1" />
98137
<button
99138
type="button"
@@ -209,11 +248,20 @@ export default function RichTextEditor({ value, onChange, placeholder = 'Enter t
209248
namespace: 'WishlistPreferences',
210249
theme: {
211250
paragraph: 'mb-2',
251+
heading: {
252+
h2: 'text-2xl font-bold mb-3 mt-4 text-gray-900 dark:text-white',
253+
h3: 'text-xl font-semibold mb-2 mt-3 text-gray-900 dark:text-white',
254+
},
212255
list: {
213256
ul: 'list-disc list-inside mb-2',
214257
ol: 'list-decimal list-inside mb-2',
215258
},
216259
link: 'text-indigo-600 dark:text-indigo-400 hover:underline',
260+
text: {
261+
bold: 'font-bold',
262+
italic: 'italic',
263+
underline: 'underline',
264+
},
217265
},
218266
onError: (error: Error) => {
219267
console.error(error);

components/admin/WishlistCard.tsx

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -218,51 +218,66 @@ export default function WishlistCard({
218218
type="wishlist"
219219
label="Wishlist Image"
220220
/>
221-
<div className="space-y-2">
222-
<div className="flex items-center gap-2">
221+
<div className="space-y-3">
222+
<div>
223+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
224+
Wishlist Name *
225+
</label>
226+
<div className="flex items-center gap-2">
227+
<input
228+
type="text"
229+
value={editForm.name}
230+
onChange={(e) => handleEditNameChange(e.target.value)}
231+
className="text-lg font-bold px-2 py-1 border-2 border-indigo-500 rounded focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white flex-1"
232+
placeholder="Wishlist name"
233+
/>
234+
<label className="flex items-center gap-2 text-base">
235+
<input
236+
type="checkbox"
237+
checked={editForm.isPublic}
238+
onChange={(e) =>
239+
setEditForm((prev) => ({
240+
...prev,
241+
isPublic: e.target.checked,
242+
}))
243+
}
244+
className="h-4 w-4 text-indigo-600 border-gray-300 rounded"
245+
/>
246+
<span className="text-gray-700 dark:text-gray-300">Public</span>
247+
</label>
248+
</div>
249+
</div>
250+
<div>
251+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
252+
URL Slug *
253+
</label>
223254
<input
224255
type="text"
225-
value={editForm.name}
226-
onChange={(e) => handleEditNameChange(e.target.value)}
227-
className="text-lg font-bold px-2 py-1 border-2 border-indigo-500 rounded focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white flex-1"
228-
placeholder="Wishlist name"
256+
value={editForm.slug}
257+
onChange={(e) =>
258+
setEditForm((prev) => ({ ...prev, slug: e.target.value }))
259+
}
260+
className="text-base px-2 py-1 border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white w-full"
261+
placeholder="url-slug"
229262
/>
230-
<label className="flex items-center gap-2 text-base">
231-
<input
232-
type="checkbox"
233-
checked={editForm.isPublic}
234-
onChange={(e) =>
235-
setEditForm((prev) => ({
236-
...prev,
237-
isPublic: e.target.checked,
238-
}))
239-
}
240-
className="h-4 w-4 text-indigo-600 border-gray-300 rounded"
241-
/>
242-
<span className="text-gray-700 dark:text-gray-300">Public</span>
263+
</div>
264+
<div>
265+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
266+
Description
243267
</label>
268+
<textarea
269+
value={editForm.description}
270+
onChange={(e) =>
271+
setEditForm((prev) => ({
272+
...prev,
273+
description: e.target.value,
274+
}))
275+
}
276+
className="text-base px-2 py-1 border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white w-full"
277+
placeholder="Description"
278+
rows={2}
279+
/>
244280
</div>
245-
<input
246-
type="text"
247-
value={editForm.slug}
248-
onChange={(e) =>
249-
setEditForm((prev) => ({ ...prev, slug: e.target.value }))
250-
}
251-
className="text-base px-2 py-1 border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white w-full"
252-
placeholder="url-slug"
253-
/>
254-
<textarea
255-
value={editForm.description}
256-
onChange={(e) =>
257-
setEditForm((prev) => ({
258-
...prev,
259-
description: e.target.value,
260-
}))
261-
}
262-
className="text-base px-2 py-1 border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white w-full"
263-
placeholder="Description"
264-
rows={2}
265-
/>
266281
<div>
267282
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
268283
Preferences

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)