Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions template/.env.example.tmpl
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
DATABRICKS_HOST=https://...
{{- if .dotenv_example}}
{{.dotenv_example}}
{{- if .dotEnv.example}}
{{.dotEnv.example}}
{{- end}}
{{- if .plugins.lakebase}}
PGHOST=your-lakebase-host.databricks.com
PGDATABASE=databricks_postgres
# Run: databricks postgres list-endpoints projects/{project-id}/branches/{branch-id}
LAKEBASE_ENDPOINT=projects/<project-id>/branches/<branch-id>/endpoints/<endpoint-id>
# PGUSER=your_user # optional, defaults to DATABRICKS_CLIENT_ID
PGSSLMODE=require
{{- end}}
DATABRICKS_APP_PORT=8000
DATABRICKS_APP_NAME={{.project_name}}
DATABRICKS_APP_NAME={{.projectName}}
FLASK_RUN_HOST=0.0.0.0
19 changes: 15 additions & 4 deletions template/.env.tmpl
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
{{if ne .profile ""}}DATABRICKS_CONFIG_PROFILE={{.profile}}{{else}}DATABRICKS_HOST={{.workspace_host}}{{end}}
{{- if .dotenv}}
{{.dotenv}}
{{- if ne .profile ""}}
DATABRICKS_CONFIG_PROFILE={{.profile}}
{{- else}}
DATABRICKS_HOST={{.workspaceHost}}
{{- end}}
{{- if .dotEnv.content}}
{{.dotEnv.content}}
{{- end}}
{{- if .plugins.lakebase}}
PGHOST='' # Copy from the Lakebase Postgres UI
PGDATABASE='databricks_postgres' # Copy from the Lakebase Postgres UI
LAKEBASE_ENDPOINT='' # Run: databricks postgres list-endpoints projects/{project-id}/branches/{branch-id}
# PGUSER='' # optional, defaults to DATABRICKS_CLIENT_ID
PGSSLMODE=require
{{- end}}
DATABRICKS_APP_PORT=8000
DATABRICKS_APP_NAME={{.project_name}}
DATABRICKS_APP_NAME={{.projectName}}
FLASK_RUN_HOST=0.0.0.0
16 changes: 14 additions & 2 deletions template/app.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
command: ['npm', 'run', 'start']
{{- if .app_env}}
env:
{{.app_env}}
{{- if .appEnv}}
{{.appEnv}}
{{- end}}
{{- if .plugins.lakebase}}
- name: PGHOST
value: "" # Copy from the Lakebase Postgres UI
- name: PGDATABASE
value: "databricks_postgres" # Copy from the Lakebase Postgres UI
- name: LAKEBASE_ENDPOINT
value: "" # Run: databricks postgres list-endpoints projects/{project-id}/branches/{branch-id}
- name: PGSSLMODE
value: "require"
# - name: PGUSER
# value: "" # optional, defaults to DATABRICKS_CLIENT_ID
{{- end}}
2 changes: 1 addition & 1 deletion template/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{.project_name}}</title>
<title>{{.projectName}}</title>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions template/client/public/site.webmanifest
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "{{.project_name}}",
"short_name": "{{.project_name}}",
"name": "{{.projectName}}",
"short_name": "{{.projectName}}",
"icons": [
{
"src": "/favicon-192x192.png",
Expand Down
245 changes: 96 additions & 149 deletions template/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,167 +1,114 @@
/**
* ⚠️ BEFORE MODIFYING THIS FILE:
*
* 1. Create SQL files in config/queries/
* 2. Run `npm run typegen` to generate query types
* 3. Check appKitTypes.d.ts for available types
*
* Common Mistakes:
* - DataTable does NOT accept `data` or `columns` props
* - Charts use `xKey` and `yKey`, NOT `seriesKey`/`nameKey`/`valueKey`
* - useAnalyticsQuery has no `enabled` option - use conditional rendering
*/
import { createBrowserRouter, RouterProvider, NavLink, Outlet } from 'react-router';
import {
useAnalyticsQuery,
AreaChart,
LineChart,
RadarChart,
Card,
CardContent,
CardHeader,
CardTitle,
Skeleton,
Label,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@databricks/appkit-ui/react';
import { sql } from "@databricks/appkit-ui/js";
import { useState, useEffect } from 'react';
{{- if .plugins.analytics}}
import { AnalyticsPage } from './pages/analytics/AnalyticsPage';
{{- end}}
{{- if .plugins.lakebase}}
import { LakebasePage } from './pages/lakebase/LakebasePage';
{{- end}}

function App() {
const { data, loading, error } = useAnalyticsQuery('hello_world', {
message: sql.string('hello world'),
});
const navLinkClass = ({ isActive }: { isActive: boolean }) =>
`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
isActive
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`;

const [health, setHealth] = useState<{
status: string;
timestamp: string;
} | null>(null);
const [healthError, setHealthError] = useState<string | null>(null);
function Layout() {
return (
<div className="min-h-screen bg-background flex flex-col">
<header className="border-b px-6 py-3 flex items-center gap-4">
<h1 className="text-lg font-semibold text-foreground">{{.projectName}}</h1>
<nav className="flex gap-1">
<NavLink to="/" end className={navLinkClass}>
Home
</NavLink>
{{- if .plugins.analytics}}
<NavLink to="/analytics" className={navLinkClass}>
Analytics
</NavLink>
{{- end}}
{{- if .plugins.lakebase}}
<NavLink to="/lakebase" className={navLinkClass}>
Lakebase
</NavLink>
{{- end}}
</nav>
</header>

useEffect(() => {
fetch('/health')
.then((response) => response.json())
.then((data: { status: string }) => setHealth({ ...data, timestamp: new Date().toISOString() }))
.catch((error: { message: string }) => setHealthError(error.message));
}, []);
<main className="flex-1 p-6">
<Outlet />
</main>
</div>
);
}

const [maxMonthNum, setMaxMonthNum] = useState<number>(12);
const router = createBrowserRouter([
{
element: <Layout />,
children: [
{ path: '/', element: <HomePage /> },
{{- if .plugins.analytics}}
{ path: '/analytics', element: <AnalyticsPage /> },
{{- end}}
{{- if .plugins.lakebase}}
{ path: '/lakebase', element: <LakebasePage /> },
{{- end}}
],
},
]);

const salesParameters = { max_month_num: sql.number(maxMonthNum) };
export default function App() {
return <RouterProvider router={router} />;
}

function HomePage() {
return (
<div className="min-h-screen bg-background flex flex-col items-center justify-center p-4 w-full">
<div className="mb-8 text-center">
<h1 className="text-4xl font-bold mb-2 text-foreground">Minimal Databricks App</h1>
<p className="text-lg text-muted-foreground max-w-md">A minimal Databricks App powered by Databricks AppKit</p>
<div className="max-w-2xl mx-auto space-y-6 mt-8">
<div className="text-center">
<h2 className="text-3xl font-bold mb-2 text-foreground">
Welcome to your Databricks App
</h2>
<p className="text-lg text-muted-foreground">
Powered by Databricks AppKit
</p>
</div>

<div className="grid grid-cols-1 md:grid-cols-3 gap-6 w-full max-w-7xl">
<Card className="shadow-lg">
<CardHeader>
<CardTitle>SQL Query Result</CardTitle>
</CardHeader>
<CardContent>
{loading && (
<div className="space-y-2">
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-8 w-1/2" />
</div>
)}
{error && <div className="text-destructive bg-destructive/10 p-3 rounded-md">Error: {error}</div>}
{data && data.length > 0 && (
<div className="space-y-2">
<div className="text-sm text-muted-foreground">Query: SELECT :message AS value</div>
<div className="text-2xl font-bold text-primary">{data[0].value}</div>
</div>
)}
{data && data.length === 0 && <div className="text-muted-foreground">No results</div>}
</CardContent>
</Card>

<Card className="shadow-lg md:col-span-2">
<CardHeader>
<CardTitle>Health Check</CardTitle>
</CardHeader>
<CardContent>
{!health && !healthError && (
<div className="space-y-2">
<Skeleton className="h-6 w-24" />
<Skeleton className="h-4 w-48" />
</div>
)}
{healthError && (
<div className="text-destructive bg-destructive/10 p-3 rounded-md">Error: {healthError}</div>
)}
{health && (
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-success animate-pulse"></div>
<div className="text-lg font-semibold text-success">{health.status.toUpperCase()}</div>
</div>
<div className="text-sm text-muted-foreground">
Last checked: {new Date(health.timestamp).toLocaleString()}
</div>
</div>
)}
</CardContent>
</Card>

<Card className="shadow-lg md:col-span-3">
<CardHeader>
<CardTitle>Sales Data Filter</CardTitle>
</CardHeader>
<CardContent>
<div className="max-w-md">
<div className="space-y-2">
<Label htmlFor="max-month">Show data up to month</Label>
<Select value={maxMonthNum.toString()} onValueChange={(value) => setMaxMonthNum(parseInt(value))}>
<SelectTrigger id="max-month">
<SelectValue placeholder="All months" />
</SelectTrigger>
<SelectContent>
{Array.from({ length: 12 }, (_, i) => (
<SelectItem key={`month-${i + 1}`} value={(i + 1).toString()}>
{i + 1 === 12 ? 'All months (12)' : `Month ${i + 1}`}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</CardContent>
</Card>

<Card className="shadow-lg flex min-w-0">
<CardHeader>
<CardTitle>Sales Trend Area Chart</CardTitle>
</CardHeader>
<CardContent>
<AreaChart queryKey="mocked_sales" parameters={salesParameters} />
</CardContent>
</Card>
<Card className="shadow-lg flex min-w-0">
<CardHeader>
<CardTitle>Sales Trend Custom Line Chart</CardTitle>
</CardHeader>
<CardContent>
<LineChart queryKey="mocked_sales" parameters={salesParameters} />
</CardContent>
</Card>
<Card className="shadow-lg flex min-w-0">
<CardHeader>
<CardTitle>Sales Trend Radar Chart</CardTitle>
</CardHeader>
<CardContent>
<RadarChart queryKey="mocked_sales" parameters={salesParameters} />
</CardContent>
</Card>
</div>
<Card className="shadow-lg">
<CardHeader>
<CardTitle>Getting Started</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<p className="text-sm text-muted-foreground">Your app is ready. Explore the resources below to continue building.</p>
<ul className="space-y-2 text-sm">
<li>
<a
href="https://github.com/databricks/appkit"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline underline-offset-4 hover:text-primary/80"
>
AppKit on GitHub →
</a>
</li>
<li>
<a
href="https://databricks.github.io/appkit/"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline underline-offset-4 hover:text-primary/80"
>
AppKit documentation →
</a>
</li>
</ul>
</CardContent>
</Card>
</div>
);
}

export default App;
Loading
Loading