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 { Story, Meta } from '@storybook/react';
|
||||
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 { handlers } from '../../hooks/mocks/handlers.mock';
|
||||
|
||||
@ -30,3 +32,23 @@ export const Primary: Story<SuggestedRelationshipProps> = args => (
|
||||
Primary.args = {
|
||||
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 { SuggestedRelationshipForm } from './components';
|
||||
import { useSuggestedRelationships } from './hooks';
|
||||
|
||||
export interface SuggestedRelationshipProps {
|
||||
@ -15,6 +16,7 @@ export const SuggestedRelationships = ({
|
||||
target,
|
||||
}: SuggestedRelationshipProps) => {
|
||||
const { data, isLoading, isError } = useSuggestedRelationships(target);
|
||||
const [open, setOpen] = React.useState<string | null>(null);
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
@ -45,21 +47,25 @@ export const SuggestedRelationships = ({
|
||||
</CardedTable.TableHead>
|
||||
|
||||
<CardedTable.TableBody>
|
||||
{data?.map(relationship => (
|
||||
<CardedTable.TableBodyRow
|
||||
key={`${relationship.to}-${relationship.from}`}
|
||||
>
|
||||
{data.map(relationship => {
|
||||
// create a unique key
|
||||
const key = `${relationship.to.table}-${relationship.to.column}-${relationship.from.table}-${relationship.from.column}`;
|
||||
|
||||
return (
|
||||
<CardedTable.TableBodyRow key={key}>
|
||||
<CardedTable.TableBodyCell>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
console.log(
|
||||
'in the future ill open a form, but now i nothing :('
|
||||
)
|
||||
}
|
||||
>
|
||||
{open === key ? (
|
||||
<SuggestedRelationshipForm
|
||||
key={key}
|
||||
target={target}
|
||||
relationship={relationship}
|
||||
close={() => setOpen(null)}
|
||||
/>
|
||||
) : (
|
||||
<Button size="sm" onClick={() => setOpen(key)}>
|
||||
Add
|
||||
</Button>
|
||||
)}
|
||||
</CardedTable.TableBodyCell>
|
||||
<CardedTable.TableBodyCell>
|
||||
<FaTable className="text-sm text-muted mr-xs" />
|
||||
@ -84,7 +90,8 @@ export const SuggestedRelationships = ({
|
||||
{relationship.to.column}
|
||||
</CardedTable.TableBodyCell>
|
||||
</CardedTable.TableBodyRow>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</CardedTable.TableBody>
|
||||
</CardedTable.Table>
|
||||
</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 './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