UNPKG

livesheets-sdk

Version:

LiveSheets SDK for Google Sheets integration with Lovable apps. OAuth onboarding URL: https://livesheets-backend-624058308714.europe-north1.run.app/onboarding

538 lines (434 loc) 11.9 kB
# LiveSheets SDK Official SDK for integrating Google Sheets with Lovable applications. Provides OAuth authentication, sheet selection, and data access with built-in React hooks support. ## Installation ```bash npm install livesheets-sdk ``` ## Quick Start ### Using React Hooks (Recommended for Lovable Apps) ```tsx import { useLiveSheets } from 'livesheets-sdk/react'; function GoogleSheetsConnector() { const { isAuthenticated, hasSheet, data, isLoading, error, connect, selectSheet, disconnect, refresh } = useLiveSheets(); if (!isAuthenticated) { return <button onClick={connect}>Connect Google Sheets</button>; } if (!hasSheet) { return <button onClick={selectSheet}>Select a Sheet</button>; } return ( <div> <h3>Sheet Data ({data.length} rows)</h3> <button onClick={refresh}>Refresh</button> <button onClick={disconnect}>Disconnect</button> {/* Render your data */} </div> ); } ``` ### Using the Core SDK ```typescript import { LiveSheetsClient, LiveSheetsAuth } from 'livesheets-sdk'; // Initialize auth const auth = new LiveSheetsAuth(); // Check if authenticated if (!auth.isAuthenticated()) { // Start OAuth flow auth.startOAuth(); } // Get JWT token const jwt = auth.getJWT(); // Create client and fetch data const client = new LiveSheetsClient(jwt); const rows = await client.listRows(); const metadata = await client.getMetadata(); const schema = await client.getSchema(); ``` ## Features ### 🔐 OAuth Authentication Built-in OAuth flow management with automatic JWT handling: ```typescript import { auth } from 'livesheets-sdk'; // Start OAuth flow (redirects to Google) auth.startOAuth(); // Check authentication status if (auth.isAuthenticated()) { const jwt = auth.getJWT(); // Use JWT for API calls } // Logout auth.logout(); ``` ### 📊 Sheet Selection Interactive Google Picker integration: ```typescript import { LiveSheetsClient } from 'livesheets-sdk'; await LiveSheetsClient.openSheetPicker({ jwt: 'your-jwt-token', onSuccess: (result) => { console.log('Selected:', result.spreadsheetId, result.sheetName); }, onCancel: () => { console.log('Selection cancelled'); } }); ``` ### 🎣 React Hooks Complete hook with all features: ```typescript const { // Auth state isAuthenticated, // boolean isConnecting, // boolean // Sheet state hasSheet, // boolean isSelectingSheet, // boolean sheetMetadata, // { spreadsheetId, sheetName, rowCount, columnCount } // Data state data, // SheetRow[] isLoading, // boolean error, // Error | null // Actions connect, // () => void disconnect, // () => void selectSheet, // () => Promise<void> refresh, // () => Promise<void> // Direct access client // LiveSheetsClient | null } = useLiveSheets(options); ``` Simple data-only hook: ```typescript const { data, loading, error } = useSheetData(jwt); ``` ## API Reference ### LiveSheetsAuth ```typescript class LiveSheetsAuth { constructor(config?: { backendUrl?: string; storageKey?: string; userIdKey?: string; }) startOAuth(returnUrl?: string): void getJWT(): string | null isAuthenticated(): boolean logout(): void } ``` ### LiveSheetsClient ```typescript class LiveSheetsClient { constructor(jwt?: string, baseUrl?: string) // Data methods async listRows(): Promise<SheetRow[]> async getMetadata(): Promise<SheetMetadata> async getSchema(): Promise<SheetSchema[]> // Static methods static getAuth(config?: AuthConfig): LiveSheetsAuth static async openSheetPicker(config: SheetPickerConfig): Promise<void> } ``` ### Types ```typescript interface SheetRow { [key: string]: any; } interface SheetMetadata { spreadsheetId: string; sheetName: string; rowCount: number; columnCount: number; } interface SheetSchema { name: string; type: string; } ``` ## Complete Example ```tsx import React from 'react'; import { useLiveSheets } from 'livesheets-sdk/react'; export default function DataTable() { const { isAuthenticated, hasSheet, data, isLoading, error, connect, selectSheet, refresh, sheetMetadata } = useLiveSheets({ autoLoadData: true, onDataLoaded: (rows) => { console.log(`Loaded ${rows.length} rows`); } }); if (error) { return <div>Error: {error.message}</div>; } if (!isAuthenticated) { return ( <button onClick={connect} disabled={isLoading}> {isLoading ? 'Connecting...' : 'Connect Google Sheets'} </button> ); } if (!hasSheet) { return ( <button onClick={selectSheet} disabled={isLoading}> {isLoading ? 'Opening picker...' : 'Select a Sheet'} </button> ); } return ( <div> <h2>{sheetMetadata?.sheetName}</h2> <p>{data.length} rows × {sheetMetadata?.columnCount} columns</p> <button onClick={refresh} disabled={isLoading}> {isLoading ? 'Loading...' : 'Refresh'} </button> <table> <thead> <tr> {data[0] && Object.keys(data[0]).map(key => ( <th key={key}>{key}</th> ))} </tr> </thead> <tbody> {data.map((row, i) => ( <tr key={i}> {Object.values(row).map((val, j) => ( <td key={j}>{String(val)}</td> ))} </tr> ))} </tbody> </table> </div> ); } ``` ## Lovable Integration Instructions ### Simple Component for Lovable Here's a complete GoogleSheetsConnector component that works perfectly with Lovable: ```tsx import React from 'react'; import { useLiveSheets } from 'livesheets-sdk/react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Loader2 } from 'lucide-react'; export default function GoogleSheetsConnector() { const { isAuthenticated, hasSheet, data, isLoading, error, connect, selectSheet, disconnect, refresh, sheetMetadata } = useLiveSheets(); if (!isAuthenticated) { return ( <Card> <CardHeader> <CardTitle>Connect Google Sheets</CardTitle> </CardHeader> <CardContent className="text-center"> <Button onClick={connect} disabled={isLoading}> {isLoading ? ( <> <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Connecting... </> ) : ( 'Connect Google Sheets' )} </Button> </CardContent> </Card> ); } if (!hasSheet) { return ( <Card> <CardHeader> <CardTitle>Select a Spreadsheet</CardTitle> </CardHeader> <CardContent className="text-center space-y-4"> <Button onClick={selectSheet} disabled={isLoading}> {isLoading ? ( <> <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Opening picker... </> ) : ( 'Select Sheet' )} </Button> <Button variant="ghost" onClick={disconnect}> Disconnect </Button> </CardContent> </Card> ); } return ( <Card> <CardHeader> <CardTitle>{sheetMetadata?.sheetName || 'Sheet Data'}</CardTitle> </CardHeader> <CardContent> <div className="mb-4 flex gap-2"> <Button onClick={refresh} disabled={isLoading} size="sm"> {isLoading ? ( <> <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Loading... </> ) : ( 'Refresh' )} </Button> <Button onClick={selectSheet} variant="outline" size="sm"> Change Sheet </Button> <Button onClick={disconnect} variant="ghost" size="sm"> Disconnect </Button> </div> {error && ( <div className="text-red-500 mb-4">{error.message}</div> )} {data.length > 0 && ( <div className="overflow-x-auto"> <table className="w-full border"> <thead> <tr> {Object.keys(data[0]).map(key => ( <th key={key} className="border p-2 text-left"> {key} </th> ))} </tr> </thead> <tbody> {data.slice(0, 10).map((row, i) => ( <tr key={i}> {Object.values(row).map((val, j) => ( <td key={j} className="border p-2"> {String(val)} </td> ))} </tr> ))} </tbody> </table> {data.length > 10 && ( <p className="text-center mt-2 text-sm text-muted-foreground"> Showing 10 of {data.length} rows </p> )} </div> )} </CardContent> </Card> ); } ``` ### Integration Steps for Lovable 1. **Install the SDK**: Add `livesheets-sdk` to your dependencies 2. **Copy the component above** into your project 3. **Add it to your page**: Just drop `<GoogleSheetsConnector />` anywhere 4. **That's it!** The component handles everything else ## Configuration ### Backend URL Default: `https://livesheets-backend-624058308714.europe-north1.run.app` To use a custom backend: ```typescript const auth = new LiveSheetsAuth({ backendUrl: 'https://your-backend-url.com' }); // Or with hooks const { connect } = useLiveSheets({ backendUrl: 'https://your-backend-url.com' }); ``` ### Storage Keys Customize localStorage keys: ```typescript const auth = new LiveSheetsAuth({ storageKey: 'my_app_jwt', userIdKey: 'my_app_user_id' }); ``` ## OAuth Setup The SDK handles the complete OAuth flow: 1. User clicks "Connect" → Redirects to Google OAuth 2. User authorizes → Redirects back with JWT 3. SDK stores JWT in localStorage 4. All subsequent API calls use the JWT ### Required Google OAuth Scopes - `https://www.googleapis.com/auth/spreadsheets.readonly` - `https://www.googleapis.com/auth/drive.readonly` ### OAuth Onboarding URL For manual integration or testing: ``` https://livesheets-backend-624058308714.europe-north1.run.app/onboarding ``` ## Alternative Usage: readSheet Helper For simple use cases, you can use the `readSheet` helper: ```javascript import { readSheet } from 'livesheets-sdk'; // Option 1: Pass JWT directly const rows = await readSheet(jwt); // Option 2: Auto-detect JWT from localStorage const rows = await readSheet(); // Option 3: Pass options const rows = await readSheet({ jwt: jwt, // Optional if stored in localStorage sheetId: 'abc123', // For future use sheet: 'Sheet1' // For future use }); ``` ## Common Issues ### Import Errors Always use named imports with the `new` keyword: ```typescript // ✅ CORRECT import { LiveSheetsClient } from 'livesheets-sdk'; const client = new LiveSheetsClient(jwt); // ❌ WRONG - Missing 'new' keyword import { LiveSheetsClient } from 'livesheets-sdk'; const client = LiveSheetsClient(jwt); // Error! ``` ### TypeScript Errors When rendering data, convert to strings: ```tsx // ✅ CORRECT <td>{String(cell)}</td> // ❌ WRONG - Type error <td>{cell}</td> ``` ## Browser Support - Chrome, Firefox, Safari, Edge (latest versions) - Requires localStorage and postMessage support - Popup blockers must allow the sheet picker ## License MIT