Skip to content

Commit a1cf479

Browse files
Merge pull request #75 from groundup-dev/feat/new-plot-types
Feat/new plot types
2 parents 8a3514d + 03bd7b3 commit a1cf479

File tree

75 files changed

+58994
-2555
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+58994
-2555
lines changed

package-lock.json

Lines changed: 1401 additions & 143 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
"rename:query-to-query": "node scripts/rename-query-to-query.js"
1919
},
2020
"dependencies": {
21+
"@visx/axis": "^3.12.0",
22+
"@visx/group": "^3.12.0",
23+
"@visx/legend": "^3.12.0",
24+
"@visx/responsive": "^3.12.0",
25+
"@visx/scale": "^3.12.0",
26+
"@visx/shape": "^3.12.0",
27+
"@visx/stats": "^3.12.0",
28+
"@visx/tooltip": "^3.12.0",
29+
"@visx/visx": "^3.12.0",
2130
"drizzle-kit": "^0.30.0",
2231
"drizzle-orm": "^0.40.0",
2332
"wkx": "^0.5.0"

packages/app/.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,6 @@ next-env.d.ts
4242
# Sentry Config File
4343
.env.sentry-build-plugin
4444

45-
.env.local
45+
.env.local
46+
# clerk configuration (can include secrets)
47+
/.clerk/

packages/app/package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@
1313
"@aws-sdk/client-s3": "^3.716.0",
1414
"@aws-sdk/s3-request-presigner": "^3.716.0",
1515
"@clerk/nextjs": "^6.12.10",
16-
"@codemirror/view": "^6.33.0",
16+
"@codemirror/autocomplete": "^6.18.6",
17+
"@codemirror/basic-setup": "^0.20.0",
18+
"@codemirror/commands": "^6.8.1",
19+
"@codemirror/lang-javascript": "^6.2.4",
20+
"@codemirror/language": "^6.11.2",
21+
"@codemirror/search": "^6.5.11",
22+
"@codemirror/state": "^6.5.2",
23+
"@codemirror/theme-one-dark": "^6.1.3",
24+
"@codemirror/view": "^6.38.0",
1725
"@dnd-kit/core": "^6.3.1",
1826
"@glideapps/glide-data-grid": "^6.0.3",
1927
"@groundup-dev/ags": "^0.2.2",
@@ -61,7 +69,7 @@
6169
"class-variance-authority": "^0.7.1",
6270
"clsx": "^2.1.1",
6371
"cmdk": "^1.0.0",
64-
"codemirror": "^6.0.1",
72+
"codemirror": "^6.0.2",
6573
"d3": "^7.9.0",
6674
"date-fns": "^3.6.0",
6775
"deck.gl": "^9.1.11",
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use server";
2+
3+
import { getServerUser } from "@/lib/auth";
4+
import { getProjectForUser } from "@/lib/dal/projects";
5+
import { revalidateQueryCache } from "@/lib/dal/queries";
6+
import { addComputedColumn } from "@/db/crud/query";
7+
import { ComputedColumn } from "@common/db/schema/query";
8+
9+
import {
10+
actionError,
11+
actionResult,
12+
runActionServer,
13+
} from "@/lib/actions/utils";
14+
15+
export async function addComputedColumnAction(
16+
projectId: string,
17+
queryId: string,
18+
column: Omit<ComputedColumn, "id">,
19+
) {
20+
return runActionServer(async () => {
21+
const user = await getServerUser();
22+
const userProject = await getProjectForUser(user, projectId);
23+
24+
if (!userProject) {
25+
return actionError("Project not found");
26+
}
27+
28+
const newComputedColumn = await addComputedColumn(queryId, {
29+
...column,
30+
});
31+
32+
revalidateQueryCache(queryId, projectId);
33+
34+
return actionResult(newComputedColumn);
35+
});
36+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"use server";
2+
3+
import { getServerUser } from "@/lib/auth";
4+
import { getProjectForUser } from "@/lib/dal/projects";
5+
import { revalidateQueryCache } from "@/lib/dal/queries";
6+
import { deleteComputedColumn } from "@/db/crud/query";
7+
8+
export async function deleteComputedColumnAction(
9+
projectId: string,
10+
queryId: string,
11+
columnName: string,
12+
): Promise<void> {
13+
const user = await getServerUser();
14+
const userProject = await getProjectForUser(user, projectId);
15+
16+
if (!userProject) {
17+
throw new Error("Project not found");
18+
}
19+
20+
await deleteComputedColumn(queryId, columnName);
21+
22+
revalidateQueryCache(queryId, projectId);
23+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use server";
2+
3+
import { getServerUser } from "@/lib/auth";
4+
import { getProjectForUser } from "@/lib/dal/projects";
5+
import { revalidateQueryCache } from "@/lib/dal/queries";
6+
import { updateComputedColumn } from "@/db/crud/query";
7+
import { ComputedColumn } from "@common/db/schema/query";
8+
import {
9+
runActionServer,
10+
actionError,
11+
actionResult,
12+
} from "@/lib/actions/utils";
13+
14+
export async function updateComputedColumnAction(
15+
projectId: string,
16+
queryId: string,
17+
columnId: string,
18+
column: Omit<ComputedColumn, "id">,
19+
) {
20+
return runActionServer(async () => {
21+
const user = await getServerUser();
22+
const userProject = await getProjectForUser(user, projectId);
23+
24+
if (!userProject) {
25+
return actionError("Project not found");
26+
}
27+
28+
const updatedComputedColumn = await updateComputedColumn(
29+
queryId,
30+
columnId,
31+
column,
32+
);
33+
34+
revalidateQueryCache(queryId, projectId);
35+
36+
return actionResult(updatedComputedColumn);
37+
});
38+
}

packages/app/src/actions/data/templates/applyTemplate.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
actionResult,
1717
runActionServer,
1818
} from "@/lib/actions/utils";
19+
import { ComputedColumn } from "@common/db/schema/query";
1920

2021
type ApplyTemplateParams = {
2122
projectId: string;
@@ -24,6 +25,7 @@ type ApplyTemplateParams = {
2425
plotName?: string;
2526
plotDefinition?: PlotDefinitionWithoutSource;
2627
folderId?: string | null;
28+
queryCustomColumns?: ComputedColumn[];
2729
};
2830

2931
export async function applyTemplateAction({
@@ -33,6 +35,7 @@ export async function applyTemplateAction({
3335
plotName,
3436
plotDefinition,
3537
folderId,
38+
queryCustomColumns,
3639
}: ApplyTemplateParams) {
3740
return runActionServer(async () => {
3841
const user = await getServerUser();
@@ -56,6 +59,7 @@ export async function applyTemplateAction({
5659
},
5760
]),
5861
),
62+
computedColumns: queryCustomColumns || [],
5963
});
6064

6165
revalidateQueryCache(newQuery.id, projectId);

packages/app/src/actions/data/templates/createTemplate.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
QueryWithoutFilters,
1212
PlotDefinitionWithoutSource,
1313
} from "@common/db/schema/template";
14+
import { ComputedColumn } from "@common/db/schema/query";
1415
import {
1516
runActionServer,
1617
actionError,
@@ -45,6 +46,7 @@ export async function createTemplateAction({
4546

4647
let queryDefinition: QueryWithoutFilters | null = null;
4748
let plotDefinition: PlotDefinitionWithoutSource | null = null;
49+
let computedColumns: ComputedColumn[] = [];
4850

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

58+
computedColumns = query.computedColumns || [];
59+
5660
// Remove filters from the definition for the template
61+
// Create a partial record without filters
5762
queryDefinition = Object.fromEntries(
5863
Object.entries(query.definition).map(([tableName, tableDef]) => [
5964
tableName,
@@ -62,7 +67,7 @@ export async function createTemplateAction({
6267
filters: undefined, // Remove filters
6368
},
6469
]),
65-
) as Record<TableName, any>;
70+
) as Partial<Record<TableName, any>> as QueryWithoutFilters;
6671
}
6772

6873
// Get plot definition if provided
@@ -89,7 +94,7 @@ export async function createTemplateAction({
8994
filters: undefined, // Remove filters
9095
},
9196
]),
92-
) as Record<TableName, any>;
97+
) as Partial<Record<TableName, any>> as QueryWithoutFilters;
9398
}
9499

95100
// If we still don't have a query definition, try to get it from the plot's definition.dataSource
@@ -143,7 +148,10 @@ export async function createTemplateAction({
143148
const newTemplate = await createTemplate({
144149
name,
145150
ownerId: user.id,
146-
queryDefinition: queryDefinition || ({} as QueryWithoutFilters),
151+
queryDefinition:
152+
queryDefinition ||
153+
({} as Partial<Record<TableName, any>> as QueryWithoutFilters),
154+
queryCustomColumns: computedColumns,
147155
plotDefinition: plotDefinition || null,
148156
scope,
149157
});

packages/app/src/app/(app)/projects/[projectId]/data/queries/[queryId]/definition/page.tsx

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { getServerUser } from "@/lib/auth";
22
import { getProjectForUser } from "@/lib/dal/projects";
33
import { parseStringParam } from "@/lib/routing";
44
import { notFound } from "next/navigation";
5-
import { QueryData } from "@/components/data/query/query-data";
6-
import { schemaConfig } from "@common/db/schema/common";
5+
76
import { Button } from "@/components/ui/button";
87
import { QueryDefinition } from "@/components/data/query/query-definition";
98
import Link from "next/link";
@@ -15,6 +14,27 @@ import {
1514
} from "@/lib/dal/queries";
1615
import { getProjectTableCounts } from "@/lib/dal/data";
1716
import { readZones, readQueryZones } from "@/db/crud/zone";
17+
import { schemaConfig } from "@common/db/schema/common";
18+
import { Query } from "@common/db/schema/query";
19+
20+
function getCompletions(query: Query) {
21+
const columns = Object.entries(query.definition ?? {}).flatMap(
22+
([table, columns]) => {
23+
const tableConfig = schemaConfig.find(
24+
(config) => config.dbName === table,
25+
);
26+
return columns.columns.map((column) => {
27+
const columnConfig = tableConfig?.columns.find(
28+
(c) => c.dbName === column,
29+
);
30+
return columnConfig?.nameCamelCase ?? column;
31+
});
32+
},
33+
);
34+
35+
return columns;
36+
}
37+
1838
type Props = {
1939
params: { projectId: string; queryId: string };
2040
};
@@ -46,39 +66,28 @@ export default async function Page({ params }: Props) {
4666
readQueryZones(queryId),
4767
]);
4868

49-
const zonesWithIsSelected = zones.map((zone) => ({
69+
const completions = getCompletions(query);
70+
71+
const zonesWithIsSelected = zones.map((zone: any) => ({
5072
...zone,
51-
isSelected: queryZones.some((z) => z.zoneId === zone.id),
73+
isSelected: queryZones.some((z: any) => z.zoneId === zone.id),
5274
}));
5375

5476
return (
55-
<div className="space-y-12 overflow-auto pb-6">
77+
<div className="h-full overflow-auto pb-6">
5678
<QueryDefinition
5779
projectId={projectId}
5880
query={query}
5981
schemaConfig={schemaConfig}
6082
tableCounts={tableCounts}
83+
completions={completions}
84+
columns={columns}
85+
zones={zonesWithIsSelected}
86+
totalRowCount={totalRowCount}
87+
maxRows={5}
6188
/>
6289

63-
{definitionConfigured && (
64-
<div className="space-y-2">
65-
<div className="flex items-center justify-between">
66-
<h2 className="px-6 text-lg font-semibold">Data preview</h2>
67-
</div>
68-
<div className="h-[480px]">
69-
<QueryData
70-
projectId={projectId}
71-
query={query}
72-
columns={columns}
73-
totalRowCount={totalRowCount}
74-
maxRows={10}
75-
zones={zonesWithIsSelected}
76-
/>
77-
</div>
78-
</div>
79-
)}
80-
81-
<div className="flex items-center justify-end px-6">
90+
<div className="flex items-center justify-end px-6 pt-4">
8291
<Link href={`/projects/${projectId}/data/queries/${queryId}/data`}>
8392
<Button disabled={!definitionConfigured} variant="default" asChild>
8493
Show full table

0 commit comments

Comments
 (0)