|
1 | 1 | import { useState, useEffect } from 'react'; |
2 | | -import { Button, Modal, Spinner, AutoForm } from '@objectql/ui'; |
| 2 | +import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, Spinner } from '@objectql/ui'; |
3 | 3 | import { getHeaders } from '../../lib/api'; |
| 4 | +import { ChevronLeft, Pencil, Trash } from 'lucide-react'; |
| 5 | +import { ObjectForm } from './ObjectForm'; |
4 | 6 |
|
5 | 7 | interface ObjectDetailViewProps { |
6 | 8 | objectName: string; |
@@ -59,77 +61,76 @@ export function ObjectDetailView({ objectName, recordId, navigate, objectSchema |
59 | 61 | }; |
60 | 62 |
|
61 | 63 | if (loading) return ( |
62 | | - <div className="flex flex-col h-full bg-white rounded-xl border border-gray-200/60 shadow-sm overflow-hidden p-8 items-center justify-center"> |
63 | | - <Spinner className="w-6 h-6 text-gray-400" /> |
| 64 | + <div className="flex flex-col h-full bg-background rounded-xl border shadow-sm overflow-hidden p-8 items-center justify-center"> |
| 65 | + <Spinner className="w-6 h-6" /> |
64 | 66 | </div> |
65 | 67 | ); |
66 | 68 |
|
67 | 69 | if (!data) return <div>Record not found</div>; |
68 | 70 |
|
69 | 71 | return ( |
70 | | - <div className="flex flex-col h-full bg-white rounded-xl border border-gray-200/60 shadow-sm overflow-hidden animate-[fadeIn_0.3s_ease-out]"> |
| 72 | + <div className="flex flex-col h-full bg-background rounded-xl border shadow-sm overflow-hidden"> |
71 | 73 | {/* Header */} |
72 | | - <div className="px-6 py-4 border-b border-gray-100 flex justify-between items-center bg-white"> |
| 74 | + <div className="px-6 py-4 border-b flex justify-between items-center bg-card"> |
73 | 75 | <div className="flex items-center gap-4"> |
74 | | - <button onClick={() => navigate(`/object/${objectName}`)} className="p-2 -ml-2 hover:bg-gray-100 rounded-full transition-colors text-gray-500"> |
75 | | - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/></svg> |
76 | | - </button> |
| 76 | + <Button variant="ghost" size="icon" onClick={() => navigate(`/object/${objectName}`)} className="rounded-full"> |
| 77 | + <ChevronLeft className="w-5 h-5"/> |
| 78 | + </Button> |
77 | 79 | <div> |
78 | | - <div className="flex items-center gap-2 text-xs text-gray-500 uppercase font-medium tracking-wider mb-0.5"> |
| 80 | + <div className="flex items-center gap-2 text-xs text-muted-foreground uppercase font-medium tracking-wider mb-0.5"> |
79 | 81 | {label} |
80 | | - <span className="text-gray-300">/</span> |
81 | | - <span className="text-gray-400">{recordId}</span> |
| 82 | + <span className="text-muted-foreground/30">/</span> |
| 83 | + <span className="text-muted-foreground">{recordId}</span> |
82 | 84 | </div> |
83 | | - <h1 className="text-xl font-bold text-gray-900">{data.name || data.title || recordId}</h1> |
| 85 | + <h1 className="text-xl font-bold text-foreground">{data.name || data.title || recordId}</h1> |
84 | 86 | </div> |
85 | 87 | </div> |
86 | 88 |
|
87 | 89 | <div className="flex gap-2"> |
88 | | - <Button variant="secondary" onClick={() => setIsEditing(true)} className="gap-2"> |
89 | | - <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg> |
| 90 | + <Button variant="outline" onClick={() => setIsEditing(true)} className="gap-2"> |
| 91 | + <Pencil className="w-4 h-4" /> |
90 | 92 | Edit |
91 | 93 | </Button> |
92 | | - <Button variant="secondary" onClick={handleDelete} className="hover:bg-red-50 hover:text-red-600 gap-2 border-transparent"> |
93 | | - <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg> |
| 94 | + <Button variant="outline" onClick={handleDelete} className="hover:bg-destructive/10 hover:text-destructive gap-2 border-transparent text-destructive"> |
| 95 | + <Trash className="w-4 h-4" /> |
94 | 96 | Delete |
95 | 97 | </Button> |
96 | 98 | </div> |
97 | 99 | </div> |
98 | 100 |
|
99 | 101 | {/* Content */} |
100 | | - <div className="flex-1 overflow-auto bg-gray-50/50 p-6"> |
101 | | - <div className="bg-white rounded-xl border border-gray-200 shadow-sm p-6 max-w-4xl mx-auto"> |
102 | | - {schema ? ( |
103 | | - <AutoForm |
104 | | - schema={schema} |
105 | | - initialValues={data} |
106 | | - readonly={true} |
107 | | - onSubmit={() => {}} |
108 | | - /> |
109 | | - ) : ( |
110 | | - <div className="grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-8"> |
111 | | - {Object.entries(data).map(([key, value]) => ( |
112 | | - <div key={key} className="space-y-1.5 border-b border-gray-50 pb-2"> |
113 | | - <div className="text-xs font-medium text-gray-400 uppercase tracking-wide">{key}</div> |
114 | | - <div className="text-sm text-gray-900 font-medium break-words"> |
115 | | - {typeof value === 'object' ? JSON.stringify(value) : String(value ?? '-')} |
116 | | - </div> |
117 | | - </div> |
118 | | - ))} |
119 | | - </div> |
120 | | - )} |
| 102 | + <div className="flex-1 overflow-auto bg-muted/20 p-6"> |
| 103 | + <div className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl"> |
| 104 | + {Object.entries(data).map(([key, value]) => { |
| 105 | + if (['id', '_id', '__v'].includes(key)) return null; |
| 106 | + const fieldLabel = schema?.fields?.[key]?.label || key; |
| 107 | + |
| 108 | + return ( |
| 109 | + <div key={key} className="space-y-1"> |
| 110 | + <div className="text-sm font-medium text-muted-foreground capitalize">{fieldLabel}</div> |
| 111 | + <div className="text-base font-medium break-words"> |
| 112 | + {typeof value === 'object' ? JSON.stringify(value) : String(value)} |
| 113 | + </div> |
| 114 | + </div> |
| 115 | + ) |
| 116 | + })} |
121 | 117 | </div> |
122 | 118 | </div> |
123 | 119 |
|
124 | | - {/* Edit Modal */} |
125 | | - <Modal isOpen={isEditing} onClose={() => setIsEditing(false)} title={`Edit ${label}`}> |
126 | | - <AutoForm |
127 | | - schema={schema} |
128 | | - initialValues={data} |
129 | | - onSubmit={handleUpdate} |
130 | | - onCancel={() => setIsEditing(false)} |
131 | | - /> |
132 | | - </Modal> |
| 120 | + <Dialog open={isEditing} onOpenChange={setIsEditing}> |
| 121 | + <DialogContent> |
| 122 | + <DialogHeader> |
| 123 | + <DialogTitle>Edit {label}</DialogTitle> |
| 124 | + </DialogHeader> |
| 125 | + <ObjectForm |
| 126 | + objectName={objectName} |
| 127 | + initialValues={data} |
| 128 | + headers={getHeaders()} |
| 129 | + onSubmit={handleUpdate} |
| 130 | + onCancel={() => setIsEditing(false)} |
| 131 | + /> |
| 132 | + </DialogContent> |
| 133 | + </Dialog> |
133 | 134 | </div> |
134 | 135 | ); |
135 | 136 | } |
0 commit comments