@liorandb/studio 0.0.1

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/bin/index.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { execSync } from "child_process";
5
+
6
+ const target = process.cwd();
7
+ const templateDir = new URL("../template", import.meta.url).pathname;
8
+
9
+ console.log("šŸš€ Creating LioranDB Studio...\n");
10
+
11
+ fs.cpSync(templateDir, target, { recursive: true });
12
+
13
+ console.log("šŸ“¦ Installing dependencies...\n");
14
+
15
+ execSync("npm install", { stdio: "inherit" });
16
+
17
+ console.log("\nšŸ”„ Starting dev server...\n");
18
+
19
+ execSync("npm run dev", { stdio: "inherit" });
package/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@liorandb/studio",
3
+ "version": "0.0.1",
4
+ "bin": {
5
+ "liorandb-studio": "./bin/index.js"
6
+ },
7
+ "files": ["bin", "template"],
8
+ "type": "module",
9
+ "private": false
10
+ }
@@ -0,0 +1,36 @@
1
+ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2
+
3
+ ## Getting Started
4
+
5
+ First, run the development server:
6
+
7
+ ```bash
8
+ npm run dev
9
+ # or
10
+ yarn dev
11
+ # or
12
+ pnpm dev
13
+ # or
14
+ bun dev
15
+ ```
16
+
17
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18
+
19
+ You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20
+
21
+ This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22
+
23
+ ## Learn More
24
+
25
+ To learn more about Next.js, take a look at the following resources:
26
+
27
+ - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28
+ - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29
+
30
+ You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31
+
32
+ ## Deploy on Vercel
33
+
34
+ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35
+
36
+ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
@@ -0,0 +1,240 @@
1
+ 'use client';
2
+
3
+ import React, { useEffect, useState } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useAppStore } from '@/store';
6
+ import { LioranDBService } from '@/lib/lioran';
7
+ import { Sidebar } from '@/components/Sidebar';
8
+ import { Navbar } from '@/components/Navbar';
9
+ import { DocumentViewer } from '@/components/DocumentViewer';
10
+ import { QueryEditor } from '@/components/QueryEditor';
11
+ import { InputModal, JsonInputModal } from '@/components/Modal';
12
+ import { useToast } from '@/components/Toast';
13
+ import { Document } from '@/types';
14
+
15
+ export default function DashboardPage() {
16
+ const router = useRouter();
17
+ const { addToast } = useToast();
18
+
19
+ const {
20
+ isLoggedIn,
21
+ currentDatabase,
22
+ selectedCollection,
23
+ logout,
24
+ setCurrentDatabase,
25
+ setSelectedCollection,
26
+ setDatabases,
27
+ setCollections,
28
+ } = useAppStore();
29
+
30
+ // Modal states
31
+ const [createDbModal, setCreateDbModal] = useState(false);
32
+ const [createColModal, setCreateColModal] = useState(false);
33
+ const [addDocModal, setAddDocModal] = useState(false);
34
+ const [editDocModal, setEditDocModal] = useState(false);
35
+ const [editingDoc, setEditingDoc] = useState<Document | null>(null);
36
+
37
+ // Check authentication
38
+ useEffect(() => {
39
+ if (!isLoggedIn) {
40
+ router.push('/login');
41
+ }
42
+ }, [isLoggedIn, router]);
43
+
44
+ // Load databases on mount
45
+ useEffect(() => {
46
+ if (isLoggedIn) {
47
+ loadDatabases();
48
+ }
49
+ }, [isLoggedIn]);
50
+
51
+ // Load collections when database changes
52
+ useEffect(() => {
53
+ if (currentDatabase) {
54
+ loadCollections(currentDatabase);
55
+ }
56
+ }, [currentDatabase]);
57
+
58
+ async function loadDatabases() {
59
+ try {
60
+ const databases = await LioranDBService.listDatabases();
61
+ setDatabases(databases);
62
+ } catch (error) {
63
+ addToast(`Error loading databases: ${error}`, 'error');
64
+ }
65
+ }
66
+
67
+ async function loadCollections(dbName: string) {
68
+ try {
69
+ const collections = await LioranDBService.listCollections(dbName);
70
+ setCollections(dbName, collections);
71
+ } catch (error) {
72
+ addToast(`Error loading collections: ${error}`, 'error');
73
+ }
74
+ }
75
+
76
+ async function handleCreateDatabase(name: string) {
77
+ try {
78
+ await LioranDBService.createDatabase(name);
79
+ await loadDatabases();
80
+ setCurrentDatabase(name);
81
+ addToast(`Database "${name}" created`, 'success');
82
+ } catch (error) {
83
+ addToast(`Error creating database: ${error}`, 'error');
84
+ }
85
+ }
86
+
87
+ async function handleCreateCollection(name: string) {
88
+ if (!currentDatabase) return;
89
+ try {
90
+ await LioranDBService.createCollection(currentDatabase, name);
91
+ await loadCollections(currentDatabase);
92
+ setSelectedCollection(name);
93
+ addToast(`Collection "${name}" created`, 'success');
94
+ } catch (error) {
95
+ addToast(`Error creating collection: ${error}`, 'error');
96
+ }
97
+ }
98
+
99
+ async function handleAddDocument(doc: Record<string, any>) {
100
+ if (!currentDatabase || !selectedCollection) return;
101
+ try {
102
+ await LioranDBService.insertOne(currentDatabase, selectedCollection, doc);
103
+ addToast('Document added', 'success');
104
+ setAddDocModal(false);
105
+ // Reload documents
106
+ const docViewer = document.querySelector('[data-reload-documents]');
107
+ if (docViewer) {
108
+ const event = new CustomEvent('reload');
109
+ docViewer.dispatchEvent(event);
110
+ }
111
+ } catch (error) {
112
+ addToast(`Error adding document: ${error}`, 'error');
113
+ }
114
+ }
115
+
116
+ async function handleLogout() {
117
+ if (confirm('Are you sure you want to logout?')) {
118
+ logout();
119
+ LioranDBService.disconnect();
120
+ router.push('/login');
121
+ }
122
+ }
123
+
124
+ return (
125
+ <div className="h-screen flex flex-col bg-slate-950">
126
+ {/* Navbar */}
127
+ <Navbar onLogout={handleLogout} />
128
+
129
+ {/* Main Content */}
130
+ <div className="flex-1 flex overflow-hidden">
131
+ {/* Sidebar */}
132
+ <Sidebar
133
+ onDatabaseSelect={setCurrentDatabase}
134
+ onCollectionSelect={(db, col) => {
135
+ setCurrentDatabase(db);
136
+ setSelectedCollection(col);
137
+ }}
138
+ onCreateDatabase={() => setCreateDbModal(true)}
139
+ onCreateCollection={() => setCreateColModal(true)}
140
+ />
141
+
142
+ {/* Workspace */}
143
+ <div className="flex-1 flex flex-col overflow-hidden">
144
+ {!currentDatabase ? (
145
+ // Empty State
146
+ <div className="flex-1 flex items-center justify-center text-slate-400">
147
+ <div className="text-center">
148
+ <div className="text-4xl mb-4">šŸ“¦</div>
149
+ <p className="text-lg">No database selected</p>
150
+ <p className="text-sm text-slate-500">Create or select a database to get started</p>
151
+ </div>
152
+ </div>
153
+ ) : !selectedCollection ? (
154
+ // Collection Selection State
155
+ <div className="flex-1 flex items-center justify-center text-slate-400">
156
+ <div className="text-center">
157
+ <div className="text-4xl mb-4">šŸ“š</div>
158
+ <p className="text-lg">No collection selected</p>
159
+ <p className="text-sm text-slate-500">Select or create a collection to view documents</p>
160
+ </div>
161
+ </div>
162
+ ) : (
163
+ // Two Panel Layout
164
+ <div className="flex-1 flex overflow-hidden gap-4 p-4 bg-slate-900">
165
+ {/* Left: Document Viewer */}
166
+ <div className="flex-1 bg-slate-950 rounded-lg border border-slate-800 overflow-hidden flex flex-col">
167
+ <DocumentViewer
168
+ onAddDocument={() => setAddDocModal(true)}
169
+ onEditDocument={(doc) => {
170
+ setEditingDoc(doc);
171
+ setEditDocModal(true);
172
+ }}
173
+ />
174
+ </div>
175
+
176
+ {/* Right: Query Editor */}
177
+ <div className="w-96 bg-slate-950 rounded-lg border border-slate-800 overflow-hidden">
178
+ <QueryEditor />
179
+ </div>
180
+ </div>
181
+ )}
182
+ </div>
183
+ </div>
184
+
185
+ {/* Modals */}
186
+ <InputModal
187
+ isOpen={createDbModal}
188
+ title="Create Database"
189
+ label="Database Name"
190
+ placeholder="mydb"
191
+ onClose={() => setCreateDbModal(false)}
192
+ onConfirm={handleCreateDatabase}
193
+ />
194
+
195
+ <InputModal
196
+ isOpen={createColModal}
197
+ title="Create Collection"
198
+ label="Collection Name"
199
+ placeholder="users"
200
+ onClose={() => setCreateColModal(false)}
201
+ onConfirm={handleCreateCollection}
202
+ />
203
+
204
+ <JsonInputModal
205
+ isOpen={addDocModal}
206
+ title="Add Document"
207
+ defaultValue='{}'
208
+ onClose={() => setAddDocModal(false)}
209
+ onConfirm={handleAddDocument}
210
+ />
211
+
212
+ <JsonInputModal
213
+ isOpen={editDocModal}
214
+ title="Edit Document"
215
+ defaultValue={editingDoc ? JSON.stringify(editingDoc, null, 2) : '{}'}
216
+ onClose={() => {
217
+ setEditDocModal(false);
218
+ setEditingDoc(null);
219
+ }}
220
+ onConfirm={async (doc) => {
221
+ if (!currentDatabase || !selectedCollection || !editingDoc) return;
222
+ try {
223
+ const _id = editingDoc._id;
224
+ await LioranDBService.updateMany(
225
+ currentDatabase,
226
+ selectedCollection,
227
+ { _id },
228
+ { $set: doc }
229
+ );
230
+ addToast('Document updated', 'success');
231
+ setEditDocModal(false);
232
+ setEditingDoc(null);
233
+ } catch (error) {
234
+ addToast(`Error updating document: ${error}`, 'error');
235
+ }
236
+ }}
237
+ />
238
+ </div>
239
+ );
240
+ }
Binary file
@@ -0,0 +1,73 @@
1
+ @import "tailwindcss";
2
+
3
+ :root {
4
+ --background: #0f172a;
5
+ --foreground: #f1f5f9;
6
+ --primary: #10b981;
7
+ --primary-dark: #059669;
8
+ --secondary: #06b6d4;
9
+ --destructive: #ef4444;
10
+ }
11
+
12
+ @theme inline {
13
+ --color-background: var(--background);
14
+ --color-foreground: var(--foreground);
15
+ --color-primary: var(--primary);
16
+ --font-sans: var(--font-geist-sans);
17
+ --font-mono: var(--font-geist-mono);
18
+ }
19
+
20
+ * {
21
+ margin: 0;
22
+ padding: 0;
23
+ box-sizing: border-box;
24
+ }
25
+
26
+ html, body {
27
+ height: 100%;
28
+ background: var(--background);
29
+ color: var(--foreground);
30
+ font-family: system-ui, -apple-system, sans-serif;
31
+ }
32
+
33
+ html {
34
+ color-scheme: dark;
35
+ }
36
+
37
+ body {
38
+ background-color: #0f172a;
39
+ color: #f1f5f9;
40
+ overflow: hidden;
41
+ }
42
+
43
+ /* Scrollbar Styling */
44
+ ::-webkit-scrollbar {
45
+ width: 8px;
46
+ height: 8px;
47
+ }
48
+
49
+ ::-webkit-scrollbar-track {
50
+ background: transparent;
51
+ }
52
+
53
+ ::-webkit-scrollbar-thumb {
54
+ background: #64748b;
55
+ border-radius: 4px;
56
+ }
57
+
58
+ ::-webkit-scrollbar-thumb:hover {
59
+ background: #94a3b8;
60
+ }
61
+
62
+ /* Selection */
63
+ ::selection {
64
+ background-color: #10b98140;
65
+ color: #f1f5f9;
66
+ }
67
+
68
+ /* Smooth transitions */
69
+ @supports (supports: selector(:has(*))) {
70
+ html {
71
+ scroll-behavior: smooth;
72
+ }
73
+ }
@@ -0,0 +1,37 @@
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import "./globals.css";
4
+ import { ToastProvider } from "@/components/Toast";
5
+
6
+ const geistSans = Geist({
7
+ variable: "--font-geist-sans",
8
+ subsets: ["latin"],
9
+ });
10
+
11
+ const geistMono = Geist_Mono({
12
+ variable: "--font-geist-mono",
13
+ subsets: ["latin"],
14
+ });
15
+
16
+ export const metadata: Metadata = {
17
+ title: "LioranDB Studio",
18
+ description: "MongoDB Compass-like UI for LioranDB databases",
19
+ };
20
+
21
+ export default function RootLayout({
22
+ children,
23
+ }: Readonly<{
24
+ children: React.ReactNode;
25
+ }>) {
26
+ return (
27
+ <html lang="en">
28
+ <body
29
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
30
+ >
31
+ <ToastProvider>
32
+ {children}
33
+ </ToastProvider>
34
+ </body>
35
+ </html>
36
+ );
37
+ }
@@ -0,0 +1,233 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useEffect } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useAppStore } from '@/store';
6
+ import { LioranDBService } from '@/lib/lioran';
7
+ import { parseConnectionUri } from '@/lib/utils';
8
+ import { useToast } from '@/components/Toast';
9
+
10
+ export default function LoginPage() {
11
+ const router = useRouter();
12
+ const { addToast } = useToast();
13
+ const { setLoggedIn } = useAppStore();
14
+
15
+ const [uri, setUri] = useState('');
16
+ const [isLoading, setIsLoading] = useState(false);
17
+ const [showAdvanced, setShowAdvanced] = useState(false);
18
+
19
+ const [username, setUsername] = useState('admin');
20
+ const [password, setPassword] = useState('');
21
+ const [host, setHost] = useState('localhost');
22
+ const [port, setPort] = useState('4000');
23
+
24
+ useEffect(() => {
25
+ // Load saved URI from localStorage
26
+ const savedUri = localStorage.getItem('liorandb_uri');
27
+ const savedToken = localStorage.getItem('liorandb_token');
28
+
29
+ if (savedUri && savedToken) {
30
+ // Try to auto-login
31
+ attemptAutoLogin(savedUri, savedToken);
32
+ }
33
+ }, []);
34
+
35
+ async function attemptAutoLogin(uri: string, token: string) {
36
+ try {
37
+ setIsLoading(true);
38
+ LioranDBService.initialize(uri);
39
+
40
+ // Test connection
41
+ const databases = await LioranDBService.listDatabases();
42
+
43
+ setLoggedIn(true, token, uri);
44
+ useAppStore.setState({ databases });
45
+ addToast('Connected successfully', 'success');
46
+ router.push('/dashboard');
47
+ } catch (error) {
48
+ localStorage.removeItem('liorandb_token');
49
+ localStorage.removeItem('liorandb_uri');
50
+ addToast(`Connection failed: ${error}`, 'error');
51
+ } finally {
52
+ setIsLoading(false);
53
+ }
54
+ }
55
+
56
+ async function handleLogin(e: React.FormEvent) {
57
+ e.preventDefault();
58
+
59
+ const loginUri = showAdvanced
60
+ ? `lioran://${username}:${password}@${host}:${port}`
61
+ : uri;
62
+
63
+ if (!loginUri) {
64
+ addToast('Please enter a connection URI', 'warning');
65
+ return;
66
+ }
67
+
68
+ try {
69
+ setIsLoading(true);
70
+
71
+ // Validate URI format
72
+ try {
73
+ parseConnectionUri(loginUri);
74
+ } catch (err) {
75
+ addToast(String(err), 'error');
76
+ return;
77
+ }
78
+
79
+ // Initialize client
80
+ await LioranDBService.initialize(loginUri);
81
+
82
+ // List databases to verify connection
83
+ const databases = await LioranDBService.listDatabases();
84
+
85
+ // Store session (in a real app, you'd get a token from the server)
86
+ const token = `token_${Date.now()}`;
87
+
88
+ setLoggedIn(true, token, loginUri);
89
+ useAppStore.setState({ databases });
90
+
91
+ addToast('Connected successfully', 'success');
92
+ router.push('/dashboard');
93
+ } catch (error) {
94
+ addToast(`Login failed: ${error}`, 'error');
95
+ } finally {
96
+ setIsLoading(false);
97
+ }
98
+ }
99
+
100
+ return (
101
+ <div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-800 flex items-center justify-center p-4">
102
+ {/* Background Effect */}
103
+ <div className="absolute inset-0 overflow-hidden">
104
+ <div className="absolute -top-40 -right-40 w-80 h-80 bg-emerald-500/5 rounded-full blur-3xl" />
105
+ <div className="absolute -bottom-40 -left-40 w-80 h-80 bg-cyan-500/5 rounded-full blur-3xl" />
106
+ </div>
107
+
108
+ {/* Login Card */}
109
+ <div className="relative w-full max-w-md">
110
+ <div className="bg-slate-900/80 backdrop-blur border border-slate-800 rounded-xl shadow-2xl overflow-hidden">
111
+ {/* Header */}
112
+ <div className="px-8 pt-8 pb-6 border-b border-slate-800">
113
+ <div className="flex items-center gap-3 mb-2">
114
+ <div className="w-10 h-10 bg-gradient-to-br from-emerald-400 to-cyan-400 rounded-lg flex items-center justify-center">
115
+ <span className="text-slate-900 font-bold text-lg">⚔</span>
116
+ </div>
117
+ <h1 className="text-2xl font-bold text-slate-100">LioranDB</h1>
118
+ </div>
119
+ <p className="text-slate-400 text-sm">Database Studio</p>
120
+ </div>
121
+
122
+ {/* Form */}
123
+ <form onSubmit={handleLogin} className="px-8 py-8 space-y-6">
124
+ {!showAdvanced ? (
125
+ // Quick Connect
126
+ <div>
127
+ <label className="block text-sm font-medium text-slate-300 mb-2">
128
+ Connection URI
129
+ </label>
130
+ <input
131
+ type="text"
132
+ value={uri}
133
+ onChange={(e) => setUri(e.target.value)}
134
+ placeholder="lioran://admin:password@localhost:4000"
135
+ className="w-full bg-slate-800/50 border border-slate-700 rounded-lg px-4 py-3 text-slate-100 placeholder-slate-500 focus:outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20 transition"
136
+ disabled={isLoading}
137
+ />
138
+ </div>
139
+ ) : (
140
+ // Advanced Connect
141
+ <div className="space-y-4">
142
+ <div>
143
+ <label className="block text-sm font-medium text-slate-300 mb-2">
144
+ Username
145
+ </label>
146
+ <input
147
+ type="text"
148
+ value={username}
149
+ onChange={(e) => setUsername(e.target.value)}
150
+ className="w-full bg-slate-800/50 border border-slate-700 rounded-lg px-4 py-3 text-slate-100 focus:outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20 transition"
151
+ disabled={isLoading}
152
+ />
153
+ </div>
154
+
155
+ <div>
156
+ <label className="block text-sm font-medium text-slate-300 mb-2">
157
+ Password
158
+ </label>
159
+ <input
160
+ type="password"
161
+ value={password}
162
+ onChange={(e) => setPassword(e.target.value)}
163
+ className="w-full bg-slate-800/50 border border-slate-700 rounded-lg px-4 py-3 text-slate-100 focus:outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20 transition"
164
+ disabled={isLoading}
165
+ />
166
+ </div>
167
+
168
+ <div className="grid grid-cols-2 gap-4">
169
+ <div>
170
+ <label className="block text-sm font-medium text-slate-300 mb-2">
171
+ Host
172
+ </label>
173
+ <input
174
+ type="text"
175
+ value={host}
176
+ onChange={(e) => setHost(e.target.value)}
177
+ className="w-full bg-slate-800/50 border border-slate-700 rounded-lg px-4 py-3 text-slate-100 focus:outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20 transition"
178
+ disabled={isLoading}
179
+ />
180
+ </div>
181
+
182
+ <div>
183
+ <label className="block text-sm font-medium text-slate-300 mb-2">
184
+ Port
185
+ </label>
186
+ <input
187
+ type="text"
188
+ value={port}
189
+ onChange={(e) => setPort(e.target.value)}
190
+ className="w-full bg-slate-800/50 border border-slate-700 rounded-lg px-4 py-3 text-slate-100 focus:outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20 transition"
191
+ disabled={isLoading}
192
+ />
193
+ </div>
194
+ </div>
195
+ </div>
196
+ )}
197
+
198
+ {/* Toggle Advanced */}
199
+ <button
200
+ type="button"
201
+ onClick={() => setShowAdvanced(!showAdvanced)}
202
+ className="text-sm text-emerald-400 hover:text-emerald-300 transition"
203
+ disabled={isLoading}
204
+ >
205
+ {showAdvanced ? '← Back to Quick Connect' : 'Advanced Options →'}
206
+ </button>
207
+
208
+ {/* Submit Button */}
209
+ <button
210
+ type="submit"
211
+ disabled={isLoading || (!showAdvanced && !uri) || (showAdvanced && !password)}
212
+ className="w-full bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-700 hover:to-emerald-600 text-white font-semibold py-3 rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed"
213
+ >
214
+ {isLoading ? 'Connecting...' : 'Connect to LioranDB'}
215
+ </button>
216
+ </form>
217
+
218
+ {/* Footer */}
219
+ <div className="px-8 py-6 bg-slate-800/30 border-t border-slate-800">
220
+ <p className="text-xs text-slate-400 text-center">
221
+ Default: <code className="bg-slate-900 px-2 py-1 rounded">lioran://admin:password@localhost:4000</code>
222
+ </p>
223
+ </div>
224
+ </div>
225
+
226
+ {/* Beta Badge */}
227
+ <div className="absolute -top-3 right-4 bg-amber-500 text-slate-900 px-3 py-1 rounded-full text-xs font-semibold">
228
+ BETA
229
+ </div>
230
+ </div>
231
+ </div>
232
+ );
233
+ }
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useAppStore } from '@/store';
6
+
7
+ export default function Home() {
8
+ const router = useRouter();
9
+ const { loadFromStorage, isLoggedIn } = useAppStore();
10
+
11
+ useEffect(() => {
12
+ loadFromStorage();
13
+
14
+ // Check localStorage for existing session
15
+ const hasToken = typeof window !== 'undefined' && !!localStorage.getItem('liorandb_token');
16
+
17
+ if (hasToken) {
18
+ router.push('/dashboard');
19
+ } else {
20
+ router.push('/login');
21
+ }
22
+ }, [router, loadFromStorage]);
23
+
24
+ return (
25
+ <div className="h-screen bg-slate-950 flex items-center justify-center">
26
+ <div className="text-center space-y-4">
27
+ <div className="text-4xl">⚔</div>
28
+ <p className="text-slate-400">Connecting to LioranDB...</p>
29
+ </div>
30
+ </div>
31
+ );
32
+ }