1+ 'use client' ;
2+
3+ /**
4+ * Batch Edit Movie Form Component
5+ *
6+ * Form for editing multiple movies at once with validation
7+ */
8+
9+ import { useState } from 'react' ;
10+ import { Movie } from '../../types/movie' ;
11+ import styles from '../EditMovieForm/EditMovieForm.module.css' ;
12+
13+ interface BatchEditMovieFormProps {
14+ selectedCount : number ;
15+ onSave : ( updateData : Partial < Movie > ) => void ;
16+ onCancel : ( ) => void ;
17+ isLoading ?: boolean ;
18+ }
19+
20+ export default function BatchEditMovieForm ( {
21+ selectedCount,
22+ onSave,
23+ onCancel,
24+ isLoading = false
25+ } : BatchEditMovieFormProps ) {
26+ const [ formData , setFormData ] = useState ( {
27+ title : '' ,
28+ year : '' ,
29+ plot : '' ,
30+ runtime : '' ,
31+ rated : '' ,
32+ genres : '' ,
33+ directors : '' ,
34+ writers : '' ,
35+ cast : '' ,
36+ countries : '' ,
37+ languages : '' ,
38+ poster : '' ,
39+ } ) ;
40+
41+ const [ errors , setErrors ] = useState < Record < string , string > > ( { } ) ;
42+
43+ const validateForm = ( ) => {
44+ const newErrors : Record < string , string > = { } ;
45+
46+ // For batch updates, we only validate if fields have values
47+ // Empty fields will be ignored in the update
48+
49+ if ( formData . year && ( parseInt ( formData . year ) < 1800 || parseInt ( formData . year ) > new Date ( ) . getFullYear ( ) + 5 ) ) {
50+ newErrors . year = 'Please enter a valid year' ;
51+ }
52+
53+ if ( formData . runtime && ( parseInt ( formData . runtime ) < 1 || parseInt ( formData . runtime ) > 1000 ) ) {
54+ newErrors . runtime = 'Please enter a valid runtime in minutes' ;
55+ }
56+
57+ setErrors ( newErrors ) ;
58+ return Object . keys ( newErrors ) . length === 0 ;
59+ } ;
60+
61+ const handleSubmit = ( e : React . FormEvent ) => {
62+ e . preventDefault ( ) ;
63+
64+ if ( ! validateForm ( ) ) {
65+ return ;
66+ }
67+
68+ // Only include fields that have values
69+ const updateData : Partial < Movie > = { } ;
70+
71+ if ( formData . title . trim ( ) ) {
72+ updateData . title = formData . title . trim ( ) ;
73+ }
74+ if ( formData . year ) {
75+ updateData . year = parseInt ( formData . year ) ;
76+ }
77+ if ( formData . plot . trim ( ) ) {
78+ updateData . plot = formData . plot . trim ( ) ;
79+ }
80+ if ( formData . runtime ) {
81+ updateData . runtime = parseInt ( formData . runtime ) ;
82+ }
83+ if ( formData . rated . trim ( ) ) {
84+ updateData . rated = formData . rated . trim ( ) ;
85+ }
86+ if ( formData . genres . trim ( ) ) {
87+ updateData . genres = formData . genres . split ( ',' ) . map ( g => g . trim ( ) ) . filter ( g => g ) ;
88+ }
89+ if ( formData . directors . trim ( ) ) {
90+ updateData . directors = formData . directors . split ( ',' ) . map ( d => d . trim ( ) ) . filter ( d => d ) ;
91+ }
92+ if ( formData . writers . trim ( ) ) {
93+ updateData . writers = formData . writers . split ( ',' ) . map ( w => w . trim ( ) ) . filter ( w => w ) ;
94+ }
95+ if ( formData . cast . trim ( ) ) {
96+ updateData . cast = formData . cast . split ( ',' ) . map ( c => c . trim ( ) ) . filter ( c => c ) ;
97+ }
98+ if ( formData . countries . trim ( ) ) {
99+ updateData . countries = formData . countries . split ( ',' ) . map ( c => c . trim ( ) ) . filter ( c => c ) ;
100+ }
101+ if ( formData . languages . trim ( ) ) {
102+ updateData . languages = formData . languages . split ( ',' ) . map ( l => l . trim ( ) ) . filter ( l => l ) ;
103+ }
104+ if ( formData . poster . trim ( ) ) {
105+ updateData . poster = formData . poster . trim ( ) ;
106+ }
107+
108+ // Check if there's actually something to update
109+ if ( Object . keys ( updateData ) . length === 0 ) {
110+ setErrors ( { general : 'Please fill in at least one field to update' } ) ;
111+ return ;
112+ }
113+
114+ onSave ( updateData ) ;
115+ } ;
116+
117+ const handleInputChange = ( field : string , value : string ) => {
118+ setFormData ( prev => ( { ...prev , [ field ] : value } ) ) ;
119+ // Clear error when user starts typing
120+ if ( errors [ field ] ) {
121+ setErrors ( prev => ( { ...prev , [ field ] : '' } ) ) ;
122+ }
123+ // Clear general error
124+ if ( errors . general ) {
125+ setErrors ( prev => ( { ...prev , general : '' } ) ) ;
126+ }
127+ } ;
128+
129+ return (
130+ < div className = { styles . formContainer } >
131+ < h2 className = { styles . formTitle } > Batch Edit { selectedCount } Movies</ h2 >
132+ < p className = { styles . batchDescription } >
133+ Only fill in the fields you want to update. Empty fields will be left unchanged on all selected movies.
134+ </ p >
135+
136+ { errors . general && (
137+ < div className = { styles . generalError } >
138+ { errors . general }
139+ </ div >
140+ ) }
141+
142+ < form onSubmit = { handleSubmit } className = { styles . form } >
143+ < div className = { styles . formGrid } >
144+ { /* Title */ }
145+ < div className = { styles . formGroup } >
146+ < label htmlFor = "title" className = { styles . label } >
147+ Title
148+ </ label >
149+ < input
150+ type = "text"
151+ id = "title"
152+ value = { formData . title }
153+ onChange = { ( e ) => handleInputChange ( 'title' , e . target . value ) }
154+ className = { `${ styles . input } ${ errors . title ? styles . inputError : '' } ` }
155+ disabled = { isLoading }
156+ placeholder = "Leave empty to keep existing titles"
157+ />
158+ { errors . title && < span className = { styles . error } > { errors . title } </ span > }
159+ </ div >
160+
161+ { /* Year */ }
162+ < div className = { styles . formGroup } >
163+ < label htmlFor = "year" className = { styles . label } >
164+ Year
165+ </ label >
166+ < input
167+ type = "number"
168+ id = "year"
169+ value = { formData . year }
170+ onChange = { ( e ) => handleInputChange ( 'year' , e . target . value ) }
171+ className = { `${ styles . input } ${ errors . year ? styles . inputError : '' } ` }
172+ disabled = { isLoading }
173+ min = "1800"
174+ max = { new Date ( ) . getFullYear ( ) + 5 }
175+ placeholder = "Leave empty to keep existing years"
176+ />
177+ { errors . year && < span className = { styles . error } > { errors . year } </ span > }
178+ </ div >
179+
180+ { /* Runtime */ }
181+ < div className = { styles . formGroup } >
182+ < label htmlFor = "runtime" className = { styles . label } >
183+ Runtime (minutes)
184+ </ label >
185+ < input
186+ type = "number"
187+ id = "runtime"
188+ value = { formData . runtime }
189+ onChange = { ( e ) => handleInputChange ( 'runtime' , e . target . value ) }
190+ className = { `${ styles . input } ${ errors . runtime ? styles . inputError : '' } ` }
191+ disabled = { isLoading }
192+ min = "1"
193+ max = "1000"
194+ placeholder = "Leave empty to keep existing runtimes"
195+ />
196+ { errors . runtime && < span className = { styles . error } > { errors . runtime } </ span > }
197+ </ div >
198+
199+ { /* Rated */ }
200+ < div className = { styles . formGroup } >
201+ < label htmlFor = "rated" className = { styles . label } >
202+ Rating
203+ </ label >
204+ < input
205+ type = "text"
206+ id = "rated"
207+ value = { formData . rated }
208+ onChange = { ( e ) => handleInputChange ( 'rated' , e . target . value ) }
209+ className = { styles . input }
210+ disabled = { isLoading }
211+ placeholder = "e.g., PG-13, R, G"
212+ />
213+ </ div >
214+
215+ { /* Poster URL */ }
216+ < div className = { styles . formGroup } >
217+ < label htmlFor = "poster" className = { styles . label } >
218+ Poster URL
219+ </ label >
220+ < input
221+ type = "url"
222+ id = "poster"
223+ value = { formData . poster }
224+ onChange = { ( e ) => handleInputChange ( 'poster' , e . target . value ) }
225+ className = { styles . input }
226+ disabled = { isLoading }
227+ placeholder = "https://..."
228+ />
229+ </ div >
230+ </ div >
231+
232+ { /* Plot */ }
233+ < div className = { styles . formGroup } >
234+ < label htmlFor = "plot" className = { styles . label } >
235+ Plot
236+ </ label >
237+ < textarea
238+ id = "plot"
239+ value = { formData . plot }
240+ onChange = { ( e ) => handleInputChange ( 'plot' , e . target . value ) }
241+ className = { styles . textarea }
242+ disabled = { isLoading }
243+ rows = { 4 }
244+ placeholder = "Leave empty to keep existing plots..."
245+ />
246+ </ div >
247+
248+ { /* Lists (comma-separated) */ }
249+ < div className = { styles . listFields } >
250+ < div className = { styles . formGroup } >
251+ < label htmlFor = "genres" className = { styles . label } >
252+ Genres (comma-separated)
253+ </ label >
254+ < input
255+ type = "text"
256+ id = "genres"
257+ value = { formData . genres }
258+ onChange = { ( e ) => handleInputChange ( 'genres' , e . target . value ) }
259+ className = { styles . input }
260+ disabled = { isLoading }
261+ placeholder = "Action, Drama, Comedy"
262+ />
263+ </ div >
264+
265+ < div className = { styles . formGroup } >
266+ < label htmlFor = "directors" className = { styles . label } >
267+ Directors (comma-separated)
268+ </ label >
269+ < input
270+ type = "text"
271+ id = "directors"
272+ value = { formData . directors }
273+ onChange = { ( e ) => handleInputChange ( 'directors' , e . target . value ) }
274+ className = { styles . input }
275+ disabled = { isLoading }
276+ placeholder = "Director 1, Director 2"
277+ />
278+ </ div >
279+
280+ < div className = { styles . formGroup } >
281+ < label htmlFor = "writers" className = { styles . label } >
282+ Writers (comma-separated)
283+ </ label >
284+ < input
285+ type = "text"
286+ id = "writers"
287+ value = { formData . writers }
288+ onChange = { ( e ) => handleInputChange ( 'writers' , e . target . value ) }
289+ className = { styles . input }
290+ disabled = { isLoading }
291+ placeholder = "Writer 1, Writer 2"
292+ />
293+ </ div >
294+
295+ < div className = { styles . formGroup } >
296+ < label htmlFor = "cast" className = { styles . label } >
297+ Cast (comma-separated)
298+ </ label >
299+ < input
300+ type = "text"
301+ id = "cast"
302+ value = { formData . cast }
303+ onChange = { ( e ) => handleInputChange ( 'cast' , e . target . value ) }
304+ className = { styles . input }
305+ disabled = { isLoading }
306+ placeholder = "Actor 1, Actor 2, Actor 3"
307+ />
308+ </ div >
309+
310+ < div className = { styles . formGroup } >
311+ < label htmlFor = "countries" className = { styles . label } >
312+ Countries (comma-separated)
313+ </ label >
314+ < input
315+ type = "text"
316+ id = "countries"
317+ value = { formData . countries }
318+ onChange = { ( e ) => handleInputChange ( 'countries' , e . target . value ) }
319+ className = { styles . input }
320+ disabled = { isLoading }
321+ placeholder = "USA, UK, France"
322+ />
323+ </ div >
324+
325+ < div className = { styles . formGroup } >
326+ < label htmlFor = "languages" className = { styles . label } >
327+ Languages (comma-separated)
328+ </ label >
329+ < input
330+ type = "text"
331+ id = "languages"
332+ value = { formData . languages }
333+ onChange = { ( e ) => handleInputChange ( 'languages' , e . target . value ) }
334+ className = { styles . input }
335+ disabled = { isLoading }
336+ placeholder = "English, Spanish, French"
337+ />
338+ </ div >
339+ </ div >
340+
341+ { /* Form Actions */ }
342+ < div className = { styles . formActions } >
343+ < button
344+ type = "button"
345+ onClick = { onCancel }
346+ className = { `${ styles . button } ${ styles . cancelButton } ` }
347+ disabled = { isLoading }
348+ >
349+ Cancel
350+ </ button >
351+ < button
352+ type = "submit"
353+ className = { `${ styles . button } ${ styles . saveButton } ` }
354+ disabled = { isLoading }
355+ >
356+ { isLoading ? 'Updating...' : `Update ${ selectedCount } Movies` }
357+ </ button >
358+ </ div >
359+ </ form >
360+ </ div >
361+ ) ;
362+ }
0 commit comments