Phase 6.4: DMS-Forms on RHF + Zod
- Dms.tsx folder-create: RHF+Zod (name required) - ShareDialog add-share: RHF+Zod (shareId required, shareType/permission kept as useState) - Error display under each field - Preserved all existing functionality: folder tree, file grid, share links - Added 2 validation tests for ShareDialog (empty shareId, valid submit)
This commit is contained in:
+24
-15
@@ -7,6 +7,9 @@
|
||||
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
@@ -69,7 +72,6 @@ export function DmsPage() {
|
||||
// Modal state
|
||||
const [showUpload, setShowUpload] = useState(false);
|
||||
const [showNewFolder, setShowNewFolder] = useState(false);
|
||||
const [newFolderName, setNewFolderName] = useState('');
|
||||
const [previewFile, setPreviewFile] = useState<DmsFile | null>(null);
|
||||
const [shareFile, setShareFile] = useState<DmsFile | null>(null);
|
||||
const [deleteTarget, setDeleteTarget] = useState<DmsFile | null>(null);
|
||||
@@ -77,6 +79,17 @@ export function DmsPage() {
|
||||
const [showBulkMove, setShowBulkMove] = useState(false);
|
||||
const [bulkMoveTarget, setBulkMoveTarget] = useState<string>('');
|
||||
|
||||
// ── Folder create form (RHF + Zod) ──
|
||||
const folderSchema = z.object({
|
||||
name: z.string().min(1, 'required'),
|
||||
});
|
||||
type FolderFormData = z.infer<typeof folderSchema>;
|
||||
|
||||
const { register: registerFolder, handleSubmit: handleSubmitFolder, reset: resetFolder, formState: { errors: folderErrors } } = useForm<FolderFormData>({
|
||||
resolver: zodResolver(folderSchema),
|
||||
defaultValues: { name: '' },
|
||||
});
|
||||
|
||||
// Mobile view state
|
||||
const [activeView, setActiveView] = useState<'tree' | 'files' | 'details'>('tree');
|
||||
|
||||
@@ -236,16 +249,15 @@ export function DmsPage() {
|
||||
}, [deleteTarget, selectedFile, toast, t]);
|
||||
|
||||
// Handle create folder
|
||||
const handleCreateFolder = useCallback(async () => {
|
||||
if (!newFolderName.trim()) return;
|
||||
const handleCreateFolder = useCallback(async (data: FolderFormData) => {
|
||||
setSubmittingFolder(true);
|
||||
try {
|
||||
const folder = await createFolder({
|
||||
name: newFolderName.trim(),
|
||||
name: data.name.trim(),
|
||||
parent_id: selectedFolderId,
|
||||
});
|
||||
setFolders((prev) => [...prev, folder]);
|
||||
setNewFolderName('');
|
||||
resetFolder({ name: '' });
|
||||
setShowNewFolder(false);
|
||||
toast.success(t('dms.createFolder'));
|
||||
} catch (err) {
|
||||
@@ -253,7 +265,7 @@ export function DmsPage() {
|
||||
toast.error(msg);
|
||||
}
|
||||
setSubmittingFolder(false);
|
||||
}, [newFolderName, selectedFolderId, toast, t]);
|
||||
}, [selectedFolderId, toast, t, resetFolder]);
|
||||
|
||||
// Handle upload complete
|
||||
const handleUploadComplete = useCallback(() => {
|
||||
@@ -469,25 +481,22 @@ export function DmsPage() {
|
||||
|
||||
{/* New folder form */}
|
||||
{showNewFolder && (
|
||||
<div className="mb-2 p-3 bg-secondary-50 border-b border-secondary-200" data-testid="new-folder-form">
|
||||
<form onSubmit={handleSubmitFolder(handleCreateFolder)} className="mb-2 p-3 bg-secondary-50 border-b border-secondary-200" data-testid="new-folder-form">
|
||||
<div className="flex items-center gap-3 max-w-md">
|
||||
<Input
|
||||
label={t('dms.folderName')}
|
||||
value={newFolderName}
|
||||
onChange={(e) => setNewFolderName(e.target.value)}
|
||||
{...registerFolder('name')}
|
||||
error={folderErrors.name?.message === 'required' ? t('validation.required') : undefined}
|
||||
placeholder={t('dms.folderName')}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') handleCreateFolder();
|
||||
}}
|
||||
/>
|
||||
<Button size="sm" onClick={handleCreateFolder} isLoading={submittingFolder}>
|
||||
<Button size="sm" type="submit" isLoading={submittingFolder}>
|
||||
{t('dms.createFolder')}
|
||||
</Button>
|
||||
<Button variant="secondary" size="sm" onClick={() => { setShowNewFolder(false); setNewFolderName(''); }}>
|
||||
<Button variant="secondary" size="sm" type="button" onClick={() => { setShowNewFolder(false); resetFolder({ name: '' }); }}>
|
||||
{t('dms.cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
|
||||
{/* Upload dropzone */}
|
||||
|
||||
Reference in New Issue
Block a user