2026-05-28 07:18:04 +00:00

124 lines
3.5 KiB
TypeScript

/**
* CreatePageModal Component
*
* Modal dialog for creating new pages with custom name.
* Slug is auto-generated from the page name behind the scenes.
*/
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import CardBoxModal from '../CardBoxModal';
import { sanitizeSlug, buildUniqueSlug } from '../../lib/slugHelpers';
interface CreatePageModalProps {
/** Whether the modal is visible */
isActive: boolean;
/** Whether page creation is in progress */
isCreating: boolean;
/** Set of existing slugs for uniqueness validation */
existingSlugs: Set<string>;
/** Suggested page number for default name */
suggestedPageNumber: number;
/** Called when user confirms with valid name and slug */
onConfirm: (pageName: string, slug: string) => void;
/** Called when user cancels */
onCancel: () => void;
}
/**
* Modal for creating new pages - only asks for name, slug is auto-generated
*/
const CreatePageModal: React.FC<CreatePageModalProps> = ({
isActive,
isCreating,
existingSlugs,
suggestedPageNumber,
onConfirm,
onCancel,
}) => {
const [pageName, setPageName] = useState('');
const [nameError, setNameError] = useState('');
// Reset form when modal opens
useEffect(() => {
if (isActive) {
const defaultName = `Page ${suggestedPageNumber}`;
setPageName(defaultName);
setNameError('');
}
}, [isActive, suggestedPageNumber]);
// Validate name
const nameValidationError = useMemo(() => {
const trimmed = pageName.trim();
if (!trimmed) return 'Page name is required';
if (trimmed.length > 255) return 'Page name must be 255 characters or less';
return '';
}, [pageName]);
// Auto-generate unique slug from name
const generatedSlug = useMemo(() => {
const baseSlug = sanitizeSlug(pageName) || 'page';
return buildUniqueSlug(baseSlug, existingSlugs);
}, [pageName, existingSlugs]);
const handleNameChange = useCallback((value: string) => {
setPageName(value);
setNameError('');
}, []);
const handleConfirm = useCallback(() => {
if (nameValidationError) {
setNameError(nameValidationError);
return;
}
onConfirm(pageName.trim(), generatedSlug);
}, [pageName, generatedSlug, nameValidationError, onConfirm]);
const handleCancel = useCallback(() => {
if (!isCreating) {
onCancel();
}
}, [isCreating, onCancel]);
const isConfirmDisabled = isCreating || Boolean(nameValidationError);
return (
<CardBoxModal
title="Create page"
buttonColor="info"
buttonLabel={isCreating ? 'Creating...' : 'Create'}
isConfirmDisabled={isConfirmDisabled}
isActive={isActive}
onConfirm={handleConfirm}
onCancel={isCreating ? undefined : handleCancel}
>
<div>
<label
htmlFor="create-page-name"
className="block text-sm font-semibold mb-1"
>
Page name
</label>
<input
id="create-page-name"
type="text"
value={pageName}
onChange={(e) => handleNameChange(e.target.value)}
placeholder="Enter page name"
className="w-full border border-gray-300 rounded px-3 py-2 bg-white dark:bg-dark-800"
autoFocus
maxLength={255}
/>
{(nameError || nameValidationError) && (
<p className="text-xs text-red-600 mt-1">
{nameError || nameValidationError}
</p>
)}
</div>
</CardBoxModal>
);
};
export default CreatePageModal;