@object-ui/plugin-detail 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,8 +7,32 @@
7
7
  */
8
8
 
9
9
  import * as React from 'react';
10
- import { cn, Button, Skeleton } from '@object-ui/components';
11
- import { ArrowLeft, Edit, Trash2, MoreHorizontal } from 'lucide-react';
10
+ import {
11
+ cn,
12
+ Button,
13
+ Skeleton,
14
+ DropdownMenu,
15
+ DropdownMenuContent,
16
+ DropdownMenuItem,
17
+ DropdownMenuSeparator,
18
+ DropdownMenuTrigger,
19
+ Tooltip,
20
+ TooltipContent,
21
+ TooltipProvider,
22
+ TooltipTrigger,
23
+ } from '@object-ui/components';
24
+ import {
25
+ ArrowLeft,
26
+ Edit,
27
+ Trash2,
28
+ MoreHorizontal,
29
+ Share2,
30
+ Copy,
31
+ Download,
32
+ History,
33
+ Star,
34
+ StarOff,
35
+ } from 'lucide-react';
12
36
  import { DetailSection } from './DetailSection';
13
37
  import { DetailTabs } from './DetailTabs';
14
38
  import { RelatedList } from './RelatedList';
@@ -34,6 +58,7 @@ export const DetailView: React.FC<DetailViewProps> = ({
34
58
  }) => {
35
59
  const [data, setData] = React.useState<any>(schema.data);
36
60
  const [loading, setLoading] = React.useState(!schema.data && !!((schema.api && schema.resourceId) || (dataSource && schema.objectName && schema.resourceId)));
61
+ const [isFavorite, setIsFavorite] = React.useState(false);
37
62
 
38
63
  // Fetch data if API or DataSource provided
39
64
  React.useEffect(() => {
@@ -108,6 +133,41 @@ export const DetailView: React.FC<DetailViewProps> = ({
108
133
  }
109
134
  }, [onDelete, schema]);
110
135
 
136
+ const handleShare = React.useCallback(() => {
137
+ // Share functionality - could trigger share dialog or copy link
138
+ if (navigator.share && schema.objectName && schema.resourceId) {
139
+ navigator.share({
140
+ title: schema.title || 'Record Details',
141
+ text: `${schema.objectName} #${schema.resourceId}`,
142
+ url: window.location.href,
143
+ }).catch((err) => console.log('Share failed:', err));
144
+ } else {
145
+ // Fallback: copy link to clipboard
146
+ navigator.clipboard.writeText(window.location.href).then(() => {
147
+ console.log('Link copied to clipboard');
148
+ });
149
+ }
150
+ }, [schema]);
151
+
152
+ const handleDuplicate = React.useCallback(() => {
153
+ // Duplicate functionality - could navigate to create page with prefilled data
154
+ console.log('Duplicate record:', data);
155
+ }, [data]);
156
+
157
+ const handleExport = React.useCallback(() => {
158
+ // Export functionality - could download as JSON, PDF, etc.
159
+ console.log('Export record:', data);
160
+ }, [data]);
161
+
162
+ const handleViewHistory = React.useCallback(() => {
163
+ // View history functionality
164
+ console.log('View history for record:', schema.resourceId);
165
+ }, [schema]);
166
+
167
+ const handleToggleFavorite = React.useCallback(() => {
168
+ setIsFavorite(!isFavorite);
169
+ }, [isFavorite]);
170
+
111
171
  if (loading || schema.loading) {
112
172
  return (
113
173
  <div className={cn('space-y-4', className)}>
@@ -119,49 +179,123 @@ export const DetailView: React.FC<DetailViewProps> = ({
119
179
  }
120
180
 
121
181
  return (
122
- <div className={cn('space-y-6', className)}>
123
- {/* Header */}
124
- <div className="flex items-center justify-between">
125
- <div className="flex items-center gap-4">
126
- {(schema.showBack ?? true) && (
127
- <Button variant="ghost" size="icon" onClick={handleBack}>
128
- <ArrowLeft className="h-4 w-4" />
129
- </Button>
130
- )}
131
- <div>
132
- <h1 className="text-2xl font-bold">{schema.title || 'Details'}</h1>
133
- {schema.objectName && (
134
- <p className="text-sm text-muted-foreground mt-1">
135
- {schema.objectName} #{schema.resourceId}
136
- </p>
182
+ <TooltipProvider>
183
+ <div className={cn('space-y-6', className)}>
184
+ {/* Header - Airtable-inspired layout */}
185
+ <div className="flex flex-col sm:flex-row items-start justify-between gap-3 sm:gap-4 pb-4 border-b">
186
+ <div className="flex items-start gap-2 sm:gap-3 flex-1 min-w-0">
187
+ {(schema.showBack ?? true) && (
188
+ <Tooltip>
189
+ <TooltipTrigger asChild>
190
+ <Button variant="ghost" size="icon" onClick={handleBack} className="shrink-0 mt-1">
191
+ <ArrowLeft className="h-4 w-4" />
192
+ </Button>
193
+ </TooltipTrigger>
194
+ <TooltipContent>Back</TooltipContent>
195
+ </Tooltip>
137
196
  )}
197
+ <div className="flex-1 min-w-0">
198
+ <div className="flex items-center gap-2">
199
+ <h1 className="text-xl sm:text-2xl font-bold truncate">{schema.title || 'Details'}</h1>
200
+ <Tooltip>
201
+ <TooltipTrigger asChild>
202
+ <Button
203
+ variant="ghost"
204
+ size="icon"
205
+ className="h-6 w-6 shrink-0"
206
+ onClick={handleToggleFavorite}
207
+ >
208
+ {isFavorite ? (
209
+ <Star className="h-4 w-4 fill-yellow-400 text-yellow-400" />
210
+ ) : (
211
+ <StarOff className="h-4 w-4 text-muted-foreground" />
212
+ )}
213
+ </Button>
214
+ </TooltipTrigger>
215
+ <TooltipContent>
216
+ {isFavorite ? 'Remove from favorites' : 'Add to favorites'}
217
+ </TooltipContent>
218
+ </Tooltip>
219
+ </div>
220
+ {schema.objectName && (
221
+ <p className="text-sm text-muted-foreground mt-1 flex items-center gap-1.5">
222
+ <span className="font-medium">{schema.objectName}</span>
223
+ <span className="text-muted-foreground/60">•</span>
224
+ <span>#{schema.resourceId}</span>
225
+ </p>
226
+ )}
227
+ </div>
138
228
  </div>
139
- </div>
140
229
 
141
- <div className="flex items-center gap-2">
142
- {schema.actions?.map((action, index) => (
143
- <SchemaRenderer key={index} schema={action} data={data} />
144
- ))}
230
+ <div className="flex flex-wrap items-center gap-1.5 shrink-0 w-full sm:w-auto">
231
+ {schema.actions?.map((action, index) => (
232
+ <SchemaRenderer key={index} schema={action} data={data} />
233
+ ))}
234
+
235
+ {/* Share Button */}
236
+ <Tooltip>
237
+ <TooltipTrigger asChild>
238
+ <Button variant="outline" size="icon" onClick={handleShare}>
239
+ <Share2 className="h-4 w-4" />
240
+ </Button>
241
+ </TooltipTrigger>
242
+ <TooltipContent>Share</TooltipContent>
243
+ </Tooltip>
244
+
245
+ {/* Edit Button */}
246
+ {schema.showEdit && (
247
+ <Tooltip>
248
+ <TooltipTrigger asChild>
249
+ <Button variant="default" onClick={handleEdit} className="gap-2">
250
+ <Edit className="h-4 w-4" />
251
+ <span className="hidden sm:inline">Edit</span>
252
+ </Button>
253
+ </TooltipTrigger>
254
+ <TooltipContent>Edit record</TooltipContent>
255
+ </Tooltip>
256
+ )}
145
257
 
146
- {schema.showEdit && (
147
- <Button variant="outline" onClick={handleEdit}>
148
- <Edit className="h-4 w-4 mr-2" />
149
- Edit
150
- </Button>
151
- )}
152
-
153
- {schema.showDelete && (
154
- <Button variant="destructive" onClick={handleDelete}>
155
- <Trash2 className="h-4 w-4 mr-2" />
156
- Delete
157
- </Button>
158
- )}
159
-
160
- <Button variant="ghost" size="icon">
161
- <MoreHorizontal className="h-4 w-4" />
162
- </Button>
258
+ {/* More Actions Menu */}
259
+ <DropdownMenu>
260
+ <Tooltip>
261
+ <TooltipTrigger asChild>
262
+ <DropdownMenuTrigger asChild>
263
+ <Button variant="ghost" size="icon">
264
+ <MoreHorizontal className="h-4 w-4" />
265
+ </Button>
266
+ </DropdownMenuTrigger>
267
+ </TooltipTrigger>
268
+ <TooltipContent>More actions</TooltipContent>
269
+ </Tooltip>
270
+ <DropdownMenuContent align="end" className="w-[calc(100vw-2rem)] sm:w-48 max-h-[60vh] overflow-y-auto">
271
+ <DropdownMenuItem onClick={handleDuplicate}>
272
+ <Copy className="h-4 w-4 mr-2" />
273
+ Duplicate
274
+ </DropdownMenuItem>
275
+ <DropdownMenuItem onClick={handleExport}>
276
+ <Download className="h-4 w-4 mr-2" />
277
+ Export
278
+ </DropdownMenuItem>
279
+ <DropdownMenuItem onClick={handleViewHistory}>
280
+ <History className="h-4 w-4 mr-2" />
281
+ View history
282
+ </DropdownMenuItem>
283
+ {schema.showDelete && (
284
+ <>
285
+ <DropdownMenuSeparator />
286
+ <DropdownMenuItem
287
+ onClick={handleDelete}
288
+ className="text-destructive focus:text-destructive"
289
+ >
290
+ <Trash2 className="h-4 w-4 mr-2" />
291
+ Delete
292
+ </DropdownMenuItem>
293
+ </>
294
+ )}
295
+ </DropdownMenuContent>
296
+ </DropdownMenu>
297
+ </div>
163
298
  </div>
164
- </div>
165
299
 
166
300
  {/* Custom Header */}
167
301
  {schema.header && (
@@ -172,7 +306,7 @@ export const DetailView: React.FC<DetailViewProps> = ({
172
306
 
173
307
  {/* Sections */}
174
308
  {schema.sections && schema.sections.length > 0 && (
175
- <div className="space-y-4">
309
+ <div className="space-y-3 sm:space-y-4">
176
310
  {schema.sections.map((section, index) => (
177
311
  <DetailSection
178
312
  key={index}
@@ -223,6 +357,7 @@ export const DetailView: React.FC<DetailViewProps> = ({
223
357
  <SchemaRenderer schema={schema.footer} data={data} />
224
358
  </div>
225
359
  )}
226
- </div>
360
+ </div>
361
+ </TooltipProvider>
227
362
  );
228
363
  };