mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +03:00
console: Refactor duplicated logical model and add native query wrappers into one
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9199 GitOrigin-RevId: 87ded79e4130a9622b6733055882cee9096088ba
This commit is contained in:
parent
62304a9182
commit
6caf33bf27
@ -18,7 +18,7 @@ export default {
|
||||
} as ComponentMeta<typeof AddNativeQuery>;
|
||||
|
||||
export const Basic: ComponentStory<typeof AddNativeQuery> = args => {
|
||||
return <AddNativeQuery pathname="/data/native-queries/create" />;
|
||||
return <AddNativeQuery />;
|
||||
};
|
||||
|
||||
const fillAndSubmitForm = async ({
|
||||
@ -83,7 +83,6 @@ const fillAndSubmitForm = async ({
|
||||
export const HappyPath: ComponentStory<typeof AddNativeQuery> = args => {
|
||||
return (
|
||||
<AddNativeQuery
|
||||
pathname="/data/native-queries/create"
|
||||
defaultFormValues={{
|
||||
code: `SELECT * FROM (VALUES ('hello', 'world'), ('welcome', 'friend')) as t("one", "two")`,
|
||||
}}
|
||||
@ -112,7 +111,6 @@ HappyPath.play = async ({ canvasElement }) => {
|
||||
export const ErrorExists: ComponentStory<typeof AddNativeQuery> = args => {
|
||||
return (
|
||||
<AddNativeQuery
|
||||
pathname="/data/native-queries/create"
|
||||
defaultFormValues={{
|
||||
code: `SELECT * FROM (VALUES ('hello', 'world'), ('welcome', 'friend')) as t("one", "two")`,
|
||||
}}
|
||||
@ -146,7 +144,6 @@ ErrorExists.play = async ({ canvasElement }) => {
|
||||
export const ErrorValidation: ComponentStory<typeof AddNativeQuery> = args => {
|
||||
return (
|
||||
<AddNativeQuery
|
||||
pathname="/data/native-queries/create"
|
||||
defaultFormValues={{
|
||||
code: `select * from foo`,
|
||||
}}
|
||||
@ -180,7 +177,6 @@ ErrorValidation.play = async ({ canvasElement }) => {
|
||||
export const ErrorDisabled: ComponentStory<typeof AddNativeQuery> = args => {
|
||||
return (
|
||||
<AddNativeQuery
|
||||
pathname="/data/native-queries/create"
|
||||
defaultFormValues={{
|
||||
code: `SELECT * FROM (VALUES ('hello', 'world'), ('welcome', 'friend')) as t("one", "two")`,
|
||||
}}
|
||||
|
@ -8,7 +8,6 @@ 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';
|
||||
@ -18,20 +17,19 @@ import { useSupportedDataTypes } from '../../hooks/useSupportedDataTypes';
|
||||
import { useTrackNativeQuery } from '../../hooks/useTrackNativeQuery';
|
||||
import { LogicalModelWidget } from '../LogicalModelWidget/LogicalModelWidget';
|
||||
import { ArgumentsField } from './components/ArgumentsField';
|
||||
import { PageWrapper } from './components/PageWrapper';
|
||||
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>;
|
||||
pathname: string;
|
||||
push?: (path: string) => void;
|
||||
};
|
||||
|
||||
export const AddNativeQuery = ({
|
||||
defaultFormValues,
|
||||
pathname,
|
||||
push,
|
||||
}: AddNativeQueryProps) => {
|
||||
const {
|
||||
@ -129,93 +127,91 @@ export const AddNativeQuery = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<PageWrapper pathname={pathname} push={push}>
|
||||
<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..."
|
||||
/>
|
||||
<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>
|
||||
{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={() => {
|
||||
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">
|
||||
{/*
|
||||
) : (
|
||||
<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={() => {
|
||||
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">
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</PageWrapper>
|
||||
{/* <Button icon={<FaPlay />}>Validate</Button> */}
|
||||
<Button type="submit" icon={<FaSave />} mode="primary">
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
@ -1,9 +1,14 @@
|
||||
import { InjectedRouter, withRouter } from 'react-router';
|
||||
import { AddNativeQuery } from './AddNativeQuery';
|
||||
import { RouteWrapper } from '../components/RouteWrapper';
|
||||
|
||||
export const AddNativeQueryRoute = withRouter<{
|
||||
location: Location;
|
||||
router: InjectedRouter;
|
||||
}>(({ location, router }) => {
|
||||
return <AddNativeQuery pathname={location.pathname} push={router.push} />;
|
||||
return (
|
||||
<RouteWrapper pathname={location.pathname} push={router.push}>
|
||||
<AddNativeQuery push={router.push} />
|
||||
</RouteWrapper>
|
||||
);
|
||||
});
|
||||
|
@ -1,35 +0,0 @@
|
||||
import { Breadcrumbs } from '../../../../../new-components/Breadcrumbs';
|
||||
import startCase from 'lodash/startCase';
|
||||
|
||||
export const PageWrapper: React.FC<{
|
||||
pathname: string | undefined;
|
||||
push?: (to: string) => void;
|
||||
}> = ({ children, push, pathname }) => {
|
||||
const paths = pathname?.split('/').filter(Boolean) ?? [];
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="py-md px-md w-full">
|
||||
<Breadcrumbs
|
||||
items={paths.map((path: string, index) => {
|
||||
return {
|
||||
title: startCase(path),
|
||||
onClick:
|
||||
index === paths.length - 1
|
||||
? undefined
|
||||
: () => {
|
||||
push?.(`/${paths.slice(0, index + 1).join('/')}`);
|
||||
},
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<div className="w-full">
|
||||
<div className="text-xl font-bold mt-2">Create Native Query</div>
|
||||
<div className="text-muted">
|
||||
Access more queries and operators through SQL on your database.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-md w-full flex flex-col">{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,4 +1,3 @@
|
||||
import { Breadcrumbs } from '../../../../new-components/Breadcrumbs';
|
||||
import { Tabs } from '../../../../new-components/Tabs';
|
||||
import { useMetadata } from '../../../hasura-metadata-api';
|
||||
import { ListLogicalModels } from './components/ListLogicalModels';
|
||||
@ -12,44 +11,16 @@ import { LogicalModelWithSource, NativeQueryWithSource } from '../types';
|
||||
import { useTrackNativeQuery } from '../../hooks/useTrackNativeQuery';
|
||||
import { useTrackLogicalModel } from '../../hooks/useTrackLogicalModel';
|
||||
import { hasuraToast } from '../../../../new-components/Toasts';
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { InjectedRouter, Link, withRouter } from 'react-router';
|
||||
import { LogicalModelWidget } from '../LogicalModelWidget/LogicalModelWidget';
|
||||
import { Button } from '../../../../new-components/Button';
|
||||
import startCase from 'lodash/startCase';
|
||||
import { LimitedFeatureWrapper } from '../../../ConnectDBRedesign/components/LimitedFeatureWrapper/LimitedFeatureWrapper';
|
||||
import { useServerConfig } from '../../../../hooks';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { RouteWrapper } from '../components/RouteWrapper';
|
||||
import { ListStoredProcedures } from '../StoredProcedures/ListStoredProcedures';
|
||||
|
||||
type AllowedTabs = 'logical-models' | 'native-queries' | 'stored-procedures';
|
||||
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',
|
||||
};
|
||||
return {
|
||||
title: 'Not a valid path',
|
||||
subtitle: '',
|
||||
};
|
||||
};
|
||||
|
||||
export const LandingPage = ({
|
||||
pathname,
|
||||
push,
|
||||
pathname,
|
||||
}: {
|
||||
pathname: string | undefined;
|
||||
push?: (to: string) => void;
|
||||
@ -69,9 +40,6 @@ export const LandingPage = ({
|
||||
);
|
||||
const nativeQueries = data?.queries ?? [];
|
||||
const logicalModels = data?.models ?? [];
|
||||
const paths = pathname?.split('/').filter(Boolean) ?? [];
|
||||
const tabType = paths[paths.length - 1] as AllowedTabs;
|
||||
const { title, subtitle } = getTitleAndSubtitle(tabType);
|
||||
|
||||
const [isLogicalModelsDialogOpen, setIsLogicalModelsDialogOpen] =
|
||||
useState(false);
|
||||
@ -135,158 +103,107 @@ export const LandingPage = ({
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="py-md px-md w-full">
|
||||
<LimitedFeatureWrapper
|
||||
title="Looking to add Native Queries?"
|
||||
id="native-queries"
|
||||
description="Get production-ready today with a 30-day free trial of Hasura EE, no credit card required."
|
||||
>
|
||||
<NativeQueriesFeatureFlag>
|
||||
<div className="flex flex-col">
|
||||
<Breadcrumbs
|
||||
items={paths.map((path: string, index) => {
|
||||
return {
|
||||
title: startCase(path),
|
||||
onClick:
|
||||
index === paths.length - 1
|
||||
? undefined
|
||||
: () => {
|
||||
push?.(paths.slice(0, index + 1).join('/'));
|
||||
},
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<div className="flex w-full justify-between">
|
||||
<div className="mb-sm">
|
||||
<div className="text-xl font-bold mt-2">{title}</div>
|
||||
<div className="text-muted">{subtitle}</div>
|
||||
</div>
|
||||
</div>
|
||||
{isLogicalModelsDialogOpen ? (
|
||||
<LogicalModelWidget
|
||||
onCancel={() => {
|
||||
setIsLogicalModelsDialogOpen(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
setIsLogicalModelsDialogOpen(false);
|
||||
}}
|
||||
asDialog
|
||||
/>
|
||||
) : null}
|
||||
<Tabs
|
||||
defaultValue={pathname}
|
||||
onValueChange={value => {
|
||||
push?.(value);
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
value: '/data/native-queries',
|
||||
label: `Native Queries (${nativeQueries.length})`,
|
||||
content: (
|
||||
<div className="mt-md">
|
||||
<ListNativeQueries
|
||||
nativeQueries={nativeQueries}
|
||||
isLoading={isLoading}
|
||||
onEditClick={() => {
|
||||
hasuraAlert({
|
||||
title: 'Not Implemented',
|
||||
message:
|
||||
'Editing is not implemented in the alpha release',
|
||||
});
|
||||
}}
|
||||
onRemoveClick={handleRemoveNativeQuery}
|
||||
/>
|
||||
<div className="flex justify-end mt-sm">
|
||||
<Link to="/data/native-queries/create">
|
||||
<Button mode="primary">Create Native Query</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: '/data/native-queries/logical-models',
|
||||
label: `Logical Models (${logicalModels.length})`,
|
||||
content: (
|
||||
<div className="mt-md">
|
||||
<ListLogicalModels
|
||||
logicalModels={logicalModels}
|
||||
isLoading={isLoading}
|
||||
onEditClick={() => {
|
||||
hasuraAlert({
|
||||
title: 'Not Implemented',
|
||||
message:
|
||||
'Editing is not implemented in the alpha release',
|
||||
});
|
||||
}}
|
||||
onRemoveClick={handleRemoveLogicalModel}
|
||||
/>
|
||||
<div className="flex justify-end mt-sm">
|
||||
<Button
|
||||
mode="primary"
|
||||
onClick={() => {
|
||||
setIsLogicalModelsDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Logical Model
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: '/data/native-queries/stored-procedures',
|
||||
label: `Stored Procedures (${storedProcedures.length})`,
|
||||
content: (
|
||||
<div className="mt-md">
|
||||
<ListStoredProcedures />
|
||||
<div className="flex justify-end mt-sm">
|
||||
<Link to="/data/native-queries/stored-procedures/track">
|
||||
<Button mode="primary">Track Stored Procedure</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</NativeQueriesFeatureFlag>
|
||||
</LimitedFeatureWrapper>
|
||||
<div className="w-full">
|
||||
<div className="flex flex-col">
|
||||
{isLogicalModelsDialogOpen ? (
|
||||
<LogicalModelWidget
|
||||
onCancel={() => {
|
||||
setIsLogicalModelsDialogOpen(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
setIsLogicalModelsDialogOpen(false);
|
||||
}}
|
||||
asDialog
|
||||
/>
|
||||
) : null}
|
||||
<Tabs
|
||||
key={pathname}
|
||||
defaultValue={pathname}
|
||||
onValueChange={value => {
|
||||
push?.(value);
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
value: '/data/native-queries',
|
||||
label: `Native Queries (${nativeQueries.length})`,
|
||||
content: (
|
||||
<div className="mt-md">
|
||||
<ListNativeQueries
|
||||
nativeQueries={nativeQueries}
|
||||
isLoading={isLoading}
|
||||
onEditClick={() => {
|
||||
hasuraAlert({
|
||||
title: 'Not Implemented',
|
||||
message:
|
||||
'Editing is not implemented in the alpha release',
|
||||
});
|
||||
}}
|
||||
onRemoveClick={handleRemoveNativeQuery}
|
||||
/>
|
||||
<div className="flex justify-end mt-sm">
|
||||
<Link to="/data/native-queries/create">
|
||||
<Button mode="primary">Create Native Query</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: '/data/native-queries/logical-models',
|
||||
label: `Logical Models (${logicalModels.length})`,
|
||||
content: (
|
||||
<div className="mt-md">
|
||||
<ListLogicalModels
|
||||
logicalModels={logicalModels}
|
||||
isLoading={isLoading}
|
||||
onEditClick={() => {
|
||||
push?.('/data/native-queries/logical-models/permissions');
|
||||
}}
|
||||
onRemoveClick={handleRemoveLogicalModel}
|
||||
/>
|
||||
<div className="flex justify-end mt-sm">
|
||||
<Button
|
||||
mode="primary"
|
||||
onClick={() => {
|
||||
setIsLogicalModelsDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Logical Model
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: '/data/native-queries/stored-procedures',
|
||||
label: `Stored Procedures (${storedProcedures.length})`,
|
||||
content: (
|
||||
<div className="mt-md">
|
||||
<ListStoredProcedures />
|
||||
<div className="flex justify-end mt-sm">
|
||||
<Link to="/data/native-queries/stored-procedures/track">
|
||||
<Button mode="primary">Track Stored Procedure</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function NativeQueriesFeatureFlag({ children }: { children: ReactNode }) {
|
||||
const { data: serverConfig, isLoading: isLoadingServerConfig } =
|
||||
useServerConfig();
|
||||
if (isLoadingServerConfig) {
|
||||
return <Skeleton count={2} />;
|
||||
}
|
||||
const areNativeQueriesEnabled = serverConfig?.feature_flags?.find(
|
||||
feature => feature.name === 'native-query-interface' && feature.enabled
|
||||
);
|
||||
if (!areNativeQueriesEnabled) {
|
||||
return (
|
||||
<div className="bg-white border-2 shadow-sm p-5 rounded space-y-4">
|
||||
<div className="text-xl text-slate-900">
|
||||
Looking to try Native Queries?
|
||||
</div>
|
||||
<div className="text-base text-muted max-w-2xl">
|
||||
Native Queries are not enabled. To enable them, start the Hasura
|
||||
server with the environment variable
|
||||
<code>HASURA_FF_NATIVE_QUERY_INTERFACE: 'True'</code>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return children as JSX.Element;
|
||||
}
|
||||
|
||||
export const LandingPageRoute = withRouter<{
|
||||
location: Location;
|
||||
router: InjectedRouter;
|
||||
}>(({ location, router }) => {
|
||||
return <LandingPage pathname={location.pathname} push={router.push} />;
|
||||
return (
|
||||
<RouteWrapper pathname={location.pathname} push={router.push}>
|
||||
<LandingPage pathname={location.pathname} push={router.push} />
|
||||
</RouteWrapper>
|
||||
);
|
||||
});
|
||||
|
@ -0,0 +1,116 @@
|
||||
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';
|
||||
|
||||
function NativeQueriesFeatureFlag({ children }: { children: ReactNode }) {
|
||||
const { data: serverConfig, isLoading: isLoadingServerConfig } =
|
||||
useServerConfig();
|
||||
if (isLoadingServerConfig) {
|
||||
return <Skeleton count={2} />;
|
||||
}
|
||||
const areNativeQueriesEnabled = serverConfig?.feature_flags?.find(
|
||||
feature => feature.name === 'native-query-interface' && feature.enabled
|
||||
);
|
||||
if (!areNativeQueriesEnabled) {
|
||||
return (
|
||||
<div className="bg-white border-2 shadow-sm p-5 rounded space-y-4">
|
||||
<div className="text-xl text-slate-900">
|
||||
Looking to try Native Queries?
|
||||
</div>
|
||||
<div className="text-base text-muted max-w-2xl">
|
||||
Native Queries are not enabled. To enable them, start the Hasura
|
||||
server with the environment variable
|
||||
<code>HASURA_FF_NATIVE_QUERY_INTERFACE: 'True'</code>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
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 = ({
|
||||
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
|
||||
title="Looking to add Native Queries?"
|
||||
id="native-queries"
|
||||
description="Get production-ready today with a 30-day free trial of Hasura EE, no credit card required."
|
||||
>
|
||||
<NativeQueriesFeatureFlag>
|
||||
<div className="flex flex-col">
|
||||
<Breadcrumbs
|
||||
items={paths.map((path: string, index) => {
|
||||
return {
|
||||
title: startCase(path),
|
||||
onClick:
|
||||
index === paths.length - 1
|
||||
? undefined
|
||||
: () => {
|
||||
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-muted">{subtitle}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="">{children}</div>
|
||||
</div>
|
||||
</NativeQueriesFeatureFlag>
|
||||
</LimitedFeatureWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user