@objectql/studio 1.6.0 → 1.7.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.
package/dist/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>ObjectQL Console</title>
7
- <script type="module" crossorigin src="/studio/assets/index-BT6-06vQ.js"></script>
7
+ <script type="module" crossorigin src="/studio/assets/index-c126GlTy.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/studio/assets/index-DXr7ziDn.css">
9
9
  </head>
10
10
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectql/studio",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Web-based admin studio for ObjectQL database management",
5
5
  "type": "module",
6
6
  "dependencies": {
package/src/App.tsx CHANGED
@@ -23,6 +23,7 @@ function App() {
23
23
  <Route path="/" element={<Dashboard />} />
24
24
  <Route path="/schema" element={<SchemaEditor />} />
25
25
  <Route path="/metadata" element={<MetadataBrowser />} />
26
+ <Route path="/metadata/:type" element={<MetadataBrowser />} />
26
27
  <Route path="/object/:name" element={<ObjectViewWrapper />} />
27
28
  </Routes>
28
29
  </main>
@@ -23,7 +23,7 @@ function DataGrid() {
23
23
 
24
24
  const fetchObjectMetadata = async () => {
25
25
  try {
26
- const response = await fetch(`/api/metadata/objects/${objectName}`);
26
+ const response = await fetch(`/api/metadata/object/${objectName}`);
27
27
  if (response.ok) {
28
28
  const data = await response.json();
29
29
  if (data.fields) {
@@ -20,12 +20,12 @@ function ObjectList() {
20
20
  const fetchObjects = async () => {
21
21
  try {
22
22
  setLoading(true);
23
- const response = await fetch('/api/metadata/objects');
23
+ const response = await fetch('/api/metadata/object');
24
24
  if (!response.ok) {
25
25
  throw new Error(`HTTP error! status: ${response.status}`);
26
26
  }
27
27
  const data = await response.json();
28
- setObjects(data.objects || []);
28
+ setObjects(data.object || data.objects || []);
29
29
  } catch (e: any) {
30
30
  console.error('Failed to fetch objects:', e);
31
31
  setError(e.message);
@@ -36,12 +36,12 @@ function SchemaInspector() {
36
36
  const fetchObjects = async () => {
37
37
  try {
38
38
  setLoading(true);
39
- const response = await fetch('/api/metadata/objects');
39
+ const response = await fetch('/api/metadata/object');
40
40
  if (!response.ok) {
41
41
  throw new Error(`HTTP error! status: ${response.status}`);
42
42
  }
43
43
  const data = await response.json();
44
- setObjects(data.objects || []);
44
+ setObjects(data.object || data.objects || []);
45
45
  } catch (e: any) {
46
46
  console.error('Failed to fetch objects:', e);
47
47
  setError(e.message);
@@ -52,7 +52,7 @@ function SchemaInspector() {
52
52
 
53
53
  const fetchObjectDetails = async (name: string) => {
54
54
  try {
55
- const response = await fetch(`/api/metadata/objects/${name}`);
55
+ const response = await fetch(`/api/metadata/object/${name}`);
56
56
  if (!response.ok) {
57
57
  throw new Error(`HTTP error! status: ${response.status}`);
58
58
  }
@@ -1,8 +1,18 @@
1
1
  import { NavLink } from 'react-router-dom';
2
2
  import { useMetadata } from '@/hooks/use-metadata';
3
- import { Database, Home, Loader2, Table2, FileCode, BookOpen, Layers } from 'lucide-react';
3
+ import { Database, Home, Loader2, Table2, FileCode, BookOpen, Layers, Layout, Shield, FileText, AlertTriangle, Workflow, Activity, FileJson } from 'lucide-react';
4
4
  import { cn } from '@/lib/utils';
5
5
 
6
+ const METADATA_TYPES = [
7
+ { id: 'object', label: 'Objects', icon: Layers },
8
+ { id: 'app', label: 'Apps', icon: FileJson },
9
+ { id: 'view', label: 'Views', icon: Layout },
10
+ { id: 'permission', label: 'Permissions', icon: Shield },
11
+ { id: 'report', label: 'Reports', icon: FileText },
12
+ { id: 'validation', label: 'Validations', icon: AlertTriangle },
13
+ { id: 'workflow', label: 'Workflows', icon: Workflow },
14
+ { id: 'form', label: 'Forms', icon: Activity },
15
+ ];
6
16
 
7
17
  export function Sidebar() {
8
18
  const { objects, loading, error } = useMetadata();
@@ -30,20 +40,29 @@ export function Sidebar() {
30
40
 
31
41
  <div className="pt-4 px-4 pb-2">
32
42
  <h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
33
- Development
43
+ Metadata
34
44
  </h4>
35
45
  </div>
36
46
 
37
- <NavLink
38
- to="/metadata"
39
- className={({isActive}) => cn(
40
- "flex items-center space-x-2 px-4 py-2 rounded-md text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground",
41
- isActive && "bg-accent/50 text-accent-foreground"
42
- )}
43
- >
44
- <Layers className="h-4 w-4" />
45
- <span>Metadata Explorer</span>
46
- </NavLink>
47
+ {METADATA_TYPES.map(type => (
48
+ <NavLink
49
+ key={type.id}
50
+ to={`/metadata/${type.id}`}
51
+ className={({isActive}) => cn(
52
+ "flex items-center space-x-2 px-4 py-2 rounded-md text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground",
53
+ isActive && "bg-accent/50 text-accent-foreground"
54
+ )}
55
+ >
56
+ <type.icon className="h-4 w-4" />
57
+ <span>{type.label}</span>
58
+ </NavLink>
59
+ ))}
60
+
61
+ <div className="pt-4 px-4 pb-2">
62
+ <h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
63
+ Development
64
+ </h4>
65
+ </div>
47
66
 
48
67
  <NavLink
49
68
  to="/schema"
@@ -68,7 +87,7 @@ export function Sidebar() {
68
87
 
69
88
  <div className="pt-4 px-4 pb-2">
70
89
  <h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
71
- Collections
90
+ Data
72
91
  </h4>
73
92
  </div>
74
93
 
@@ -1,4 +1,5 @@
1
1
  import { useState, useEffect } from 'react';
2
+ import { useParams, useNavigate } from 'react-router-dom';
2
3
  import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
3
4
  import { Button } from '@/components/ui/button';
4
5
  import { Loader2, ArrowLeft, FileJson, Layers, Shield, FileText, Activity, Layout, AlertTriangle, Workflow } from 'lucide-react';
@@ -14,19 +15,22 @@ function JsonViewer({ data }: { data: any }) {
14
15
  }
15
16
 
16
17
  const METADATA_TYPES = [
17
- { id: 'objects', label: 'Objects', icon: Layers },
18
+ { id: 'object', label: 'Objects', icon: Layers },
19
+ { id: 'app', label: 'Apps', icon: FileJson },
18
20
  { id: 'view', label: 'Views', icon: Layout },
19
21
  { id: 'permission', label: 'Permissions', icon: Shield },
20
22
  { id: 'report', label: 'Reports', icon: FileText },
21
23
  { id: 'validation', label: 'Validations', icon: AlertTriangle },
22
24
  { id: 'workflow', label: 'Workflows', icon: Workflow },
23
25
  { id: 'form', label: 'Forms', icon: Activity },
24
- { id: 'app', label: 'Apps', icon: FileJson },
25
26
  ];
26
27
 
27
28
  export function MetadataBrowser() {
29
+ const { type: urlType } = useParams<{ type: string }>();
30
+ const navigate = useNavigate();
31
+
28
32
  // State
29
- const [selectedType, setSelectedType] = useState<string | null>(null);
33
+ const [selectedType, setSelectedType] = useState<string | null>(urlType || null);
30
34
  const [selectedItem, setSelectedItem] = useState<string | null>(null);
31
35
 
32
36
  const [items, setItems] = useState<any[]>([]);
@@ -35,6 +39,15 @@ export function MetadataBrowser() {
35
39
  const [loading, setLoading] = useState(false);
36
40
  const [error, setError] = useState<string | null>(null);
37
41
 
42
+ // Sync selectedType with URL
43
+ useEffect(() => {
44
+ if (urlType) {
45
+ setSelectedType(urlType);
46
+ } else {
47
+ setSelectedType(null);
48
+ }
49
+ }, [urlType]);
50
+
38
51
  // Fetch list when type changes
39
52
  useEffect(() => {
40
53
  if (!selectedType) return;
@@ -48,8 +61,9 @@ export function MetadataBrowser() {
48
61
  .then(async res => {
49
62
  if (!res.ok) throw new Error(`Failed to fetch ${selectedType}`);
50
63
  const data = await res.json();
51
- // API returns { [type]: [...] }
52
- const list = data[selectedType] || data.objects || [];
64
+ // API returns { [type]: [...] } or for object: { object: [...], objects: [...] }
65
+ // Handle singular/plural mismatch from API response
66
+ const list = data[selectedType] || data.objects || data.object || [];
53
67
  setItems(list);
54
68
  })
55
69
  .catch(err => setError(err.message))
@@ -93,7 +107,7 @@ export function MetadataBrowser() {
93
107
  <Card
94
108
  key={type.id}
95
109
  className="cursor-pointer hover:bg-accent/50 transition-colors border-2 hover:border-primary/50"
96
- onClick={() => setSelectedType(type.id)}
110
+ onClick={() => navigate(`/metadata/${type.id}`)}
97
111
  >
98
112
  <CardHeader className="flex flex-row items-center space-y-0 space-x-4">
99
113
  <div className="p-2 bg-primary/10 rounded-full text-primary">
@@ -120,7 +134,7 @@ export function MetadataBrowser() {
120
134
  {/* Left Panel: List */}
121
135
  <div className="w-1/3 border-r bg-card flex flex-col">
122
136
  <div className="p-4 border-b flex items-center space-x-2">
123
- <Button variant="ghost" size="sm" onClick={() => setSelectedType(null)}>
137
+ <Button variant="ghost" size="sm" onClick={() => navigate('/metadata')}>
124
138
  <ArrowLeft className="h-4 w-4" />
125
139
  </Button>
126
140
  <h2 className="font-semibold text-lg capitalize">{selectedType} List</h2>