@@ -182,146 +182,256 @@ const StatementItem: React.FC<StatementItemProps> = ({
182182
183183 if ( isEditing ) {
184184 return (
185- < div className = 'flex items-center space-x-2 bg-gray-100 p-2 rounded' >
186- { /* Privacy toggle button */ }
187- < Button
188- variant = { draft . isPublic ? 'success' : 'destructive' }
189- size = 'compact'
190- onClick = { ( ) => {
191- // Create a new draft object to ensure React detects the change
192- setDraft ( ( prevDraft ) => {
193- const newDraft = JSON . parse ( JSON . stringify ( prevDraft ) ) ;
194- newDraft . isPublic = ! prevDraft . isPublic ;
195- return newDraft ;
196- } ) ;
197- } }
198- className = 'p-2 transition-colors shadow-sm'
199- >
200- { draft . isPublic ? < MailPlus size = { 16 } /> : < MailX size = { 16 } /> }
201- </ Button >
185+ < div className = 'bg-gray-100 p-3 rounded-lg shadow' >
186+ { /* Desktop layout - horizontal row */ }
187+ < div className = 'hidden md:flex md:items-center md:space-x-2' >
188+ { /* Privacy toggle button */ }
189+ < Button
190+ variant = { draft . isPublic ? 'success' : 'destructive' }
191+ size = 'compact'
192+ onClick = { ( ) => {
193+ // Create a new draft object to ensure React detects the change
194+ setDraft ( ( prevDraft ) => {
195+ const newDraft = JSON . parse ( JSON . stringify ( prevDraft ) ) ;
196+ newDraft . isPublic = ! prevDraft . isPublic ;
197+ return newDraft ;
198+ } ) ;
199+ } }
200+ className = 'p-2 transition-colors shadow-sm'
201+ >
202+ { draft . isPublic ? < MailPlus size = { 16 } /> : < MailX size = { 16 } /> }
203+ </ Button >
202204
203- < div className = 'flex flex-1 items-center flex-wrap gap-2' >
204- < div className = 'flex space-x-2 flex-wrap' >
205- { /* Subject */ }
205+ < div className = 'flex flex-1 items-center flex-wrap gap-2' >
206+ < div className = 'flex space-x-2 flex-wrap' >
207+ { /* Subject */ }
208+ < div
209+ onClick = { ( ) => onPartClick ( 'subject' , draft . id ) }
210+ className = 'cursor-pointer px-2 py-1 rounded bg-subjectSelector hover:bg-subjectSelectorHover'
211+ >
212+ { draft . atoms . subject }
213+ </ div >
214+ { /* Verb */ }
215+ < div
216+ onClick = { ( ) => onPartClick ( 'verb' , draft . id ) }
217+ className = 'cursor-pointer px-2 py-1 rounded bg-verbSelector hover:bg-verbSelectorHover'
218+ >
219+ < span > { getVerbName ( draft . atoms . verb ) } </ span >
220+ </ div >
221+ { /* Object */ }
222+ < div
223+ onClick = { ( ) => onPartClick ( 'object' , draft . id ) }
224+ className = 'cursor-pointer px-2 py-1 rounded bg-objectInput hover:bg-objectInputHover'
225+ >
226+ { draft . atoms . object }
227+ </ div >
228+ </ div >
229+ { /* Category */ }
206230 < div
231+ onClick = { ( ) => onPartClick ( 'category' , draft . id ) }
232+ className = 'cursor-pointer px-2 py-1 rounded bg-categorySelector text-black flex items-center gap-1 hover:bg-categorySelectorHover'
233+ >
234+ < span className = 'mr-1' > 📁</ span >
235+ { /* Use formatted category name */ }
236+ { draft . category &&
237+ draft . category . toLowerCase ( ) !== 'uncategorized' &&
238+ draft . category . toLowerCase ( ) !== 'uncategorised'
239+ ? getCategoryDisplayName ( draft . category )
240+ : 'Uncategorized' }
241+ </ div >
242+ </ div >
243+
244+ < div className = 'flex items-center space-x-2 ml-auto' >
245+ { /* Save button with tooltip */ }
246+ < Tooltip >
247+ < TooltipTrigger asChild >
248+ < span className = 'inline-block' >
249+ < Button
250+ variant = 'success'
251+ size = 'compact'
252+ onClick = { async ( ) => {
253+ setIsSaving ( true ) ;
254+ const updatedDraft = { ...draft } ;
255+ updatedDraft . input = `${ draft . atoms . subject } ${ getVerbName (
256+ draft . atoms . verb
257+ ) } ${ draft . atoms . object } `;
258+ await onLocalSave ( updatedDraft ) ;
259+ setIsSaving ( false ) ;
260+ } }
261+ disabled = { ! hasChanged || isSaving }
262+ className = 'shadow-sm p-2'
263+ >
264+ < Save size = { 16 } />
265+ </ Button >
266+ </ span >
267+ </ TooltipTrigger >
268+ < TooltipContent className = 'p-2 bg-black text-white rounded' >
269+ Save changes
270+ </ TooltipContent >
271+ </ Tooltip >
272+
273+ { /* Cancel button with PenOff icon and tooltip */ }
274+ < Tooltip >
275+ < TooltipTrigger asChild >
276+ < span className = 'inline-block' >
277+ < Button
278+ variant = 'outline'
279+ size = 'compact'
280+ onClick = { ( ) => {
281+ setDraft ( JSON . parse ( JSON . stringify ( initialDraft ) ) ) ;
282+ if ( onCancel ) onCancel ( statement . id ) ;
283+ } }
284+ className = 'p-2'
285+ >
286+ < PenOff size = { 16 } />
287+ </ Button >
288+ </ span >
289+ </ TooltipTrigger >
290+ < TooltipContent className = 'p-2 bg-black text-white rounded' >
291+ Cancel editing
292+ </ TooltipContent >
293+ </ Tooltip >
294+
295+ { /* Delete button with tooltip */ }
296+ < Tooltip >
297+ < TooltipTrigger asChild >
298+ < span className = 'inline-block' >
299+ < Button
300+ variant = 'destructive'
301+ size = 'compact'
302+ onClick = { ( ) => onDelete ( draft . id ) }
303+ className = 'shadow-sm p-2'
304+ >
305+ < Trash2 size = { 16 } />
306+ </ Button >
307+ </ span >
308+ </ TooltipTrigger >
309+ < TooltipContent className = 'p-2 bg-black text-white rounded' >
310+ Delete statement
311+ </ TooltipContent >
312+ </ Tooltip >
313+ </ div >
314+ </ div >
315+
316+ { /* Mobile layout - vertical stack */ }
317+ < div className = 'md:hidden flex flex-col space-y-4' >
318+ { /* Statement header with privacy toggle */ }
319+ < div className = 'flex items-center justify-between pb-2 border-b border-gray-300' >
320+ < h3 className = 'text-sm font-semibold text-gray-700' > Edit Statement</ h3 >
321+ < Button
322+ variant = { draft . isPublic ? 'success' : 'destructive' }
323+ size = 'compact'
207324 onClick = { ( ) => {
208- // Just call onPartClick to open the modal, and don't try to edit inline
209- onPartClick ( 'subject' , draft . id ) ;
325+ setDraft ( ( prevDraft ) => {
326+ const newDraft = JSON . parse ( JSON . stringify ( prevDraft ) ) ;
327+ newDraft . isPublic = ! prevDraft . isPublic ;
328+ return newDraft ;
329+ } ) ;
210330 } }
211- className = 'cursor-pointer px-2 py-1 rounded bg-subjectSelector hover:bg-subjectSelectorHover'
331+ className = 'p-2 transition-colors shadow-sm'
332+ >
333+ { draft . isPublic ?
334+ < > < MailPlus size = { 16 } /> < span className = "hidden sm:inline ml-1 text-xs" > Public</ span > </ > :
335+ < > < MailX size = { 16 } /> < span className = "hidden sm:inline ml-1 text-xs" > Private</ span > </ >
336+ }
337+ </ Button >
338+ </ div >
339+
340+ { /* Statement parts grid */ }
341+ < div className = 'grid grid-cols-2 gap-2' >
342+ { /* Subject */ }
343+ < div
344+ onClick = { ( ) => onPartClick ( 'subject' , draft . id ) }
345+ className = 'cursor-pointer p-3 rounded bg-subjectSelector hover:bg-subjectSelectorHover flex flex-col'
212346 >
213- { draft . atoms . subject }
347+ < span className = 'text-xs mb-1 opacity-70' > Subject</ span >
348+ < span className = 'font-medium truncate' > { draft . atoms . subject } </ span >
214349 </ div >
350+
215351 { /* Verb */ }
216352 < div
217- onClick = { ( ) => {
218- // Just call onPartClick to open the modal, and don't try to edit inline
219- onPartClick ( 'verb' , draft . id ) ;
220- } }
221- className = 'cursor-pointer px-2 py-1 rounded bg-verbSelector hover:bg-verbSelectorHover'
353+ onClick = { ( ) => onPartClick ( 'verb' , draft . id ) }
354+ className = 'cursor-pointer p-3 rounded bg-verbSelector hover:bg-verbSelectorHover flex flex-col'
222355 >
223- < span > { getVerbName ( draft . atoms . verb ) } </ span >
356+ < span className = 'text-xs mb-1 opacity-70' > Verb</ span >
357+ < span className = 'font-medium truncate' > { getVerbName ( draft . atoms . verb ) } </ span >
224358 </ div >
359+
225360 { /* Object */ }
226361 < div
227- onClick = { ( ) => {
228- // Just call onPartClick to open the modal, and don't try to edit inline
229- onPartClick ( 'object' , draft . id ) ;
230- } }
231- className = 'cursor-pointer px-2 py-1 rounded bg-objectInput hover:bg-objectInputHover'
362+ onClick = { ( ) => onPartClick ( 'object' , draft . id ) }
363+ className = 'cursor-pointer p-3 rounded bg-objectInput hover:bg-objectInputHover flex flex-col'
364+ >
365+ < span className = 'text-xs mb-1 opacity-70' > Object</ span >
366+ < span className = 'font-medium truncate' > { draft . atoms . object } </ span >
367+ </ div >
368+
369+ { /* Category */ }
370+ < div
371+ onClick = { ( ) => onPartClick ( 'category' , draft . id ) }
372+ className = 'cursor-pointer p-3 rounded bg-categorySelector hover:bg-categorySelectorHover flex flex-col'
232373 >
233- { draft . atoms . object }
374+ < span className = 'text-xs mb-1 opacity-70' > Category</ span >
375+ < span className = 'font-medium truncate' >
376+ { draft . category &&
377+ draft . category . toLowerCase ( ) !== 'uncategorized' &&
378+ draft . category . toLowerCase ( ) !== 'uncategorised'
379+ ? getCategoryDisplayName ( draft . category )
380+ : 'Uncategorized' }
381+ </ span >
234382 </ div >
235383 </ div >
236- { /* Category */ }
237- < div
238- onClick = { ( ) => {
239- // Open the category modal
240- onPartClick ( 'category' , draft . id ) ;
241- } }
242- className = 'cursor-pointer px-2 py-1 rounded bg-categorySelector text-black flex items-center gap-1 hover:bg-categorySelectorHover'
243- >
244- < span className = 'mr-1' > 📁</ span >
245- { /* Use formatted category name */ }
246- { draft . category &&
247- draft . category . toLowerCase ( ) !== 'uncategorized' &&
248- draft . category . toLowerCase ( ) !== 'uncategorised'
249- ? getCategoryDisplayName ( draft . category )
250- : 'Uncategorized' }
384+
385+ { /* Action buttons - bottom fixed bar */ }
386+ < div className = 'flex justify-between items-center pt-3 mt-2 border-t border-gray-300' >
387+ { /* Delete button */ }
388+ < Button
389+ variant = 'destructive'
390+ size = 'compact'
391+ onClick = { ( ) => onDelete ( draft . id ) }
392+ className = 'min-w-[40px] xs:px-3 py-2 flex justify-center'
393+ >
394+ < Trash2 size = { 16 } className = "xs:mr-1" />
395+ < span className = "hidden xs:inline text-xs" > Delete</ span >
396+ </ Button >
397+
398+ < div className = 'flex space-x-2' >
399+ { /* Cancel button */ }
400+ < Button
401+ variant = 'outline'
402+ size = 'compact'
403+ onClick = { ( ) => {
404+ setDraft ( JSON . parse ( JSON . stringify ( initialDraft ) ) ) ;
405+ if ( onCancel ) onCancel ( statement . id ) ;
406+ } }
407+ className = 'min-w-[40px] xs:px-3 py-2 flex justify-center'
408+ >
409+ < PenOff size = { 16 } className = "xs:mr-1" />
410+ < span className = "hidden xs:inline text-xs" > Cancel</ span >
411+ </ Button >
412+
413+ { /* Save button */ }
414+ < Button
415+ variant = 'success'
416+ size = 'compact'
417+ onClick = { async ( ) => {
418+ setIsSaving ( true ) ;
419+ const updatedDraft = { ...draft } ;
420+ updatedDraft . input = `${ draft . atoms . subject } ${ getVerbName (
421+ draft . atoms . verb
422+ ) } ${ draft . atoms . object } `;
423+ await onLocalSave ( updatedDraft ) ;
424+ setIsSaving ( false ) ;
425+ } }
426+ disabled = { ! hasChanged || isSaving }
427+ className = 'min-w-[40px] xs:px-3 py-2 flex justify-center'
428+ >
429+ < Save size = { 16 } className = "xs:mr-1" />
430+ < span className = "hidden xs:inline text-xs" > Save</ span >
431+ </ Button >
432+ </ div >
251433 </ div >
252434 </ div >
253- < div className = 'flex items-center space-x-2 ml-auto' >
254- { /* Save button with tooltip */ }
255- < Tooltip >
256- < TooltipTrigger asChild >
257- < span className = 'inline-block' >
258- < Button
259- variant = 'success'
260- size = 'compact'
261- onClick = { async ( ) => {
262- setIsSaving ( true ) ;
263- // Update the input field to reflect the edited statement
264- const updatedDraft = { ...draft } ;
265- updatedDraft . input = `${ draft . atoms . subject } ${ getVerbName (
266- draft . atoms . verb
267- ) } ${ draft . atoms . object } `;
268- await onLocalSave ( updatedDraft ) ;
269- setIsSaving ( false ) ;
270- } }
271- disabled = { ! hasChanged || isSaving }
272- className = 'shadow-sm p-2'
273- >
274- < Save size = { 16 } />
275- </ Button >
276- </ span >
277- </ TooltipTrigger >
278- < TooltipContent className = 'p-2 bg-black text-white rounded' >
279- Save changes
280- </ TooltipContent >
281- </ Tooltip >
282-
283- { /* Cancel button with PenOff icon and tooltip */ }
284- < Tooltip >
285- < TooltipTrigger asChild >
286- < span className = 'inline-block' >
287- < Button
288- variant = 'outline'
289- size = 'compact'
290- onClick = { ( ) => {
291- // Deep clone to avoid reference issues
292- setDraft ( JSON . parse ( JSON . stringify ( initialDraft ) ) ) ;
293- if ( onCancel ) onCancel ( statement . id ) ;
294- } }
295- className = 'p-2'
296- >
297- < PenOff size = { 16 } />
298- </ Button >
299- </ span >
300- </ TooltipTrigger >
301- < TooltipContent className = 'p-2 bg-black text-white rounded' >
302- Cancel editing
303- </ TooltipContent >
304- </ Tooltip >
305-
306- { /* Delete button with tooltip */ }
307- < Tooltip >
308- < TooltipTrigger asChild >
309- < span className = 'inline-block' >
310- < Button
311- variant = 'destructive'
312- size = 'compact'
313- onClick = { ( ) => onDelete ( draft . id ) }
314- className = 'shadow-sm p-2'
315- >
316- < Trash2 size = { 16 } />
317- </ Button >
318- </ span >
319- </ TooltipTrigger >
320- < TooltipContent className = 'p-2 bg-black text-white rounded' >
321- Delete statement
322- </ TooltipContent >
323- </ Tooltip >
324- </ div >
325435 </ div >
326436 ) ;
327437 }
0 commit comments