Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
07c3ab5
computed column
kushagra0304 Jul 31, 2025
bab170f
basic complete
kushagra0304 Aug 11, 2025
de1b42f
merge fix
kushagra0304 Aug 19, 2025
afab8c4
merge fix again
kushagra0304 Aug 19, 2025
6e4eea3
selection and preview
kushagra0304 Aug 20, 2025
46b09dc
Revert "selection and preview"
kushagra0304 Aug 21, 2025
8efa1dc
deletion functionality
kushagra0304 Aug 21, 2025
8ad7116
ui fixes
kushagra0304 Aug 23, 2025
72c5322
fix calc logic
jamesholcombe Aug 26, 2025
485eeda
Merge branch 'computedColumn' into fix-computed-cols
jamesholcombe Aug 26, 2025
618cfa5
ui fixes for sidebar
jamesholcombe Aug 26, 2025
080fe48
Merge branch 'main' into fix-computed-cols
jamesholcombe Aug 26, 2025
0efe50a
merge
jamesholcombe Aug 26, 2025
95c6d73
lint
jamesholcombe Aug 26, 2025
c50be77
add migration file
jamesholcombe Aug 26, 2025
96dd278
update form
jamesholcombe Aug 27, 2025
75d45cc
cleanup compted cols
jamesholcombe Aug 28, 2025
d71d80b
add computed cols to templates
jamesholcombe Aug 28, 2025
9cd8513
lint
jamesholcombe Aug 29, 2025
e401f27
integrate plotting with calc columns
jamesholcombe Aug 29, 2025
48fea88
lint
jamesholcombe Aug 29, 2025
7872a81
add completions to codemirror
jamesholcombe Aug 29, 2025
fd96146
being migration to visx
jamesholcombe Sep 1, 2025
471ada4
plots working in visx
jamesholcombe Sep 3, 2025
3fbbeea
tooltip working
jamesholcombe Sep 4, 2025
d215738
cleanup
jamesholcombe Sep 4, 2025
cafd75d
cleanup and tooltips
jamesholcombe Sep 4, 2025
8820672
plots cleanup
jamesholcombe Sep 4, 2025
e7fbfcb
add colors back
jamesholcombe Sep 4, 2025
2be0700
Merge branch 'fix-computed-cols' into feat/new-plot-types
jamesholcombe Sep 4, 2025
1847fb9
cleanup
jamesholcombe Sep 4, 2025
fa85f66
lint
jamesholcombe Sep 4, 2025
8307040
update plot legend size
jamesholcombe Sep 5, 2025
68d289b
custom cols working with plotting
jamesholcombe Sep 5, 2025
67bf957
begin refactor of def UI
jamesholcombe Sep 5, 2025
55e415c
new ui implemented and working
jamesholcombe Sep 5, 2025
b0e10f3
update theme
jamesholcombe Sep 8, 2025
e66c9a7
lint
jamesholcombe Sep 8, 2025
aebd6da
plot styling
jamesholcombe Sep 9, 2025
03bd7b3
final plot tweaks
jamesholcombe Sep 9, 2025
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
1,544 changes: 1,401 additions & 143 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
"rename:query-to-query": "node scripts/rename-query-to-query.js"
},
"dependencies": {
"@visx/axis": "^3.12.0",
"@visx/group": "^3.12.0",
"@visx/legend": "^3.12.0",
"@visx/responsive": "^3.12.0",
"@visx/scale": "^3.12.0",
"@visx/shape": "^3.12.0",
"@visx/stats": "^3.12.0",
"@visx/tooltip": "^3.12.0",
"@visx/visx": "^3.12.0",
"drizzle-kit": "^0.30.0",
"drizzle-orm": "^0.40.0",
"wkx": "^0.5.0"
Expand Down
4 changes: 3 additions & 1 deletion packages/app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ next-env.d.ts
# Sentry Config File
.env.sentry-build-plugin

.env.local
.env.local
# clerk configuration (can include secrets)
/.clerk/
12 changes: 10 additions & 2 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@
"@aws-sdk/client-s3": "^3.716.0",
"@aws-sdk/s3-request-presigner": "^3.716.0",
"@clerk/nextjs": "^6.12.10",
"@codemirror/view": "^6.33.0",
"@codemirror/autocomplete": "^6.18.6",
"@codemirror/basic-setup": "^0.20.0",
"@codemirror/commands": "^6.8.1",
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/language": "^6.11.2",
"@codemirror/search": "^6.5.11",
"@codemirror/state": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.38.0",
"@dnd-kit/core": "^6.3.1",
"@glideapps/glide-data-grid": "^6.0.3",
"@groundup-dev/ags": "^0.2.2",
Expand Down Expand Up @@ -61,7 +69,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"codemirror": "^6.0.1",
"codemirror": "^6.0.2",
"d3": "^7.9.0",
"date-fns": "^3.6.0",
"deck.gl": "^9.1.11",
Expand Down
36 changes: 36 additions & 0 deletions packages/app/src/actions/data/queries/addComputedColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use server";

import { getServerUser } from "@/lib/auth";
import { getProjectForUser } from "@/lib/dal/projects";
import { revalidateQueryCache } from "@/lib/dal/queries";
import { addComputedColumn } from "@/db/crud/query";
import { ComputedColumn } from "@common/db/schema/query";

import {
actionError,
actionResult,
runActionServer,
} from "@/lib/actions/utils";

export async function addComputedColumnAction(
projectId: string,
queryId: string,
column: Omit<ComputedColumn, "id">,
) {
return runActionServer(async () => {
const user = await getServerUser();
const userProject = await getProjectForUser(user, projectId);

if (!userProject) {
return actionError("Project not found");
}

const newComputedColumn = await addComputedColumn(queryId, {
...column,
});

revalidateQueryCache(queryId, projectId);

return actionResult(newComputedColumn);
});
}
23 changes: 23 additions & 0 deletions packages/app/src/actions/data/queries/deleteComputedColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use server";

import { getServerUser } from "@/lib/auth";
import { getProjectForUser } from "@/lib/dal/projects";
import { revalidateQueryCache } from "@/lib/dal/queries";
import { deleteComputedColumn } from "@/db/crud/query";

export async function deleteComputedColumnAction(
projectId: string,
queryId: string,
columnName: string,
): Promise<void> {
const user = await getServerUser();
const userProject = await getProjectForUser(user, projectId);

if (!userProject) {
throw new Error("Project not found");
}

await deleteComputedColumn(queryId, columnName);

revalidateQueryCache(queryId, projectId);
}
38 changes: 38 additions & 0 deletions packages/app/src/actions/data/queries/updateComputedColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use server";

import { getServerUser } from "@/lib/auth";
import { getProjectForUser } from "@/lib/dal/projects";
import { revalidateQueryCache } from "@/lib/dal/queries";
import { updateComputedColumn } from "@/db/crud/query";
import { ComputedColumn } from "@common/db/schema/query";
import {
runActionServer,
actionError,
actionResult,
} from "@/lib/actions/utils";

export async function updateComputedColumnAction(
projectId: string,
queryId: string,
columnId: string,
column: Omit<ComputedColumn, "id">,
) {
return runActionServer(async () => {
const user = await getServerUser();
const userProject = await getProjectForUser(user, projectId);

if (!userProject) {
return actionError("Project not found");
}

const updatedComputedColumn = await updateComputedColumn(
queryId,
columnId,
column,
);

revalidateQueryCache(queryId, projectId);

return actionResult(updatedComputedColumn);
});
}
4 changes: 4 additions & 0 deletions packages/app/src/actions/data/templates/applyTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
actionResult,
runActionServer,
} from "@/lib/actions/utils";
import { ComputedColumn } from "@common/db/schema/query";

type ApplyTemplateParams = {
projectId: string;
Expand All @@ -24,6 +25,7 @@ type ApplyTemplateParams = {
plotName?: string;
plotDefinition?: PlotDefinitionWithoutSource;
folderId?: string | null;
queryCustomColumns?: ComputedColumn[];
};

export async function applyTemplateAction({
Expand All @@ -33,6 +35,7 @@ export async function applyTemplateAction({
plotName,
plotDefinition,
folderId,
queryCustomColumns,
}: ApplyTemplateParams) {
return runActionServer(async () => {
const user = await getServerUser();
Expand All @@ -56,6 +59,7 @@ export async function applyTemplateAction({
},
]),
),
computedColumns: queryCustomColumns || [],
});

revalidateQueryCache(newQuery.id, projectId);
Expand Down
14 changes: 11 additions & 3 deletions packages/app/src/actions/data/templates/createTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
QueryWithoutFilters,
PlotDefinitionWithoutSource,
} from "@common/db/schema/template";
import { ComputedColumn } from "@common/db/schema/query";
import {
runActionServer,
actionError,
Expand Down Expand Up @@ -45,6 +46,7 @@ export async function createTemplateAction({

let queryDefinition: QueryWithoutFilters | null = null;
let plotDefinition: PlotDefinitionWithoutSource | null = null;
let computedColumns: ComputedColumn[] = [];

// Get query definition if provided
if (queryId) {
Expand All @@ -53,7 +55,10 @@ export async function createTemplateAction({
return actionError("Query not found or has no definition");
}

computedColumns = query.computedColumns || [];

// Remove filters from the definition for the template
// Create a partial record without filters
queryDefinition = Object.fromEntries(
Object.entries(query.definition).map(([tableName, tableDef]) => [
tableName,
Expand All @@ -62,7 +67,7 @@ export async function createTemplateAction({
filters: undefined, // Remove filters
},
]),
) as Record<TableName, any>;
) as Partial<Record<TableName, any>> as QueryWithoutFilters;
}

// Get plot definition if provided
Expand All @@ -89,7 +94,7 @@ export async function createTemplateAction({
filters: undefined, // Remove filters
},
]),
) as Record<TableName, any>;
) as Partial<Record<TableName, any>> as QueryWithoutFilters;
}

// If we still don't have a query definition, try to get it from the plot's definition.dataSource
Expand Down Expand Up @@ -143,7 +148,10 @@ export async function createTemplateAction({
const newTemplate = await createTemplate({
name,
ownerId: user.id,
queryDefinition: queryDefinition || ({} as QueryWithoutFilters),
queryDefinition:
queryDefinition ||
({} as Partial<Record<TableName, any>> as QueryWithoutFilters),
queryCustomColumns: computedColumns,
plotDefinition: plotDefinition || null,
scope,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { getServerUser } from "@/lib/auth";
import { getProjectForUser } from "@/lib/dal/projects";
import { parseStringParam } from "@/lib/routing";
import { notFound } from "next/navigation";
import { QueryData } from "@/components/data/query/query-data";
import { schemaConfig } from "@common/db/schema/common";

import { Button } from "@/components/ui/button";
import { QueryDefinition } from "@/components/data/query/query-definition";
import Link from "next/link";
Expand All @@ -15,6 +14,27 @@ import {
} from "@/lib/dal/queries";
import { getProjectTableCounts } from "@/lib/dal/data";
import { readZones, readQueryZones } from "@/db/crud/zone";
import { schemaConfig } from "@common/db/schema/common";
import { Query } from "@common/db/schema/query";

function getCompletions(query: Query) {
const columns = Object.entries(query.definition ?? {}).flatMap(
([table, columns]) => {
const tableConfig = schemaConfig.find(
(config) => config.dbName === table,
);
return columns.columns.map((column) => {
const columnConfig = tableConfig?.columns.find(
(c) => c.dbName === column,
);
return columnConfig?.nameCamelCase ?? column;
});
},
);

return columns;
}

type Props = {
params: { projectId: string; queryId: string };
};
Expand Down Expand Up @@ -46,39 +66,28 @@ export default async function Page({ params }: Props) {
readQueryZones(queryId),
]);

const zonesWithIsSelected = zones.map((zone) => ({
const completions = getCompletions(query);

const zonesWithIsSelected = zones.map((zone: any) => ({
...zone,
isSelected: queryZones.some((z) => z.zoneId === zone.id),
isSelected: queryZones.some((z: any) => z.zoneId === zone.id),
}));

return (
<div className="space-y-12 overflow-auto pb-6">
<div className="h-full overflow-auto pb-6">
<QueryDefinition
projectId={projectId}
query={query}
schemaConfig={schemaConfig}
tableCounts={tableCounts}
completions={completions}
columns={columns}
zones={zonesWithIsSelected}
totalRowCount={totalRowCount}
maxRows={5}
/>

{definitionConfigured && (
<div className="space-y-2">
<div className="flex items-center justify-between">
<h2 className="px-6 text-lg font-semibold">Data preview</h2>
</div>
<div className="h-[480px]">
<QueryData
projectId={projectId}
query={query}
columns={columns}
totalRowCount={totalRowCount}
maxRows={10}
zones={zonesWithIsSelected}
/>
</div>
</div>
)}

<div className="flex items-center justify-end px-6">
<div className="flex items-center justify-end px-6 pt-4">
<Link href={`/projects/${projectId}/data/queries/${queryId}/data`}>
<Button disabled={!definitionConfigured} variant="default" asChild>
Show full table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { getProjectForUser } from "@/lib/dal/projects";
import { parseStringParam } from "@/lib/routing";
import { notFound } from "next/navigation";

import { QueryToggleButton } from "@/components/data/query/query-toggle-button";
import { QueryNameInput } from "@/components/data/query/query-name-input";
import { QuerySaveButton } from "@/components/data/query/query-save-button";
import { BreadcrumbSetter } from "@/components/data/breadcrumb-setter";
import { QueryLayoutClient } from "@/components/data/query/query-layout-client";

import { isDefinitionConfigured } from "@/components/data/query/helpers";
import { getQueryForProject } from "@/lib/dal/queries";
Expand All @@ -29,35 +26,13 @@ export default async function Layout({ params, children }: Props) {
const definitionConfigured = isDefinitionConfigured(query);

return (
<div className="flex h-full flex-col">
{/* Header with name input, breadcrumbs, and toggle button */}
<div className="flex items-center justify-between px-6 py-4">
<div className="flex items-center gap-4">
<QueryNameInput query={query} />
<BreadcrumbSetter
breadcrumbs={[
{
label: "Queries",
href: `/projects/${projectId}/data/queries`,
},
{
label: query.name,
href: `/projects/${projectId}/data/queries/${queryId}/data`,
},
]}
/>
</div>
<div className="flex items-center gap-2">
<QuerySaveButton projectId={projectId} queryId={queryId} />

<QueryToggleButton
projectId={projectId}
queryId={queryId}
definitionConfigured={definitionConfigured}
/>
</div>
</div>
<QueryLayoutClient
projectId={projectId}
queryId={queryId}
query={query}
definitionConfigured={definitionConfigured}
>
{children}
</div>
</QueryLayoutClient>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,10 @@ export async function GET(request: NextRequest, { params }: RequestArgs) {
zones.map((zone) => zone.zoneId),
);

return NextResponse.json({ rows, rowCount });
// Return data along with computed column definitions for client-side evaluation
return NextResponse.json({
rows,
rowCount,
computedColumns: query.computedColumns || [],
});
}
Loading