124 lines
3.5 KiB
TypeScript
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;
|