mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
console: create suggested relationship form
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4920 Co-authored-by: mattwhaleblue <97981123+mattwhaleblue@users.noreply.github.com> Co-authored-by: Vijay Prasanna <11921040+vijayprasanna13@users.noreply.github.com> GitOrigin-RevId: 5afb347bd5f3bdd2f14e47e279ee96b9bd928d00
This commit is contained in:
parent
c6e7dff526
commit
0d34a5b16b
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Story, Meta } from '@storybook/react';
|
import { Story, Meta } from '@storybook/react';
|
||||||
import { ReactQueryDecorator } from '@/storybook/decorators/react-query';
|
import { ReactQueryDecorator } from '@/storybook/decorators/react-query';
|
||||||
|
import { within, userEvent } from '@storybook/testing-library';
|
||||||
|
import { expect } from '@storybook/jest';
|
||||||
import { DataTarget } from '@/features/Datasources';
|
import { DataTarget } from '@/features/Datasources';
|
||||||
import { handlers } from '../../hooks/mocks/handlers.mock';
|
import { handlers } from '../../hooks/mocks/handlers.mock';
|
||||||
|
|
||||||
@ -30,3 +32,23 @@ export const Primary: Story<SuggestedRelationshipProps> = args => (
|
|||||||
Primary.args = {
|
Primary.args = {
|
||||||
target,
|
target,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const WithInteraction: Story<SuggestedRelationshipProps> = args => (
|
||||||
|
<SuggestedRelationships {...args} />
|
||||||
|
);
|
||||||
|
WithInteraction.args = Primary.args;
|
||||||
|
WithInteraction.play = async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const button = await canvas.findByRole('button', { name: 'Add' });
|
||||||
|
userEvent.click(button);
|
||||||
|
|
||||||
|
const label = await canvas.findByLabelText('Relationship Name');
|
||||||
|
userEvent.type(label, 's');
|
||||||
|
expect(label).toBeInTheDocument();
|
||||||
|
|
||||||
|
const submitButton = await canvas.findByRole('button', {
|
||||||
|
name: 'Add Relationship',
|
||||||
|
});
|
||||||
|
userEvent.click(submitButton);
|
||||||
|
};
|
||||||
|
@ -5,6 +5,7 @@ import { Button } from '@/new-components/Button';
|
|||||||
|
|
||||||
import { DataTarget } from '@/features/Datasources';
|
import { DataTarget } from '@/features/Datasources';
|
||||||
|
|
||||||
|
import { SuggestedRelationshipForm } from './components';
|
||||||
import { useSuggestedRelationships } from './hooks';
|
import { useSuggestedRelationships } from './hooks';
|
||||||
|
|
||||||
export interface SuggestedRelationshipProps {
|
export interface SuggestedRelationshipProps {
|
||||||
@ -15,6 +16,7 @@ export const SuggestedRelationships = ({
|
|||||||
target,
|
target,
|
||||||
}: SuggestedRelationshipProps) => {
|
}: SuggestedRelationshipProps) => {
|
||||||
const { data, isLoading, isError } = useSuggestedRelationships(target);
|
const { data, isLoading, isError } = useSuggestedRelationships(target);
|
||||||
|
const [open, setOpen] = React.useState<string | null>(null);
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
return (
|
return (
|
||||||
@ -45,46 +47,51 @@ export const SuggestedRelationships = ({
|
|||||||
</CardedTable.TableHead>
|
</CardedTable.TableHead>
|
||||||
|
|
||||||
<CardedTable.TableBody>
|
<CardedTable.TableBody>
|
||||||
{data?.map(relationship => (
|
{data.map(relationship => {
|
||||||
<CardedTable.TableBodyRow
|
// create a unique key
|
||||||
key={`${relationship.to}-${relationship.from}`}
|
const key = `${relationship.to.table}-${relationship.to.column}-${relationship.from.table}-${relationship.from.column}`;
|
||||||
>
|
|
||||||
<CardedTable.TableBodyCell>
|
return (
|
||||||
<Button
|
<CardedTable.TableBodyRow key={key}>
|
||||||
size="sm"
|
<CardedTable.TableBodyCell>
|
||||||
onClick={() =>
|
{open === key ? (
|
||||||
console.log(
|
<SuggestedRelationshipForm
|
||||||
'in the future ill open a form, but now i nothing :('
|
key={key}
|
||||||
)
|
target={target}
|
||||||
}
|
relationship={relationship}
|
||||||
>
|
close={() => setOpen(null)}
|
||||||
Add
|
/>
|
||||||
</Button>
|
) : (
|
||||||
</CardedTable.TableBodyCell>
|
<Button size="sm" onClick={() => setOpen(key)}>
|
||||||
<CardedTable.TableBodyCell>
|
Add
|
||||||
<FaTable className="text-sm text-muted mr-xs" />
|
</Button>
|
||||||
Local Relation
|
)}
|
||||||
</CardedTable.TableBodyCell>
|
</CardedTable.TableBodyCell>
|
||||||
<CardedTable.TableBodyCell>
|
<CardedTable.TableBodyCell>
|
||||||
<span className="capitalize">{relationship.type}</span>
|
<FaTable className="text-sm text-muted mr-xs" />
|
||||||
</CardedTable.TableBodyCell>
|
Local Relation
|
||||||
<CardedTable.TableBodyCell>
|
</CardedTable.TableBodyCell>
|
||||||
<FaTable className="text-sm text-muted mr-xs" />
|
<CardedTable.TableBodyCell>
|
||||||
{relationship.from.table} /
|
<span className="capitalize">{relationship.type}</span>
|
||||||
<FaColumns className="text-sm text-muted mr-xs" />
|
</CardedTable.TableBodyCell>
|
||||||
{relationship.from.column}
|
<CardedTable.TableBodyCell>
|
||||||
</CardedTable.TableBodyCell>
|
<FaTable className="text-sm text-muted mr-xs" />
|
||||||
<CardedTable.TableBodyCell>
|
{relationship.from.table} /
|
||||||
<FaArrowRight className="fill-current text-sm text-muted" />
|
<FaColumns className="text-sm text-muted mr-xs" />
|
||||||
</CardedTable.TableBodyCell>
|
{relationship.from.column}
|
||||||
<CardedTable.TableBodyCell>
|
</CardedTable.TableBodyCell>
|
||||||
<FaTable className="text-sm text-muted mr-xs" />
|
<CardedTable.TableBodyCell>
|
||||||
{relationship.to.table} /
|
<FaArrowRight className="fill-current text-sm text-muted" />
|
||||||
<FaColumns className="text-sm text-muted mr-xs" />
|
</CardedTable.TableBodyCell>
|
||||||
{relationship.to.column}
|
<CardedTable.TableBodyCell>
|
||||||
</CardedTable.TableBodyCell>
|
<FaTable className="text-sm text-muted mr-xs" />
|
||||||
</CardedTable.TableBodyRow>
|
{relationship.to.table} /
|
||||||
))}
|
<FaColumns className="text-sm text-muted mr-xs" />
|
||||||
|
{relationship.to.column}
|
||||||
|
</CardedTable.TableBodyCell>
|
||||||
|
</CardedTable.TableBodyRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</CardedTable.TableBody>
|
</CardedTable.TableBody>
|
||||||
</CardedTable.Table>
|
</CardedTable.Table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Button } from '@/new-components/Button';
|
||||||
|
import { Form } from '@/new-components/Form';
|
||||||
|
|
||||||
|
import { DataTarget } from '@/features/Datasources';
|
||||||
|
import { TableRelationship } from '@/features/MetadataAPI';
|
||||||
|
import { schema, useSubmit } from '../hooks';
|
||||||
|
|
||||||
|
export interface SuggestedRelationshipProps {
|
||||||
|
target: DataTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SuggestedRelationshipFormProps {
|
||||||
|
target: DataTarget;
|
||||||
|
relationship: Omit<TableRelationship, 'name' | 'comment'>;
|
||||||
|
close: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CloseIcon = () => (
|
||||||
|
<svg
|
||||||
|
className="fill-current cursor-pointer w-4 h-4 text-muted hover:text-gray-900"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="currentColor"
|
||||||
|
strokeWidth="0"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
height="1em"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const SuggestedRelationshipForm = ({
|
||||||
|
target,
|
||||||
|
relationship,
|
||||||
|
close,
|
||||||
|
}: SuggestedRelationshipFormProps) => {
|
||||||
|
const { submit, isLoading } = useSubmit();
|
||||||
|
|
||||||
|
const handleSubmit = async ({
|
||||||
|
relationshipName,
|
||||||
|
}: Record<string, unknown>) => {
|
||||||
|
try {
|
||||||
|
await submit({
|
||||||
|
relationshipName: relationshipName as string,
|
||||||
|
target,
|
||||||
|
relationship,
|
||||||
|
});
|
||||||
|
close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error while adding the relationship', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
schema={schema}
|
||||||
|
options={{
|
||||||
|
defaultValues: {
|
||||||
|
relationshipName: relationship.to.table,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
className="p-0"
|
||||||
|
>
|
||||||
|
{options => (
|
||||||
|
<div className="flex items-center space-x-1.5 bg-white">
|
||||||
|
<label htmlFor="relationshipName" className="sr-only">
|
||||||
|
Relationship Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="relationshipName"
|
||||||
|
type="text"
|
||||||
|
className="block w-full h-input shadow-sm rounded border border-gray-300 hover:border-gray-400 focus:outline-0 focus:ring-2 focus:ring-yellow-200 focus:border-yellow-400"
|
||||||
|
placeholder="Relationship Name..."
|
||||||
|
{...options.register('relationshipName')}
|
||||||
|
/>
|
||||||
|
<Button type="submit" mode="primary" isLoading={isLoading}>
|
||||||
|
Add Relationship
|
||||||
|
</Button>
|
||||||
|
<button onClick={close} aria-label="close">
|
||||||
|
<CloseIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './AddSuggestedRelationshipForm';
|
@ -1 +1,2 @@
|
|||||||
export * from './useSuggestedRelationships';
|
export * from './useSuggestedRelationships';
|
||||||
|
export * from './useSubmitForm';
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { useFireNotification } from '@/new-components/Notifications';
|
||||||
|
import { DataTarget } from '@/features/Datasources';
|
||||||
|
import {
|
||||||
|
allowedMetadataTypes,
|
||||||
|
TableRelationship,
|
||||||
|
useMetadataMigration,
|
||||||
|
} from '@/features/MetadataAPI';
|
||||||
|
|
||||||
|
export const schema = z.object({
|
||||||
|
relationshipName: z
|
||||||
|
.string()
|
||||||
|
.min(3, 'Relationship name must be at least 3 characters long'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Schema = z.infer<typeof schema>;
|
||||||
|
|
||||||
|
export interface SuggestedRelationshipProps {
|
||||||
|
target: DataTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseSubmitArgs {
|
||||||
|
relationshipName: string;
|
||||||
|
target: DataTarget;
|
||||||
|
relationship: Omit<TableRelationship, 'name' | 'comment'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSubmit = () => {
|
||||||
|
const { fireNotification } = useFireNotification();
|
||||||
|
const mutation = useMetadataMigration({
|
||||||
|
onSuccess: () => {
|
||||||
|
fireNotification({
|
||||||
|
title: 'Success!',
|
||||||
|
message: 'Relationship added successfully',
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
fireNotification({
|
||||||
|
title: 'Error',
|
||||||
|
message: 'Error while adding the relationship',
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = async ({
|
||||||
|
relationshipName,
|
||||||
|
target,
|
||||||
|
relationship,
|
||||||
|
}: UseSubmitArgs) => {
|
||||||
|
if (relationship.type === 'object') {
|
||||||
|
const query = {
|
||||||
|
type: 'pg_create_object_relationship' as allowedMetadataTypes,
|
||||||
|
args: {
|
||||||
|
table: target.table,
|
||||||
|
name: relationshipName,
|
||||||
|
source: target.database,
|
||||||
|
using: {
|
||||||
|
foreign_key_constraint_on: relationship.from.column,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return mutation.mutate({ query });
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
type: 'pg_create_array_relationship' as allowedMetadataTypes,
|
||||||
|
args: {
|
||||||
|
table: target.table,
|
||||||
|
name: relationshipName,
|
||||||
|
source: target.database,
|
||||||
|
using: {
|
||||||
|
foreign_key_constraint_on: {
|
||||||
|
table: relationship.to.table,
|
||||||
|
columns: relationship.to.column,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return mutation.mutate({ query });
|
||||||
|
};
|
||||||
|
|
||||||
|
return { submit, ...mutation };
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user