Skip to content

Commit

Permalink
Merge pull request #42 from bruinenxyz/grant/bru-1067
Browse files Browse the repository at this point in the history
Grant/bru-1067
  • Loading branch information
glpierce authored Apr 9, 2024
2 parents 1d236a9 + 3f95890 commit b9876fa
Show file tree
Hide file tree
Showing 12 changed files with 522 additions and 82 deletions.
1 change: 0 additions & 1 deletion backend/src/definitions/relation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export type Relation = z.infer<typeof RelationSchema>;
export const CreateRelationSchema = RelationSchema.omit({
id: true,
organization_id: true,
generated: true,
created_at: true,
updated_at: true,
deleted_at: true,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/relations/relations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export class RelationsService {
const relation = await this.prismaService.relation.create({
data: {
...createRelationDto,
generated: false,
organization_id: orgId,
deleted_at: null,
},
Expand Down Expand Up @@ -97,6 +96,7 @@ export class RelationsService {
const newRelation = {
database_id: databaseId,
type: relationType,
generated: true,
table_1: table1.id,
table_2: table2.id,
column_1: constraint.foreign_column_name,
Expand Down
62 changes: 60 additions & 2 deletions frontend/src/app/databases/[databaseId]/database-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IconName,
InputGroup,
MenuItem,
Popover,
Text,
TextArea,
} from "@blueprintjs/core";
Expand All @@ -19,7 +20,9 @@ import { useState } from "react";
import { useField } from "@/utils/use-field";
import * as _ from "lodash";
import { CleanDatabase, UpdateDatabaseSchema } from "@/definitions";
import { useUpdateDatabase } from "@/data/use-database";
import { useDatabaseSchemas, useUpdateDatabase } from "@/data/use-database";
import { useTables } from "@/data/use-tables";
import { getFormattedDateStrings } from "@/utils/value-format";

const DatabaseDetails = ({ database }: { database: CleanDatabase }) => {
const [editDetails, setEditDetails] = useState<boolean>(false);
Expand All @@ -29,6 +32,18 @@ const DatabaseDetails = ({ database }: { database: CleanDatabase }) => {
database.id,
);

const {
data: schemas,
isLoading: isLoadingSchemas,
error: schemasError,
} = useDatabaseSchemas(database.id);

const {
data: tables,
isLoading: isLoadingTables,
error: tablesError,
} = useTables(database.id);

function resetFields() {
nameField.onValueChange(database.name);
}
Expand All @@ -50,6 +65,9 @@ const DatabaseDetails = ({ database }: { database: CleanDatabase }) => {
}

function renderDatabaseDetails() {
const { localString, utcString } = getFormattedDateStrings(
database.created_at,
);
return (
<>
<div className="flex flex-row items-center justify-between mb-2">
Expand All @@ -62,11 +80,43 @@ const DatabaseDetails = ({ database }: { database: CleanDatabase }) => {
onClick={() => setEditDetails(true)}
/>
</div>
<div className="flex flex-col gap-2 mt-1">
<Divider />
<div className="flex flex-col gap-2 mt-2">
<div className="flex flex-row justify-between">
<Text>Name</Text>
<Text className="ml-2 bp5-text-muted">{database.name}</Text>
</div>
<div className="flex flex-row justify-between">
<Text>External name</Text>
<Text className="ml-2 bp5-text-muted">
{database.external_name}
</Text>
</div>
<div className="flex flex-row justify-between">
<Text>Added on</Text>
<Popover
content={
<div className="p-1">
<Text className=" bp5-text-muted">{utcString}</Text>
</div>
}
interactionKind="hover"
placement="top"
>
<Text className="ml-2 bp5-text-muted cursor-help">
{localString}
</Text>
</Popover>
</div>
<Divider />
<div className="flex flex-row justify-between">
<Text>Schema count</Text>
<Text className="ml-2 bp5-text-muted">{schemas!.length}</Text>
</div>
<div className="flex flex-row justify-between">
<Text>Table count</Text>
<Text className="ml-2 bp5-text-muted">{tables!.length}</Text>
</div>
</div>
</>
);
Expand Down Expand Up @@ -111,6 +161,14 @@ const DatabaseDetails = ({ database }: { database: CleanDatabase }) => {
);
}

if (isLoadingSchemas || isLoadingTables) {
return <Loading />;
}

if (schemasError || tablesError) {
return <ErrorDisplay description={schemasError.message} />;
}

return (
<div className="flex flex-col h-full p-3 overflow-y-auto">
{editDetails ? renderEditableDetails() : renderDatabaseDetails()}
Expand Down
64 changes: 64 additions & 0 deletions frontend/src/app/databases/[databaseId]/database-relations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use client";
import { CleanDatabase, Relation } from "@/definitions";
import { Card, CardList, Icon, Text } from "@blueprintjs/core";
import Loading from "@/app/loading";
import { ErrorDisplay } from "@/components/error-display";
import RelationCard from "@/components/relation/relation-card";
import CreateRelation from "@/components/relation/create-relation";
import DeleteRelation from "@/components/relation/delete-relation";
import { useRelations } from "@/data/use-relations";
import { useState } from "react";
import * as _ from "lodash";

export default function DatabaseRelations({
database,
}: {
database: CleanDatabase;
}) {
const [createRelationToggle, setCreateRelationToggle] =
useState<boolean>(false);
const [deleteRelation, setDeleteRelation] = useState<Relation | null>(null);
const {
data: relations,
isLoading: isLoadingRelations,
error: relationsError,
} = useRelations(database.id);

if (isLoadingRelations) {
return <Loading />;
}

if (relationsError) {
return <ErrorDisplay description={relationsError.message} />;
}

return (
<CardList className="h-full overflow-y-auto">
<Card
className="cursor-pointer"
onClick={() => setCreateRelationToggle(true)}
>
<div className="flex flex-row items-center gap-1 ml-1">
<Icon icon="new-object" intent="success" />
<Text className="text-success">New Relation</Text>
</div>
</Card>
{_.map(relations, (relation: Relation) => (
<RelationCard
key={relation.id}
relation={relation}
setDeleteRelation={setDeleteRelation}
/>
))}
<CreateRelation
selectedDatabase={database}
isOpen={createRelationToggle}
setIsOpen={setCreateRelationToggle}
/>
<DeleteRelation
relation={deleteRelation}
setRelation={setDeleteRelation}
/>
</CardList>
);
}
22 changes: 20 additions & 2 deletions frontend/src/app/databases/[databaseId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import React, { useState } from "react";
import * as _ from "lodash";
import DeleteDatabase from "./delete-database";
import { useDatabase } from "@/data/use-database";
import DatabaseRelations from "./database-relations";

enum DatabaseTabEnum {
DETAILS = "DETAILS",
RELATIONS = "RELATIONS",
}

export default function Page({ params }: { params: { databaseId: string } }) {
Expand All @@ -42,7 +44,6 @@ export default function Page({ params }: { params: { databaseId: string } }) {
}

if (databaseError || !database) {
console.log("Database error", databaseError);
return (
<ErrorDisplay
title="Cannot get database details"
Expand Down Expand Up @@ -72,6 +73,16 @@ export default function Page({ params }: { params: { databaseId: string } }) {
/>
}
/>
<Tab
id={DatabaseTabEnum.RELATIONS}
title={
<Button
className="bp5-minimal"
icon="one-to-many"
text="Relations"
/>
}
/>
</Tabs>
<Popover
placement="bottom-start"
Expand All @@ -98,6 +109,9 @@ export default function Page({ params }: { params: { databaseId: string } }) {

function renderObjectContent() {
switch (currentTab) {
case DatabaseTabEnum.RELATIONS:
return <DatabaseRelations database={database!} />;
case DatabaseTabEnum.DETAILS:
default:
return <DatabaseDetails database={database!} />;
}
Expand All @@ -107,7 +121,11 @@ export default function Page({ params }: { params: { databaseId: string } }) {
<Section
className="flex flex-col max-h-full col-span-2"
icon={
<SquareIcon icon={"cube"} color={"gray"} size={SquareIconSize.LARGE} />
<SquareIcon
icon={"database"}
color={"gray"}
size={SquareIconSize.LARGE}
/>
}
title={<H4 className="m-0">{database!.name}</H4>}
subtitle={database?.created_at.toString().split("T")[0]}
Expand Down
70 changes: 0 additions & 70 deletions frontend/src/app/navigation-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,18 @@ import {
MenuItem,
MenuDivider,
IconName,
Text,
Icon,
Collapse,
} from "@blueprintjs/core";
import logo from "@assets/logo.svg";
import darkLogo from "@assets/logo-dark.svg";
import Image from "next/image";
import CreateRelation from "@/components/relation/create-relation";
import DatabaseSelector from "./databases/database-selector";
import { usePathname, useRouter } from "next/navigation";
import { UserProfileButton } from "../components/account-management/user-profile-button";
import { useTables } from "@/data/use-tables";
import { useSelectedDatabase } from "@/stores";
import { useCreateUserQuery, useUserQueries } from "@/data/use-user-query";
import { useRelations } from "@/data/use-relations";
import { useDarkModeContext } from "@/components/context/dark-mode-context";
import React, { useEffect, useState } from "react";
import * as _ from "lodash";
Expand All @@ -47,11 +44,8 @@ export default function NavigationBar({}) {
NavigationTabEnums | undefined
>();
const [selectedDatabase, setSelectedDatabase] = useSelectedDatabase();
const [createRelationToggle, setCreateRelationToggle] =
useState<boolean>(false);
const { darkMode, setDarkMode } = useDarkModeContext();
const [tablesToggle, setTablesToggle] = useState<boolean>(true);
const [relationsToggle, setRelationsToggle] = useState<boolean>(true);
const [queriesToggle, setQueriesToggle] = useState<boolean>(true);

const {
Expand All @@ -60,12 +54,6 @@ export default function NavigationBar({}) {
error: tablesError,
} = useTables(selectedDatabase.id);

const {
data: relations,
isLoading: isLoadingRelations,
error: relationsError,
} = useRelations(selectedDatabase.id);

const handlePageChange = (id: string) => {
router.push(`/${id}`);
};
Expand Down Expand Up @@ -103,18 +91,6 @@ export default function NavigationBar({}) {
}
}

function getRelationIcon(relation: Relation): IconName {
switch (relation.type) {
case "many_to_many":
return "many-to-many" as IconName;
case "one_to_many":
return "one-to-many" as IconName;
case "one_to_one":
default:
return "one-to-one" as IconName;
}
}

return (
<Card className="flex flex-col h-full pl-1 pr-2 z-1">
<Image
Expand Down Expand Up @@ -159,52 +135,6 @@ export default function NavigationBar({}) {
))}
</Collapse>
<MenuDivider />
<div
className="flex flex-row items-center gap-1 mt-2 mb-1 cursor-default"
onClick={() => setRelationsToggle(!relationsToggle)}
>
<H5 className="mb-0">Relations</H5>
<Icon
icon={relationsToggle ? "chevron-down" : "chevron-right"}
color="gray"
/>
</div>
<Collapse isOpen={relationsToggle} keepChildrenMounted={true}>
<>
<MenuItem
icon="new-object"
text="New relation"
intent="success"
onClick={() => setCreateRelationToggle(true)}
popoverProps={{
usePortal: true,
}}
/>
{relations?.map((relation) => {
const table1 = _.find(tables, { id: relation.table_1 });
const table2 = _.find(tables, { id: relation.table_2 });
return table1 && table2 ? (
<MenuItem
key={relation.id}
text={
<>
<Text>{`${table1?.name} - ${table2?.name}`}</Text>
<Text className="bp5-text-muted" ellipsize>
{`${relation.column_1} - ${relation.column_2}`}
</Text>
</>
}
// TODO: onClick={() => handlePageChange(`relations/${relation.id}`)}
/>
) : null;
})}
<CreateRelation
isOpen={createRelationToggle}
setIsOpen={setCreateRelationToggle}
/>
</>
</Collapse>
<MenuDivider />
<div
className="flex flex-row items-center gap-1 mt-2 mb-1 cursor-default"
onClick={() => setQueriesToggle(!queriesToggle)}
Expand Down
Loading

0 comments on commit b9876fa

Please sign in to comment.