112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
import { useForm } from 'react-hook-form';
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
import { z } from 'zod';
|
|
import { useNavigate, Link } from 'react-router-dom';
|
|
import { useBoards } from '../hooks/useBoards';
|
|
import { useToast } from '../context/toasts/useToast';
|
|
|
|
const boardSchema = z.object({
|
|
name: z
|
|
.string()
|
|
.min(1, 'Board name is required')
|
|
.max(100, 'Board name must be less than 100 characters'),
|
|
description: z.string().max(500, 'Description must be less than 500 characters').optional(),
|
|
});
|
|
|
|
type BoardFormData = z.infer<typeof boardSchema>;
|
|
|
|
export function BoardCreate() {
|
|
const navigate = useNavigate();
|
|
const { createBoard } = useBoards();
|
|
const { addNotification } = useToast();
|
|
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
formState: { errors },
|
|
} = useForm<BoardFormData>({
|
|
resolver: zodResolver(boardSchema),
|
|
});
|
|
|
|
const onSubmit = async (data: BoardFormData) => {
|
|
try {
|
|
const newBoard = await createBoard(data);
|
|
addNotification({
|
|
type: 'success',
|
|
title: 'Board Created',
|
|
message: `Board "${newBoard.name}" created successfully.`,
|
|
duration: 3000,
|
|
});
|
|
navigate(`/boards/${newBoard.id}`);
|
|
} catch (err) {
|
|
const errorMessage = err instanceof Error ? err.message : 'Failed to create board';
|
|
addNotification({
|
|
type: 'error',
|
|
title: 'Error Creating Board',
|
|
message: errorMessage,
|
|
duration: 5000,
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-2xl mx-auto">
|
|
<div className="mb-8">
|
|
<Link to="/boards" className="text-gray-400 hover:text-white transition-colors">
|
|
← Back to Boards
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="bg-gray-800 rounded-lg p-8 border border-gray-700">
|
|
<h1 className="text-3xl font-bold text-white mb-6">Create New Board</h1>
|
|
|
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
|
<div>
|
|
<label htmlFor="name" className="block text-sm font-medium text-gray-300 mb-2">
|
|
Board Name <span className="text-red-400">*</span>
|
|
</label>
|
|
<input
|
|
id="name"
|
|
type="text"
|
|
{...register('name')}
|
|
className="w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
placeholder="e.g., Project Alpha, Personal Tasks"
|
|
/>
|
|
{errors.name && <p className="mt-1 text-sm text-red-400">{errors.name.message}</p>}
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="description" className="block text-sm font-medium text-gray-300 mb-2">
|
|
Description
|
|
</label>
|
|
<textarea
|
|
id="description"
|
|
rows={4}
|
|
{...register('description')}
|
|
className="w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
|
|
placeholder="Optional description for your board..."
|
|
/>
|
|
{errors.description && (
|
|
<p className="mt-1 text-sm text-red-400">{errors.description.message}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex justify-end gap-3 pt-4">
|
|
<Link
|
|
to="/boards"
|
|
className="px-6 py-3 text-gray-300 hover:text-white transition-colors"
|
|
>
|
|
Cancel
|
|
</Link>
|
|
<button
|
|
type="submit"
|
|
className="bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg transition-colors"
|
|
>
|
|
Create Board
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|