/** * Component: Add Shelf Modal * Documentation: documentation/frontend/components.md */ 'use client'; import React, { useState } from 'react'; import { Modal } from './Modal'; import { Input } from './Input'; import { Button } from './Button'; import { useAddGoodreadsShelf } from '@/lib/hooks/useGoodreadsShelves'; import { useAddHardcoverShelf } from '@/lib/hooks/useHardcoverShelves'; import { HardcoverForm } from './HardcoverForm'; interface AddShelfModalProps { isOpen: boolean; onClose: () => void; } const GOODREADS_RSS_PATTERN = /goodreads\.com\/review\/list_rss\//; export function AddShelfModal({ isOpen, onClose }: AddShelfModalProps) { const [provider, setProvider] = useState<'goodreads' | 'hardcover'>('goodreads'); // Goodreads State const [rssUrl, setRssUrl] = useState(''); // Hardcover State const [apiToken, setApiToken] = useState(''); const [listType, setListType] = useState<'status' | 'custom'>('status'); const [statusId, setStatusId] = useState('1'); const [customListId, setCustomListId] = useState(''); // Shared State const [autoRequest, setAutoRequest] = useState(true); const [validationError, setValidationError] = useState(''); const [success, setSuccess] = useState(false); const [successMessage, setSuccessMessage] = useState(''); const { addShelf: addGoodreads, isLoading: isGoodreadsLoading, error: goodreadsError } = useAddGoodreadsShelf(); const { addShelf: addHardcover, isLoading: isHardcoverLoading, error: hardcoverError } = useAddHardcoverShelf(); const isLoading = isGoodreadsLoading || isHardcoverLoading; const currentError = provider === 'goodreads' ? goodreadsError : hardcoverError; const validateInput = (): boolean => { if (provider === 'goodreads') { if (!rssUrl.trim()) { setValidationError('RSS URL is required'); return false; } if (!GOODREADS_RSS_PATTERN.test(rssUrl)) { setValidationError('Must be a Goodreads shelf RSS URL (goodreads.com/review/list_rss/...)'); return false; } } else { if (!apiToken.trim()) { setValidationError('Hardcover API Token is required'); return false; } if (listType === 'custom' && !customListId.trim()) { setValidationError('Hardcover List URL or Slug is required'); return false; } } setValidationError(''); return true; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validateInput()) return; try { if (provider === 'goodreads') { const shelf = await addGoodreads(rssUrl, autoRequest); setSuccessMessage(`Added shelf "${shelf.name}" successfully!`); setRssUrl(''); } else { const finalId = listType === 'status' ? `status-${statusId}` : customListId.trim(); const shelf = await addHardcover(apiToken.trim(), finalId, autoRequest); setSuccessMessage(`Added list "${shelf.name}" successfully!`); setApiToken(''); setCustomListId(''); } setSuccess(true); setTimeout(() => { setSuccess(false); onClose(); }, 2000); } catch { // Error is handled by the hooks } }; const handleClose = () => { setRssUrl(''); setApiToken(''); setCustomListId(''); setAutoRequest(true); setValidationError(''); setSuccess(false); setSuccessMessage(''); onClose(); }; return (
{/* Provider Tabs */}
{/* Visual Header */}
{provider === 'goodreads' ? ( <>
Goodreads

Paste your Goodreads shelf RSS URL. Books will be automatically requested.

) : ( <>
Hardcover

Connect a Hardcover reading list and books will be automatically requested as you add them.

)}
{/* Success Alert */} {success && (

{successMessage}

)} {/* Error Alert */} {currentError && (

{currentError}

)} {/* Form */}
{provider === 'goodreads' ? (
{ setRssUrl(e.target.value); if (validationError) setValidationError(''); }} placeholder="https://www.goodreads.com/review/list_rss/..." error={validationError} disabled={isLoading || success} />

Find it on Goodreads: My Books → select a shelf → RSS link at the bottom of the page.

) : ( )} {/* Auto-Request Toggle */}
); }