@@ -8,26 +8,20 @@ import { Card } from '@/components/ui/card';
88import Link from 'next/link' ;
99import { listDataSources } from '@/lib/tinybird' ;
1010import { TOOLS , type AppGridItem } from '@/lib/constants' ;
11+ import TokenPrompt from '@/components/token-prompt' ;
1112
1213function AppCard ( { app, isInstalled, token } : { app : AppGridItem ; isInstalled : boolean ; token ?: string } ) {
1314 return (
14- < Link
15- key = { app . id }
15+ < Link
16+ key = { app . id }
1617 href = { `/${ app . id } ${ token ? `?token=${ token } ` : '' } ` }
1718 >
18- < Card className = "p-6 hover:shadow-lg transition-shadow cursor-pointer" >
19+ < Card className = { `p-4 hover:bg-accent ${ isInstalled ? 'border-primary' : '' } ` } >
1920 < div className = "flex items-center gap-4" >
20- < div className = "text-4xl " > { app . icon } </ div >
21+ < div className = "text-2xl " > { app . icon } </ div >
2122 < div >
22- < h2 className = "text-xl font-semibold" > { app . name } </ h2 >
23- < p className = "text-gray-500" > { app . description } </ p >
24- < div className = "mt-2" >
25- { isInstalled ? (
26- < span className = "text-green-500 text-sm" > Installed</ span >
27- ) : (
28- < span className = "text-gray-400 text-sm" > Not installed</ span >
29- ) }
30- </ div >
23+ < h3 className = "font-semibold" > { app . name } </ h3 >
24+ < p className = "text-sm text-muted-foreground" > { app . description } </ p >
3125 </ div >
3226 </ div >
3327 </ Card >
@@ -36,20 +30,17 @@ function AppCard({ app, isInstalled, token }: { app: AppGridItem; isInstalled: b
3630}
3731
3832export default function Home ( ) {
39- const [ token , setToken ] = useQueryState ( 'token' ) ;
40- const [ inputToken , setInputToken ] = useState ( token || '' ) ;
33+ const [ token ] = useQueryState ( 'token' ) ;
4134 const [ installedApps , setInstalledApps ] = useState < string [ ] > ( [ ] ) ;
4235 const [ isLoading , setIsLoading ] = useState ( false ) ;
4336
4437 useEffect ( ( ) => {
4538 async function fetchDataSources ( ) {
4639 if ( ! token ) return ;
47-
4840 setIsLoading ( true ) ;
4941 try {
5042 const sources = await listDataSources ( token ) ;
51- const installed = sources . map ( s => s . name ) ;
52- setInstalledApps ( installed ) ;
43+ setInstalledApps ( sources . map ( source => source . name ) ) ;
5344 } catch ( error ) {
5445 console . error ( 'Failed to fetch data sources:' , error ) ;
5546 } finally {
@@ -60,52 +51,48 @@ export default function Home() {
6051 fetchDataSources ( ) ;
6152 } , [ token ] ) ;
6253
63- const handleSaveToken = ( ) => {
64- setToken ( inputToken ) ;
65- } ;
66-
67- if ( ! token ) {
68- return (
69- < div className = "flex min-h-screen items-center justify-center p-4" >
70- < Card className = "w-full max-w-md p-6 space-y-4" >
71- < h1 className = "text-2xl font-bold" > Welcome to TinyNest</ h1 >
72- < p className = "text-gray-500" > Please enter your Tinybird token to continue</ p >
73- < div className = "flex gap-2" >
74- < Input
75- value = { inputToken }
76- onChange = { ( e ) => setInputToken ( e . target . value ) }
77- placeholder = "Enter your Tinybird token"
78- />
79- < Button onClick = { handleSaveToken } > Save</ Button >
80- </ div >
81- </ Card >
82- </ div >
83- ) ;
84- }
85-
86- const installedAppsList = Object . values ( TOOLS ) . filter ( app => installedApps . includes ( app . ds ) ) ;
87- const uninstalledAppsList = Object . values ( TOOLS ) . filter ( app => ! installedApps . includes ( app . ds ) ) ;
88-
8954 return (
90- < div className = "space-y-8" >
91- { installedAppsList . length > 0 && (
92- < div >
93- < h2 className = "text-2xl font-semibold mb-4" > Installed Apps</ h2 >
94- < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" >
95- { installedAppsList . map ( ( app ) => (
96- < AppCard key = { app . id } app = { app } isInstalled = { true } token = { token } />
97- ) ) }
98- </ div >
55+ < div className = "container py-6" >
56+ < TokenPrompt />
57+ { token && isLoading && (
58+ < div className = "flex items-center justify-center" >
59+ < p className = "text-lg font-semibold" > Loading...</ p >
9960 </ div >
10061 ) }
101-
102- { uninstalledAppsList . length > 0 && (
103- < div >
104- < h2 className = "text-2xl font-semibold mb-4" > Available Apps</ h2 >
105- < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" >
106- { uninstalledAppsList . map ( ( app ) => (
107- < AppCard key = { app . id } app = { app } isInstalled = { false } token = { token } />
108- ) ) }
62+ { token && ! isLoading && (
63+ < div className = "space-y-8" >
64+ { installedApps . length > 0 && (
65+ < div className = "space-y-4" >
66+ < h2 className = "text-lg font-semibold" > Installed Apps</ h2 >
67+ < div className = "grid gap-4 md:grid-cols-2 lg:grid-cols-3" >
68+ { Object . values ( TOOLS )
69+ . filter ( app => installedApps . includes ( app . ds ) )
70+ . map ( app => (
71+ < AppCard
72+ key = { app . id }
73+ app = { app }
74+ isInstalled = { true }
75+ token = { token }
76+ />
77+ ) ) }
78+ </ div >
79+ </ div >
80+ ) }
81+
82+ < div className = "space-y-4" >
83+ < h2 className = "text-lg font-semibold" > Available Apps</ h2 >
84+ < div className = "grid gap-4 md:grid-cols-2 lg:grid-cols-3" >
85+ { Object . values ( TOOLS )
86+ . filter ( app => ! installedApps . includes ( app . ds ) )
87+ . map ( app => (
88+ < AppCard
89+ key = { app . id }
90+ app = { app }
91+ isInstalled = { false }
92+ token = { token }
93+ />
94+ ) ) }
95+ </ div >
10996 </ div >
11097 </ div >
11198 ) }
0 commit comments