kanban-app/frontend/src/pages/BoardCreate.tsx

113 lines
3.7 KiB
TypeScript
Raw Normal View History

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';
2026-02-27 10:15:50 +00:00
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();
2026-02-27 07:53:36 +00:00
const { addNotification } = useToast();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<BoardFormData>({
resolver: zodResolver(boardSchema),
});
const onSubmit = async (data: BoardFormData) => {
try {
2026-02-27 07:53:36 +00:00
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) {
2026-02-27 07:53:36 +00:00
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>
);
}