mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
console: Add edit/view for native queries
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9307 GitOrigin-RevId: dddc9c2701a2f7958c423a5ddda6eb35163f425d
This commit is contained in:
parent
682af1e2b9
commit
608e1bf29e
@ -37,9 +37,13 @@ import { TableEditItemContainer } from './TableEditItem/TableEditItemContainer';
|
||||
import { TableInsertItemContainer } from './TableInsertItem/TableInsertItemContainer';
|
||||
import { ModifyTableContainer } from './TableModify/ModifyTableContainer';
|
||||
import { LandingPageRoute as NativeQueries } from '../../../features/Data/LogicalModels/LandingPage/LandingPage';
|
||||
import { AddNativeQueryRoute } from '../../../features/Data/LogicalModels/AddNativeQuery/AddNativeQueryRoute';
|
||||
|
||||
import { TrackStoredProcedureRoute } from '../../../features/Data/LogicalModels/StoredProcedures/StoredProcedureWidget.route';
|
||||
import { ManageFunction } from '../../../features/Data/ManageFunction/ManageFunction';
|
||||
import {
|
||||
UpdateNativeQueryRoute,
|
||||
AddNativeQueryRoute,
|
||||
} from '../../../features/Data/LogicalModels/AddNativeQuery';
|
||||
|
||||
const makeDataRouter = (
|
||||
connect,
|
||||
@ -82,6 +86,10 @@ const makeDataRouter = (
|
||||
<Route path="native-queries">
|
||||
<IndexRoute component={NativeQueries} />
|
||||
<Route path="create" component={AddNativeQueryRoute} />
|
||||
<Route
|
||||
path="native-query/:source/:name"
|
||||
component={UpdateNativeQueryRoute}
|
||||
/>
|
||||
<Route path="logical-models" component={NativeQueries} />
|
||||
<Route path="stored-procedures" component={NativeQueries} />
|
||||
<Route
|
||||
|
@ -7,8 +7,8 @@ export const AddNativeQueryRoute = withRouter<{
|
||||
router: InjectedRouter;
|
||||
}>(({ location, router }) => {
|
||||
return (
|
||||
<RouteWrapper pathname={location.pathname} push={router.push}>
|
||||
<AddNativeQuery push={router.push} />
|
||||
<RouteWrapper route={'/data/native-queries/create'}>
|
||||
<AddNativeQuery />
|
||||
</RouteWrapper>
|
||||
);
|
||||
});
|
@ -92,7 +92,7 @@ export const Basic: Story = {
|
||||
|
||||
export const WithRouteWrapper: Story = {
|
||||
render: args => (
|
||||
<RouteWrapper pathname={'/data/native-queries/create'}>
|
||||
<RouteWrapper route={'/data/native-queries/create'}>
|
||||
<AddNativeQuery {...args} />
|
||||
</RouteWrapper>
|
||||
),
|
||||
|
@ -8,9 +8,11 @@ import {
|
||||
useConsoleForm,
|
||||
} from '../../../../new-components/Form';
|
||||
// import { FormDebugWindow } from '../../../../new-components/Form/dev-components/FormDebugWindow';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { Driver, drivers } from '../../../../dataSources';
|
||||
import { IndicatorCard } from '../../../../new-components/IndicatorCard';
|
||||
import { hasuraToast } from '../../../../new-components/Toasts';
|
||||
import { usePushRoute } from '../../../ConnectDBRedesign/hooks';
|
||||
import { Feature } from '../../../DataSource';
|
||||
import { useMetadata } from '../../../hasura-metadata-api';
|
||||
import { useSupportedDataTypes } from '../../hooks/useSupportedDataTypes';
|
||||
@ -21,16 +23,15 @@ import { SqlEditorField } from './components/SqlEditorField';
|
||||
import { schema } from './schema';
|
||||
import { NativeQueryForm } from './types';
|
||||
import { transformFormOutputToMetadata } from './utils';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
type AddNativeQueryProps = {
|
||||
defaultFormValues?: Partial<NativeQueryForm>;
|
||||
push?: (path: string) => void;
|
||||
mode?: 'create' | 'update';
|
||||
};
|
||||
|
||||
export const AddNativeQuery = ({
|
||||
defaultFormValues,
|
||||
push,
|
||||
mode = 'create',
|
||||
}: AddNativeQueryProps) => {
|
||||
const {
|
||||
Form,
|
||||
@ -40,6 +41,8 @@ export const AddNativeQuery = ({
|
||||
options: { defaultValues: defaultFormValues },
|
||||
});
|
||||
|
||||
const push = usePushRoute();
|
||||
|
||||
const {
|
||||
data: sources,
|
||||
isLoading: isSourcesLoading,
|
||||
@ -59,7 +62,7 @@ export const AddNativeQuery = ({
|
||||
s => s.name === selectedSource
|
||||
)?.logical_models;
|
||||
|
||||
const { trackNativeQuery, isLoading } = useTrackNativeQuery();
|
||||
const { trackNativeQuery, isLoading: isSaving } = useTrackNativeQuery();
|
||||
|
||||
const [isLogicalModelsDialogOpen, setIsLogicalModelsDialogOpen] =
|
||||
React.useState(false);
|
||||
@ -77,7 +80,7 @@ export const AddNativeQuery = ({
|
||||
toastOptions: { duration: 3000 },
|
||||
});
|
||||
// Go to list
|
||||
push?.('/data/native-queries');
|
||||
push('/data/native-queries');
|
||||
},
|
||||
onError: err => {
|
||||
hasuraToast({
|
||||
@ -127,96 +130,107 @@ export const AddNativeQuery = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleFormSubmit}>
|
||||
{/* <FormDebugWindow /> */}
|
||||
<div className="max-w-xl flex flex-col">
|
||||
<GraphQLSanitizedInputField
|
||||
name="root_field_name"
|
||||
label="Native Query Name"
|
||||
placeholder="Name that exposes this model in GraphQL API"
|
||||
hideTips
|
||||
/>
|
||||
<InputField
|
||||
name="comment"
|
||||
label="Comment"
|
||||
placeholder="A description of this logical model"
|
||||
/>
|
||||
<Select
|
||||
name="source"
|
||||
label="Database"
|
||||
// saving prop for future update
|
||||
//noOptionsMessage="No databases found."
|
||||
loading={isSourcesLoading}
|
||||
options={(sources ?? []).map(m => ({
|
||||
label: m.name,
|
||||
value: m.name,
|
||||
}))}
|
||||
placeholder="Select a database..."
|
||||
/>
|
||||
</div>
|
||||
{isIntrospectionLoading ? (
|
||||
<div>
|
||||
<Skeleton />
|
||||
<Skeleton />
|
||||
</div>
|
||||
) : (
|
||||
<ArgumentsField types={typeOptions} />
|
||||
<div>
|
||||
{mode === 'update' && (
|
||||
<IndicatorCard status="info">
|
||||
The current release does not support editing Native Queries. This
|
||||
feature will be available in a future release. You can still edit
|
||||
directly by modifying the Metadata.
|
||||
</IndicatorCard>
|
||||
)}
|
||||
<SqlEditorField />
|
||||
<div className="flex w-full">
|
||||
{/* Logical Model Dropdown */}
|
||||
<Select
|
||||
name="returns"
|
||||
selectClassName="max-w-xl"
|
||||
// saving prop for future update
|
||||
// noOptionsMessage={
|
||||
// !selectedSource ? 'Select a database first.' : 'No models found.'
|
||||
// }
|
||||
// force component re-init on source change
|
||||
//key={selectedSource}
|
||||
label="Query Return Type"
|
||||
placeholder={logicalModelSelectPlaceholder()}
|
||||
loading={isSourcesLoading}
|
||||
options={(logicalModels ?? []).map(m => ({
|
||||
label: m.name,
|
||||
value: m.name,
|
||||
}))}
|
||||
/>
|
||||
<Button
|
||||
icon={<FaPlusCircle />}
|
||||
onClick={() => {
|
||||
setIsLogicalModelsDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Logical Model
|
||||
</Button>
|
||||
</div>
|
||||
{isLogicalModelsDialogOpen ? (
|
||||
<LogicalModelWidget
|
||||
onCancel={() => {
|
||||
setIsLogicalModelsDialogOpen(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
setIsLogicalModelsDialogOpen(false);
|
||||
}}
|
||||
asDialog
|
||||
/>
|
||||
) : null}
|
||||
<div className="flex flex-row justify-end gap-2">
|
||||
{/*
|
||||
<Form onSubmit={handleFormSubmit}>
|
||||
<fieldset disabled={mode === 'update'}>
|
||||
{/* <FormDebugWindow /> */}
|
||||
<div className="max-w-xl flex flex-col">
|
||||
<GraphQLSanitizedInputField
|
||||
name="root_field_name"
|
||||
label="Native Query Name"
|
||||
placeholder="Name that exposes this model in GraphQL API"
|
||||
hideTips
|
||||
/>
|
||||
<InputField
|
||||
name="comment"
|
||||
label="Comment"
|
||||
placeholder="A description of this logical model"
|
||||
/>
|
||||
<Select
|
||||
name="source"
|
||||
label="Database"
|
||||
// saving prop for future update
|
||||
//noOptionsMessage="No databases found."
|
||||
loading={isSourcesLoading}
|
||||
options={(sources ?? []).map(m => ({
|
||||
label: m.name,
|
||||
value: m.name,
|
||||
}))}
|
||||
placeholder="Select a database..."
|
||||
/>
|
||||
</div>
|
||||
{isIntrospectionLoading ? (
|
||||
<div>
|
||||
<Skeleton />
|
||||
<Skeleton />
|
||||
</div>
|
||||
) : (
|
||||
<ArgumentsField types={typeOptions} />
|
||||
)}
|
||||
<SqlEditorField />
|
||||
<div className="flex w-full">
|
||||
{/* Logical Model Dropdown */}
|
||||
<Select
|
||||
name="returns"
|
||||
selectClassName="max-w-xl"
|
||||
// saving prop for future update
|
||||
// noOptionsMessage={
|
||||
// !selectedSource ? 'Select a database first.' : 'No models found.'
|
||||
// }
|
||||
// force component re-init on source change
|
||||
//key={selectedSource}
|
||||
label="Query Return Type"
|
||||
placeholder={logicalModelSelectPlaceholder()}
|
||||
loading={isSourcesLoading}
|
||||
options={(logicalModels ?? []).map(m => ({
|
||||
label: m.name,
|
||||
value: m.name,
|
||||
}))}
|
||||
/>
|
||||
<Button
|
||||
icon={<FaPlusCircle />}
|
||||
onClick={e => {
|
||||
setIsLogicalModelsDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Logical Model
|
||||
</Button>
|
||||
</div>
|
||||
{isLogicalModelsDialogOpen ? (
|
||||
<LogicalModelWidget
|
||||
onCancel={() => {
|
||||
setIsLogicalModelsDialogOpen(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
setIsLogicalModelsDialogOpen(false);
|
||||
}}
|
||||
asDialog
|
||||
/>
|
||||
) : null}
|
||||
<div className="flex flex-row justify-end gap-2">
|
||||
{/*
|
||||
Validate Button will remain hidden until we have more information about how to handle standalone validation
|
||||
Slack thread: https://hasurahq.slack.com/archives/C04LV93JNSH/p1682965503376129
|
||||
*/}
|
||||
{/* <Button icon={<FaPlay />}>Validate</Button> */}
|
||||
<Button
|
||||
type="submit"
|
||||
icon={<FaSave />}
|
||||
mode="primary"
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
{/* <Button icon={<FaPlay />}>Validate</Button> */}
|
||||
<Button
|
||||
type="submit"
|
||||
icon={<FaSave />}
|
||||
mode="primary"
|
||||
isLoading={isSaving}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,58 @@
|
||||
import { InjectedRouter, withRouter } from 'react-router';
|
||||
import { IndicatorCard } from '../../../../new-components/IndicatorCard';
|
||||
import { MetadataUtils, useMetadata } from '../../../hasura-metadata-api';
|
||||
import { RouteWrapper } from '../components/RouteWrapper';
|
||||
import { AddNativeQuery } from './AddNativeQuery';
|
||||
import { normalizeArguments } from './utils';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
export const UpdateNativeQueryRoute = withRouter<{
|
||||
location: Location;
|
||||
router: InjectedRouter;
|
||||
params: { source: string; name: string };
|
||||
}>(({ location, router, params: { source, name } }) => {
|
||||
if (!source || !name) {
|
||||
return (
|
||||
<IndicatorCard status="negative">
|
||||
Unable to parse data from url.
|
||||
</IndicatorCard>
|
||||
);
|
||||
}
|
||||
|
||||
const { data: sourceQueries, isLoading } = useMetadata(
|
||||
m => MetadataUtils.findMetadataSource(source, m)?.native_queries
|
||||
);
|
||||
|
||||
const nativeQuery = sourceQueries?.find(s => s.root_field_name === name);
|
||||
|
||||
if (!nativeQuery) {
|
||||
return (
|
||||
<IndicatorCard status="negative">
|
||||
Native Query {name} not found in {source}
|
||||
</IndicatorCard>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<RouteWrapper
|
||||
route={'/data/native-queries/{{source}}/{{name}}'}
|
||||
itemSourceName={source}
|
||||
itemName={name}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div>
|
||||
<Skeleton count={10} />
|
||||
</div>
|
||||
) : (
|
||||
<AddNativeQuery
|
||||
mode={'update'}
|
||||
defaultFormValues={{
|
||||
...nativeQuery,
|
||||
source: source,
|
||||
arguments: normalizeArguments(nativeQuery.arguments ?? {}),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</RouteWrapper>
|
||||
);
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
export { AddNativeQueryRoute } from './AddNativeQuery.route';
|
||||
export { AddNativeQuery } from './AddNativeQuery';
|
||||
export { UpdateNativeQueryRoute } from './UpdateNativeQuery.route';
|
@ -1,5 +1,8 @@
|
||||
import { NativeQueryForm } from './types';
|
||||
import { NativeQuery as NativeQueryMetadataType } from '../../../hasura-metadata-types';
|
||||
import {
|
||||
NativeQueryArgument,
|
||||
NativeQuery as NativeQueryMetadataType,
|
||||
} from '../../../hasura-metadata-types';
|
||||
import { NativeQueryArgumentNormalized, NativeQueryForm } from './types';
|
||||
|
||||
export const transformFormOutputToMetadata = (
|
||||
formValues: NativeQueryForm
|
||||
@ -28,3 +31,11 @@ export const transformFormOutputToMetadata = (
|
||||
type,
|
||||
};
|
||||
};
|
||||
|
||||
export const normalizeArguments = (
|
||||
args: Record<string, NativeQueryArgument>
|
||||
): NativeQueryArgumentNormalized[] =>
|
||||
Object.entries(args).map(([name, argument]) => ({
|
||||
name,
|
||||
...argument,
|
||||
}));
|
||||
|
@ -1,30 +1,25 @@
|
||||
import { Tabs } from '../../../../new-components/Tabs';
|
||||
import { useMetadata } from '../../../hasura-metadata-api';
|
||||
import { ListLogicalModels } from './components/ListLogicalModels';
|
||||
import { ListNativeQueries } from './components/ListNativeQueries';
|
||||
import { extractModelsAndQueriesFromMetadata } from '../utils';
|
||||
import {
|
||||
useDestructiveAlert,
|
||||
useHasuraAlert,
|
||||
} from '../../../../new-components/Alert';
|
||||
import { LogicalModelWithSource, NativeQueryWithSource } from '../types';
|
||||
import { useTrackNativeQuery } from '../../hooks/useTrackNativeQuery';
|
||||
import { useTrackLogicalModel } from '../../hooks/useTrackLogicalModel';
|
||||
import { hasuraToast } from '../../../../new-components/Toasts';
|
||||
import { useState } from 'react';
|
||||
import { InjectedRouter, Link, withRouter } from 'react-router';
|
||||
import { LogicalModelWidget } from '../LogicalModelWidget/LogicalModelWidget';
|
||||
import { useDestructiveAlert } from '../../../../new-components/Alert';
|
||||
import { Button } from '../../../../new-components/Button';
|
||||
import { Tabs } from '../../../../new-components/Tabs';
|
||||
import { hasuraToast } from '../../../../new-components/Toasts';
|
||||
import { usePushRoute } from '../../../ConnectDBRedesign/hooks';
|
||||
import { useMetadata } from '../../../hasura-metadata-api';
|
||||
import { useTrackLogicalModel } from '../../hooks/useTrackLogicalModel';
|
||||
import { useTrackNativeQuery } from '../../hooks/useTrackNativeQuery';
|
||||
import { LogicalModelWidget } from '../LogicalModelWidget/LogicalModelWidget';
|
||||
import { RouteWrapper } from '../components/RouteWrapper';
|
||||
import { ListStoredProcedures } from './components/ListStoredProcedures';
|
||||
|
||||
export const LandingPage = ({
|
||||
push,
|
||||
pathname,
|
||||
}: {
|
||||
pathname: string | undefined;
|
||||
push?: (to: string) => void;
|
||||
}) => {
|
||||
import { LogicalModelWithSource, NativeQueryWithSource } from '../types';
|
||||
import { extractModelsAndQueriesFromMetadata } from '../utils';
|
||||
import { ListLogicalModels } from './components/ListLogicalModels';
|
||||
import { ListNativeQueries } from './components/ListNativeQueries';
|
||||
import { ListStoredProcedures } from './components/ListStoredProcedures';
|
||||
import { NATIVE_QUERY_ROUTES } from '../constants';
|
||||
|
||||
export const LandingPage = ({ pathname }: { pathname: string }) => {
|
||||
const push = usePushRoute();
|
||||
const { data, isLoading } = useMetadata(m =>
|
||||
extractModelsAndQueriesFromMetadata(m)
|
||||
);
|
||||
@ -44,8 +39,6 @@ export const LandingPage = ({
|
||||
const [isLogicalModelsDialogOpen, setIsLogicalModelsDialogOpen] =
|
||||
useState(false);
|
||||
|
||||
const { hasuraAlert } = useHasuraAlert();
|
||||
|
||||
const { destructiveConfirm } = useDestructiveAlert();
|
||||
|
||||
const { untrackNativeQuery } = useTrackNativeQuery();
|
||||
@ -133,12 +126,10 @@ export const LandingPage = ({
|
||||
<ListNativeQueries
|
||||
nativeQueries={nativeQueries}
|
||||
isLoading={isLoading}
|
||||
onEditClick={() => {
|
||||
hasuraAlert({
|
||||
title: 'Not Implemented',
|
||||
message:
|
||||
'Editing is not implemented in the alpha release',
|
||||
});
|
||||
onEditClick={query => {
|
||||
push?.(
|
||||
`data/native-queries/native-query/${query.source.name}/${query.root_field_name}`
|
||||
);
|
||||
}}
|
||||
onRemoveClick={handleRemoveNativeQuery}
|
||||
/>
|
||||
@ -202,8 +193,8 @@ export const LandingPageRoute = withRouter<{
|
||||
router: InjectedRouter;
|
||||
}>(({ location, router }) => {
|
||||
return (
|
||||
<RouteWrapper pathname={location.pathname} push={router.push}>
|
||||
<LandingPage pathname={location.pathname} push={router.push} />
|
||||
<RouteWrapper route={location.pathname as keyof typeof NATIVE_QUERY_ROUTES}>
|
||||
<LandingPage pathname={location.pathname} />
|
||||
</RouteWrapper>
|
||||
);
|
||||
});
|
||||
|
@ -4,10 +4,12 @@ import {
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import React from 'react';
|
||||
import { CgDetailsMore } from 'react-icons/cg';
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { Button } from '../../../../../new-components/Button';
|
||||
import { NativeQueryWithSource } from '../../types';
|
||||
import { CardedTableFromReactTable } from '../../components/CardedTableFromReactTable';
|
||||
import { NativeQueryWithSource } from '../../types';
|
||||
|
||||
const columnHelper = createColumnHelper<NativeQueryWithSource>();
|
||||
|
||||
@ -44,10 +46,18 @@ export const ListNativeQueries = ({
|
||||
header: 'Actions',
|
||||
cell: ({ cell, row }) => (
|
||||
<div className="flex flex-row gap-2">
|
||||
{/* Re add once we implement Edit functionality */}
|
||||
{/* <Button onClick={() => onEditClick(row.original)}>Edit</Button> */}
|
||||
<Button
|
||||
// icon={<FaEdit />}
|
||||
icon={<CgDetailsMore />}
|
||||
onClick={() => onEditClick(row.original)}
|
||||
>
|
||||
{/* Edit */}
|
||||
{/* Change back to Edit once we support it */}
|
||||
View
|
||||
</Button>
|
||||
<Button
|
||||
mode="destructive"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => onRemoveClick(row.original)}
|
||||
>
|
||||
Remove
|
||||
|
@ -1,9 +1,11 @@
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { LimitedFeatureWrapper } from '../../../ConnectDBRedesign/components/LimitedFeatureWrapper/LimitedFeatureWrapper';
|
||||
import { useServerConfig } from '../../../../hooks';
|
||||
import { ReactNode } from 'react';
|
||||
import { Breadcrumbs } from '../../../../new-components/Breadcrumbs';
|
||||
import startCase from 'lodash/startCase';
|
||||
import { ReactNode } from 'react';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { useServerConfig } from '../../../../hooks';
|
||||
import { Breadcrumbs } from '../../../../new-components/Breadcrumbs';
|
||||
import { LimitedFeatureWrapper } from '../../../ConnectDBRedesign/components/LimitedFeatureWrapper/LimitedFeatureWrapper';
|
||||
import { usePushRoute } from '../../../ConnectDBRedesign/hooks';
|
||||
import { NATIVE_QUERY_ROUTES } from '../constants';
|
||||
|
||||
function NativeQueriesFeatureFlag({ children }: { children: ReactNode }) {
|
||||
const { data: serverConfig, isLoading: isLoadingServerConfig } =
|
||||
@ -32,53 +34,21 @@ function NativeQueriesFeatureFlag({ children }: { children: ReactNode }) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
type AllowedTabs =
|
||||
| 'logical-models'
|
||||
| 'native-queries'
|
||||
| 'stored-procedures'
|
||||
| 'create';
|
||||
const getTitleAndSubtitle = (tabType: AllowedTabs) => {
|
||||
if (tabType === 'logical-models')
|
||||
return {
|
||||
title: 'Logical Models',
|
||||
subtitle:
|
||||
'Creating Logical Models in advance can help generate Native Queries faster',
|
||||
};
|
||||
if (tabType === 'native-queries')
|
||||
return {
|
||||
title: 'Native Queries',
|
||||
subtitle:
|
||||
'Access more queries and operators through SQL on your database',
|
||||
};
|
||||
if (tabType === 'stored-procedures')
|
||||
return {
|
||||
title: 'Stored Procedures',
|
||||
subtitle: 'Add support for stored procedures on SQL over a GraphQL API',
|
||||
};
|
||||
if (tabType === 'create')
|
||||
return {
|
||||
title: 'Create Native Query',
|
||||
subtitle:
|
||||
'Access more queries and operators through SQL on your database',
|
||||
};
|
||||
return {
|
||||
title: 'Not a valid path',
|
||||
subtitle: '',
|
||||
};
|
||||
};
|
||||
export const RouteWrapper: React.FC<{
|
||||
route: keyof typeof NATIVE_QUERY_ROUTES;
|
||||
itemSourceName?: string;
|
||||
itemName?: string;
|
||||
}> = ({ children, route, itemSourceName, itemName }) => {
|
||||
const paths =
|
||||
route
|
||||
?.split('/')
|
||||
.filter(Boolean)
|
||||
.filter(p => p !== '{{source}}') ?? [];
|
||||
|
||||
const { title, subtitle } = NATIVE_QUERY_ROUTES[route];
|
||||
|
||||
const push = usePushRoute();
|
||||
|
||||
export const RouteWrapper = ({
|
||||
children,
|
||||
pathname,
|
||||
push,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
pathname: string | undefined;
|
||||
push?: (to: string) => void;
|
||||
}) => {
|
||||
const paths = pathname?.split('/').filter(Boolean) ?? [];
|
||||
const tabType = paths[paths.length - 1] as AllowedTabs;
|
||||
const { title, subtitle } = getTitleAndSubtitle(tabType);
|
||||
return (
|
||||
<div className="py-md px-md w-full">
|
||||
<LimitedFeatureWrapper
|
||||
@ -91,19 +61,26 @@ export const RouteWrapper = ({
|
||||
<Breadcrumbs
|
||||
items={paths.map((path: string, index) => {
|
||||
return {
|
||||
title: startCase(path),
|
||||
title: startCase(
|
||||
path
|
||||
// we don't need to display source
|
||||
.replace('{{source}}', itemSourceName ?? '')
|
||||
.replace('{{name}}', itemName ?? '')
|
||||
),
|
||||
onClick:
|
||||
index === paths.length - 1
|
||||
? undefined
|
||||
: () => {
|
||||
push?.(`/${paths.slice(0, index + 1).join('/')}`);
|
||||
push(`/${paths.slice(0, index + 1).join('/')}`);
|
||||
},
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<div className="flex w-full justify-between px-2">
|
||||
<div className="mb-sm">
|
||||
<div className="text-xl font-bold mt-2">{title}</div>
|
||||
<div className="text-xl font-bold mt-2">
|
||||
{title.replace('{{name}}', itemName ?? '')}
|
||||
</div>
|
||||
<div className="text-muted">{subtitle}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,3 +8,45 @@ export const STORED_PROCEDURE_UNTRACK_SUCCESS =
|
||||
'Successfully untracked Stored Procedure';
|
||||
export const STORED_PROCEDURE_UNTRACK_ERROR =
|
||||
'Unable to untrack Stored Procedure';
|
||||
|
||||
export const NATIVE_QUERY_ROUTES = {
|
||||
'/data/native-queries': {
|
||||
title: 'Native Queries',
|
||||
subtitle: 'Access more queries and operators through SQL on your database',
|
||||
},
|
||||
'/data/native-queries/create': {
|
||||
title: 'Create Native Query',
|
||||
subtitle: 'Access more queries and operators through SQL on your database',
|
||||
},
|
||||
'/data/native-queries/{{source}}/{{name}}': {
|
||||
title: '{{name}}',
|
||||
subtitle: 'Access more queries and operators through SQL on your database',
|
||||
},
|
||||
'/data/native-queries/logical-models': {
|
||||
title: 'Logical Models',
|
||||
subtitle:
|
||||
'Creating Logical Models in advance can help generate Native Queries faster',
|
||||
},
|
||||
'/data/native-queries/logical-models/create': {
|
||||
title: 'Logical Models',
|
||||
subtitle:
|
||||
'Creating Logical Models in advance can help generate Native Queries faster',
|
||||
},
|
||||
'/data/native-queries/logical-models/{{source}}/{{name}}': {
|
||||
title: '{{name}}',
|
||||
subtitle:
|
||||
'Creating Logical Models in advance can help generate Native Queries faster',
|
||||
},
|
||||
'/data/native-queries/stored-procedures': {
|
||||
title: 'Stored Procedures',
|
||||
subtitle: 'Add support for stored procedures on SQL over a GraphQL API',
|
||||
},
|
||||
'/data/native-queries/stored-procedures/track': {
|
||||
title: 'Track Stored Procedure',
|
||||
subtitle: 'Expose your stored SQL procedures via the GraphQL API',
|
||||
},
|
||||
'/data/native-queries/stored-procedures/{{source}}/{{name}}': {
|
||||
title: 'Track Stored Procedure',
|
||||
subtitle: 'Expose your stored SQL procedures via the GraphQL API',
|
||||
},
|
||||
};
|
||||
|
@ -80,6 +80,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
loadingText,
|
||||
disabled,
|
||||
full,
|
||||
|
||||
...otherHtmlAttributes
|
||||
} = props;
|
||||
|
||||
@ -87,9 +88,20 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
|
||||
const styles = twButtonStyles;
|
||||
|
||||
const buttonAttributes = {
|
||||
const buttonAttributes: typeof props = {
|
||||
type,
|
||||
...otherHtmlAttributes,
|
||||
onClick: e => {
|
||||
if (
|
||||
e.target instanceof HTMLElement &&
|
||||
e.target.closest('fieldset:disabled')
|
||||
) {
|
||||
//this prevents clicks when a fieldset enclosing this button is set to disabled.
|
||||
// this is due to a bug in react that's been documented here: https://github.com/facebook/react/issues/7711
|
||||
return;
|
||||
}
|
||||
props?.onClick?.(e);
|
||||
},
|
||||
disabled: isDisabled,
|
||||
className: clsx(
|
||||
styles.all,
|
||||
|
Loading…
Reference in New Issue
Block a user