Skip to content

Commit c0d0360

Browse files
committed
Add lucide-react dependency and update App component with new layout and dialog features
1 parent e7a7c0c commit c0d0360

File tree

4 files changed

+158
-125
lines changed

4 files changed

+158
-125
lines changed

examples/msw-object-form/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@objectstack/plugin-msw": "^0.7.2",
2828
"@objectstack/runtime": "^0.7.2",
2929
"@objectstack/spec": "^0.7.2",
30+
"lucide-react": "^0.563.0",
3031
"react": "^19.0.0",
3132
"react-dom": "^19.0.0"
3233
},

examples/msw-object-form/src/App.tsx

Lines changed: 135 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@
77
import { useState, useEffect } from 'react';
88
import { ObjectStackClient } from '@objectstack/client';
99
import { ObjectForm } from '@object-ui/plugin-form';
10+
import { ObjectGrid } from '@object-ui/plugin-grid';
11+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Button } from '@object-ui/components';
1012
import { ObjectStackDataSource } from './dataSource';
11-
import { ContactList } from './components/ContactList';
1213
import type { Contact } from './types';
1314
import './App.css';
15+
import { Plus, LayoutDashboard, Users, Settings } from 'lucide-react';
1416

1517
export function App() {
1618
const [client, setClient] = useState<ObjectStackClient | null>(null);
1719
const [dataSource, setDataSource] = useState<ObjectStackDataSource | null>(null);
18-
const [editingContact, setEditingContact] = useState<Contact | null>(null);
19-
const [refreshTrigger, setRefreshTrigger] = useState(0);
20+
const [isDialogOpen, setIsDialogOpen] = useState(false);
21+
const [editingRecord, setEditingRecord] = useState<Contact | null>(null);
2022
const [connected, setConnected] = useState(false);
2123
const [error, setError] = useState<string | null>(null);
24+
const [refreshKey, setRefreshKey] = useState(0);
2225

2326
useEffect(() => {
2427
initializeClient();
@@ -47,26 +50,24 @@ export function App() {
4750
}
4851
}
4952

50-
function handleFormSuccess() {
51-
setEditingContact(null);
52-
setRefreshTrigger(prev => prev + 1);
53-
}
53+
const handleCreate = () => {
54+
setEditingRecord(null);
55+
setIsDialogOpen(true);
56+
};
5457

55-
function handleEditContact(contact: Contact) {
56-
setEditingContact(contact);
57-
// Scroll to top on mobile
58-
if (window.innerWidth < 1024) {
59-
window.scrollTo({ top: 0, behavior: 'smooth' });
60-
}
61-
}
58+
const handleEdit = (record: any) => {
59+
setEditingRecord(record);
60+
setIsDialogOpen(true);
61+
};
6262

63-
function handleCancelEdit() {
64-
setEditingContact(null);
63+
const handleSuccess = () => {
64+
setIsDialogOpen(false);
65+
setRefreshKey(k => k + 1);
6566
}
6667

6768
if (error) {
6869
return (
69-
<div className="min-h-screen flex items-center justify-center p-6">
70+
<div className="min-h-screen flex items-center justify-center p-6 bg-slate-50">
7071
<div className="max-w-md w-full p-8 border border-red-300 bg-red-50 rounded-lg shadow-sm text-center">
7172
<h1 className="text-xl font-bold text-red-800 mb-2">Connection Error</h1>
7273
<p className="text-red-600 mb-6">{error}</p>
@@ -83,100 +84,135 @@ export function App() {
8384

8485
if (!connected || !client || !dataSource) {
8586
return (
86-
<div className="min-h-screen flex flex-col items-center justify-center p-6 space-y-4">
87+
<div className="min-h-screen flex flex-col items-center justify-center p-6 space-y-4 bg-slate-50">
8788
<div className="w-8 h-8 rounded-full border-4 border-gray-200 border-t-gray-900 animate-spin"></div>
8889
<div className="text-center">
89-
<h1 className="text-lg font-semibold mb-1">Connecting to ObjectStack...</h1>
90+
<h1 className="text-lg font-semibold mb-1 text-gray-900">Connecting to ObjectStack...</h1>
9091
<p className="text-gray-600 text-sm">Initializing MSW and ObjectStack Client...</p>
9192
</div>
9293
</div>
9394
);
9495
}
9596

9697
return (
97-
<div className="max-w-7xl mx-auto px-6 py-12 min-h-screen">
98-
<header className="text-center mb-12">
99-
<h1 className="text-4xl font-bold tracking-tight mb-4">
100-
ObjectForm + MSW Integration
101-
</h1>
102-
<p className="text-gray-600 text-lg mb-6">
103-
Complete CRUD operations using <code className="text-sm bg-gray-100 px-2 py-1 rounded font-mono">ObjectForm</code> with Mock Service Worker
104-
</p>
105-
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-green-50 border border-green-200 text-green-700 text-sm font-medium">
106-
<span className="relative flex h-2 w-2">
107-
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
108-
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
109-
</span>
110-
MSW Active - All API calls are mocked
111-
</div>
112-
</header>
113-
114-
<main className="grid grid-cols-1 lg:grid-cols-12 gap-8">
115-
{/* Form Section */}
116-
<section className="lg:col-span-5 lg:sticky lg:top-6 lg:self-start">
117-
<div className="bg-white p-6 rounded-lg border border-gray-200 shadow-sm">
118-
<h2 className="text-xl font-bold mb-4">
119-
{editingContact ? 'Edit Contact' : 'Create New Contact'}
120-
</h2>
121-
122-
<ObjectForm
123-
schema={{
124-
type: 'object-form',
125-
objectName: 'contact',
126-
mode: editingContact ? 'edit' : 'create',
127-
recordId: editingContact?.id,
128-
fields: [
129-
'name',
130-
'email',
131-
'phone',
132-
'company',
133-
'position',
134-
'priority',
135-
'salary', // Added
136-
'commission_rate', // Added
137-
'birthdate', // Added
138-
'last_contacted', // Added
139-
'available_time', // Added
140-
'profile_url', // Added
141-
'department', // Added
142-
'resume', // Added
143-
'avatar', // Added
144-
'is_active',
145-
'notes'
146-
],
147-
layout: 'vertical',
148-
columns: 1,
149-
showSubmit: true,
150-
showCancel: editingContact !== null,
151-
submitText: editingContact ? 'Update Contact' : 'Create Contact',
152-
cancelText: 'Cancel',
153-
onSuccess: handleFormSuccess,
154-
onCancel: handleCancelEdit,
155-
}}
156-
dataSource={dataSource}
157-
/>
98+
<div className="flex h-screen w-full bg-slate-50 overflow-hidden">
99+
{/* Sidebar */}
100+
<aside className="w-64 bg-white border-r border-slate-200 hidden md:flex flex-col flex-shrink-0">
101+
<div className="h-16 flex items-center px-6 border-b border-slate-100">
102+
<span className="font-bold text-xl text-slate-800 tracking-tight">ObjectUI</span>
103+
</div>
104+
<nav className="flex-1 p-4 space-y-1 overflow-y-auto">
105+
<Button variant="ghost" className="w-full justify-start font-medium" disabled>
106+
<LayoutDashboard className="mr-2 h-4 w-4 text-slate-500" /> Dashboard
107+
</Button>
108+
<Button variant="secondary" className="w-full justify-start font-medium text-slate-900 bg-slate-100">
109+
<Users className="mr-2 h-4 w-4 text-slate-700" /> Contacts
110+
</Button>
111+
<Button variant="ghost" className="w-full justify-start font-medium text-slate-600 hover:text-slate-900">
112+
<Settings className="mr-2 h-4 w-4 text-slate-500" /> Settings
113+
</Button>
114+
</nav>
115+
<div className="p-4 border-t border-slate-100">
116+
<div className="flex items-center gap-3">
117+
<div className="w-8 h-8 rounded-full bg-slate-200"></div>
118+
<div className="text-sm">
119+
<p className="font-medium text-slate-700">Admin User</p>
120+
<p className="text-slate-500 text-xs">admin@example.com</p>
121+
</div>
122+
</div>
158123
</div>
159-
</section>
124+
</aside>
160125

161-
{/* List Section */}
162-
<section className="lg:col-span-7">
163-
<ContactList
164-
client={client}
165-
onEdit={handleEditContact}
166-
refreshTrigger={refreshTrigger}
167-
/>
168-
</section>
169-
</main>
126+
{/* Main Content */}
127+
<main className="flex-1 flex flex-col min-w-0 h-full relative">
128+
{/* Header */}
129+
<header className="flex-shrink-0 h-16 bg-white border-b border-slate-200 flex items-center justify-between px-6 shadow-sm z-10 w-full">
130+
<div>
131+
<h1 className="text-xl font-semibold text-slate-800">Contacts</h1>
132+
<p className="text-sm text-slate-500">Manage your organization's contacts</p>
133+
</div>
134+
<Button onClick={handleCreate} className="bg-blue-600 hover:bg-blue-700 text-white shadow-sm">
135+
<Plus className="mr-2 h-4 w-4" /> New Contact
136+
</Button>
137+
</header>
138+
139+
{/* Table Area */}
140+
<div className="flex-1 overflow-hidden p-6 relative">
141+
<div className="rounded-xl border border-slate-200 bg-white shadow-sm h-full flex flex-col overflow-hidden">
142+
<ObjectGrid
143+
key={refreshKey}
144+
schema={{
145+
type: 'object-grid',
146+
objectName: 'contact',
147+
filters: [],
148+
searchableFields: ['name', 'email', 'company'],
149+
columns: [
150+
{ field: 'name', label: 'Name', width: 200 },
151+
{ field: 'email', label: 'Email', width: 220 },
152+
{ field: 'phone', label: 'Phone', width: 150 },
153+
{ field: 'company', label: 'Company', width: 180 },
154+
{ field: 'role', label: 'Role', width: 150 },
155+
{ field: 'status', label: 'Status' }
156+
]
157+
}}
158+
dataSource={dataSource}
159+
onEdit={handleEdit}
160+
className="h-full w-full"
161+
/>
162+
</div>
163+
</div>
164+
</main>
170165

171-
<footer className="mt-16 pt-8 border-t border-gray-200 text-center text-sm text-gray-500 space-y-2">
172-
<p>
173-
This example demonstrates ObjectForm integration with MSW.
174-
All API calls are intercepted and mocked in the browser.
175-
</p>
176-
<p className="font-mono text-xs text-gray-400">
177-
React + TypeScript + Vite + MSW + ObjectForm
178-
</p>
179-
</footer>
166+
{/* Form Dialog */}
167+
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
168+
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto flex flex-col gap-0 p-0">
169+
<DialogHeader className="p-6 pb-2 border-b border-slate-100">
170+
<DialogTitle className="text-xl">{editingRecord ? 'Edit Contact' : 'New Contact'}</DialogTitle>
171+
<DialogDescription>
172+
{editingRecord ? 'Update contact details below.' : 'Fill in the information to create a new contact.'}
173+
</DialogDescription>
174+
</DialogHeader>
175+
176+
<div className="p-6 pt-4 flex-1 overflow-y-auto">
177+
<ObjectForm
178+
schema={{
179+
type: 'object-form',
180+
objectName: 'contact',
181+
mode: editingRecord ? 'edit' : 'create',
182+
recordId: editingRecord?.id,
183+
fields: [
184+
'name',
185+
'email',
186+
'phone',
187+
'company',
188+
'position',
189+
'priority',
190+
'salary',
191+
'commission_rate',
192+
'birthdate',
193+
'last_contacted',
194+
'available_time',
195+
'profile_url',
196+
'department',
197+
'resume',
198+
'avatar',
199+
'is_active',
200+
'notes'
201+
],
202+
layout: 'vertical',
203+
columns: 2,
204+
onSuccess: handleSuccess,
205+
onCancel: () => setIsDialogOpen(false),
206+
showSubmit: true,
207+
showCancel: true,
208+
submitText: editingRecord ? 'Save Changes' : 'Create Contact',
209+
}}
210+
dataSource={dataSource}
211+
className="space-y-4"
212+
/>
213+
</div>
214+
</DialogContent>
215+
</Dialog>
180216
</div>
181217
);
182218
}

examples/msw-object-form/src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
@source '../../../packages/plugin-dashboard/src/**/*.{ts,tsx}';
1010
@source '../../../packages/plugin-form/src/**/*.{ts,tsx}';
1111
@source '../../../packages/plugin-grid/src/**/*.{ts,tsx}';
12+
@source '../../../packages/plugin-grid/src/**/*.{ts,tsx}';
1213

1314
/* Tailwind plugin for animations remove
1415
@plugin 'tailwindcss-animate';

0 commit comments

Comments
 (0)