@lightdash/cli 0.3433.0 → 0.3435.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.
@@ -0,0 +1,89 @@
1
+ # Google Sheets export (`exportToSheets`)
2
+
3
+ > Read this when the user wants an "Open in Google Sheets" action or any export destined for Google Sheets.
4
+
5
+ Use `exportToSheets()` when the user wants a one-click "Open in Google Sheets" button or otherwise needs the data to land in a new Google spreadsheet they own. It takes the in-memory rows the app already has and creates a new sheet against the viewer's connected Google account.
6
+
7
+ `exportToSheets` is a top-level export from the SDK, not a `useLightdash` field — it isn't tied to a single query:
8
+
9
+ ```tsx
10
+ import { exportToSheets } from '@lightdash/query-sdk';
11
+ ```
12
+
13
+ When to use it vs `downloadResults`:
14
+
15
+ - **`exportToSheets`** writes the rows you pass in to a *new Google Sheet*. The rows can be anything the app has produced — query results, joined/aggregated combinations of multiple queries, locally filtered subsets, computed columns. Use this when the data the user wants exported only exists in the React layer, or when the destination is Sheets specifically.
16
+ - **`downloadResults`** re-runs the underlying Lightdash query server-side and produces a CSV/XLSX file. Use this when the user wants raw query results matching the warehouse, not a React-transformed view.
17
+
18
+ If both fit (a straight `useLightdash(query)` table with no client transforms, and the user just wants "send to Sheets"), prefer `exportToSheets` for the Sheets case and `downloadResults` for CSV/XLSX.
19
+
20
+ ```tsx
21
+ import { Button } from '@/components/ui/button';
22
+ import { Loader2, ExternalLink } from 'lucide-react';
23
+ import { exportToSheets } from '@lightdash/query-sdk';
24
+ import { useState } from 'react';
25
+
26
+ function ExportToSheetsButton() {
27
+ const { data, columns, loading } = useLightdash(revenueQuery);
28
+ const [exporting, setExporting] = useState(false);
29
+
30
+ const handleExport = async () => {
31
+ setExporting(true);
32
+ try {
33
+ const { fileUrl } = await exportToSheets({
34
+ title: 'Revenue by segment',
35
+ columns: columns.map((c) => ({
36
+ key: c.name,
37
+ label: c.label,
38
+ type: c.type,
39
+ })),
40
+ rows: data,
41
+ });
42
+ window.open(fileUrl, '_blank');
43
+ } catch (err) {
44
+ // Show a toast in real app code. Common messages:
45
+ // "Google Sheets export is not available in this context"
46
+ // — running inside an embed; fall back to downloadResults.
47
+ // "Google authentication was cancelled" — user closed OAuth popup.
48
+ // "Export too large (max 100,000 rows / 25 MB)" — dataset too big.
49
+ console.error(err);
50
+ } finally {
51
+ setExporting(false);
52
+ }
53
+ };
54
+
55
+ const disabled = loading || exporting || data.length === 0;
56
+
57
+ return (
58
+ <Button variant="outline" size="sm" disabled={disabled} onClick={handleExport}>
59
+ {exporting ? (
60
+ <Loader2 className="h-4 w-4 mr-1 animate-spin" />
61
+ ) : (
62
+ <ExternalLink className="h-4 w-4 mr-1" />
63
+ )}
64
+ {exporting ? 'Exporting…' : 'Open in Google Sheets'}
65
+ </Button>
66
+ );
67
+ }
68
+ ```
69
+
70
+ Options:
71
+ - `title`: spreadsheet name shown in Google Drive.
72
+ - `columns`: ordered column definitions. `key` matches the row object key; optional `label` overrides the header text (defaults to `key`); optional `type` (`'string' | 'number' | 'date' | 'timestamp' | 'boolean'`) lets the backend format cells correctly.
73
+ - `rows`: an array of plain objects keyed by the column `key`s. Values must be `string | number | boolean | null`.
74
+
75
+ Returns `{ fileUrl }` — open it in a new tab to drop the user into the sheet.
76
+
77
+ OAuth behavior:
78
+ - If the viewer is signed into Lightdash but hasn't connected Google yet, the parent frame opens the standard Google OAuth popup automatically — `exportToSheets` awaits consent and then proceeds. Don't try to open the popup yourself; the iframe sandbox blocks it.
79
+
80
+ Where it works:
81
+ - First-party Lightdash sessions only (the app surfaces inside Lightdash's own UI and dashboard tiles).
82
+ - **Not** available in JWT/public embed contexts. The promise rejects with `"Google Sheets export is not available in this context"`. Surface this as a clear toast and fall back to `downloadResults` if the same app may also render inside an embed.
83
+
84
+ Rules:
85
+ - Only call from explicit user actions such as a button or menu item.
86
+ - Disable the button while exporting and when `data.length === 0`.
87
+ - Track export state (`exporting`, `isExporting`, etc.) and show a spinner or "Exporting…" label until the promise settles.
88
+ - After success, open the returned `fileUrl` in a new tab. Don't try to render Google Sheets inside the app iframe — embedded sheets need third-party cookies the sandbox blocks.
89
+ - Hard limits: 100,000 rows and 25 MB serialized payload. If you can tell upfront the dataset will exceed either, disable the button with an explanatory tooltip.
@@ -0,0 +1,30 @@
1
+ # Organization themes (`/app/src/design/`)
2
+
3
+ > Read this when `/app/src/design/` exists — an organization theme is active and constrains the visual direction.
4
+
5
+ When `/app/src/design/` exists in the workspace, the organization has supplied brand assets that **must** drive the visual direction. The pipeline copies them in at build time; you do not create them. Treat their contents as inviolable: read and reference, never edit or duplicate.
6
+
7
+ Directory layout (any subdirectory may be empty):
8
+
9
+ - `/app/src/design/css/` — stylesheets. Inspect the style sheets and decide: If they are general colors and global styles **Import them** from your main entry point (`src/main.jsx`) before any of your own styles, so cascade order lets your CSS override theme defaults only where intentional. If they are component-specific (e.g. a `.fancy-button` class), **Reference them** in your JSX (`<button className="fancy-button">`) and use them as the basis for any custom components you build. You can build styles that typically match if the specific ones you are looking for are not there.
10
+ - `/app/src/design/fonts/` — web fonts. **Reference them via `@font-face` in your own CSS** and use the resulting `font-family` everywhere you'd otherwise pick a font. Do not link to external font CDNs (Google Fonts, Bunny, etc.) when fonts are present here.
11
+ - `/app/src/design/images/` — logos and brand imagery. Import as ES modules (`import logo from './design/images/logo.png'`) so Vite hashes the URL. Use them in place of any generic logo/illustration you'd otherwise invent. Try to guess from the file names or context: is it a logo (use in the header), a pattern (use as a background), or a product screenshot (reference for UI details)?
12
+
13
+ **Images are IMPORTANT and frequently more telling than the CSS or instruction files** — they carry intent the other assets can't express. Decide how to use them in this order:
14
+
15
+ 1. **Use them as directed in the effective skill prompt or the user's prompt.** If the instructions tell you a specific image is the logo, or to apply a particular pattern, that's the answer — stop and follow it.
16
+ 2. **If you have no further direction**, classify each image by inspecting both its filename and (when in doubt) its contents via the `Read` tool. Treat each kind seriously:
17
+ - **Image assets** — things meant to appear in the rendered app: logos, mascots, hero images, icons, background patterns. Use them. A logo file means the header gets that logo, not a generic one you'd invent.
18
+ - **Design assets** — outputs from a design tool: color-swatch sheets, type-specimen pages, component mockups, exported Figma frames. These are NOT meant to appear in the app — they are a binding spec for how the app should look. Mine them for exact hex values, type sizes, spacing, component shapes, and apply them to your own components. Treat them with the same authority as a CSS file.
19
+ - **References and inspiration** — dashboard screenshots, product photos, mood-board imagery the organization wants the app to evoke. These ARE a directive, just at a higher level: match the aesthetic, density, and information hierarchy you see. Don't try to reproduce them pixel-for-pixel; do try to land in the same visual neighborhood.
20
+
21
+ When unsure which bucket an image falls into, prefer **design asset** or **reference** over guessing. A file that looks like a Figma export is almost never meant to ship in the app.
22
+
23
+ Hard rules when a theme is active:
24
+
25
+ - **Theme CSS overrides `frontend-design`'s color/typography direction.** The aesthetic distinctness `frontend-design` pushes for still applies to layout, density, and motion — but colors, font families, and any other tokens the theme CSS defines win over your own picks. If the theme sets `--accent: #6B5B95`, your headings use that purple; don't reach for a "more distinctive" alternative.
26
+ - **If the theme CSS defines a chart palette (CSS custom properties like `--chart-1`, `--chart-2`, …, or any `*-chart-*` variables), use it instead of `CHART_COLORS` from `@/lib/theme`.** Read the values via `getComputedStyle(document.documentElement).getPropertyValue('--chart-1')` once on mount and cycle them by index for multi-series. Falling back to `CHART_COLORS` when the theme doesn't define a chart palette is correct.
27
+ - **Instruction text in the appended system prompt is binding.** Any rules described under "Organization theme instructions" later in this prompt override conflicting defaults in this file. Treat them as customer-supplied product requirements, not suggestions.
28
+ - **Do not modify files under `/app/src/design/`.** `Write(//app/src/**)` would technically allow it, but those files are the source of truth for the brand and may be reused across many apps. Treat the directory as read-only.
29
+
30
+ When `/app/src/design/` does not exist or is empty, behave as if these rules don't apply — no theme is active and `frontend-design`'s direction is the whole story.