mirror of https://github.com/buster-so/buster.git
add success messages
This commit is contained in:
parent
0d2132f021
commit
f06e675e21
|
@ -32,7 +32,7 @@ const DataSourceFormHeader: React.FC<{ dataSource: DataSource }> = ({ dataSource
|
||||||
<div className="flex justify-between space-x-2">
|
<div className="flex justify-between space-x-2">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<div className="text-icon-color text-4xl">
|
<div className="text-icon-color text-4xl">
|
||||||
<AppDataSourceIcon size={55} type={dataSource.type} />
|
<AppDataSourceIcon size={32} type={dataSource.type} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col space-y-1">
|
<div className="flex flex-col space-y-1">
|
||||||
|
|
|
@ -4,16 +4,51 @@ import React from 'react';
|
||||||
import { WhiteListBlock } from './WhiteListBlock';
|
import { WhiteListBlock } from './WhiteListBlock';
|
||||||
import { FormApi } from '@tanstack/react-form';
|
import { FormApi } from '@tanstack/react-form';
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
|
import { SubscribeButton } from '@/components/ui/form/FormBase';
|
||||||
|
|
||||||
|
// Common field interface properties
|
||||||
|
interface FieldComponentProps {
|
||||||
|
label: string | null;
|
||||||
|
labelClassName?: string;
|
||||||
|
className?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a more specific but limited interface for our form
|
||||||
|
// This avoids the deep type recursion while maintaining safety
|
||||||
|
export interface BusterFormApi {
|
||||||
|
handleSubmit: () => void;
|
||||||
|
reset: () => void;
|
||||||
|
state: {
|
||||||
|
canSubmit: boolean;
|
||||||
|
isSubmitting: boolean;
|
||||||
|
isDirty: boolean;
|
||||||
|
};
|
||||||
|
AppForm: React.ComponentType<{ children?: React.ReactNode }>;
|
||||||
|
AppField: any; // Using any for AppField to avoid type complexity
|
||||||
|
SubscribeButton: React.ComponentType<{
|
||||||
|
submitLabel: string;
|
||||||
|
disableIfNotChanged?: boolean;
|
||||||
|
useResetButton?: boolean;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field interface representing what's available inside the field prop
|
||||||
|
interface FieldInterface {
|
||||||
|
TextField: React.FC<FieldComponentProps>;
|
||||||
|
NumberField: React.FC<FieldComponentProps>;
|
||||||
|
PasswordField: React.FC<FieldComponentProps>;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a typed approach for the form
|
||||||
export interface FormWrapperProps {
|
export interface FormWrapperProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
flow: 'create' | 'update';
|
flow: 'create' | 'update';
|
||||||
form: FormApi<any, any, any, any, any, any, any, any, any, any>;
|
form: BusterFormApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FormWrapper({ form, children, flow }: FormWrapperProps) {
|
export function FormWrapper({ form, children, flow }: FormWrapperProps) {
|
||||||
console.log(form);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="[&_.label-wrapper]:border-b-border flex flex-col space-y-4 [&_.label-wrapper]:border-b [&_.label-wrapper]:pb-4"
|
className="[&_.label-wrapper]:border-b-border flex flex-col space-y-4 [&_.label-wrapper]:border-b [&_.label-wrapper]:pb-4"
|
||||||
|
@ -25,18 +60,12 @@ export function FormWrapper({ form, children, flow }: FormWrapperProps) {
|
||||||
|
|
||||||
<WhiteListBlock />
|
<WhiteListBlock />
|
||||||
|
|
||||||
<div className="flex w-full justify-end space-x-2">
|
<form.AppForm>
|
||||||
<Button variant="ghost" type="reset" onClick={() => form.reset()}>
|
<form.SubscribeButton
|
||||||
Reset
|
submitLabel={flow === 'create' ? 'Create' : 'Update'}
|
||||||
</Button>
|
disableIfNotChanged={flow === 'update'}
|
||||||
<Button
|
/>
|
||||||
variant="black"
|
</form.AppForm>
|
||||||
type="submit"
|
|
||||||
disabled={!form.state.canSubmit}
|
|
||||||
loading={form.state.isSubmitting}>
|
|
||||||
{flow === 'create' ? 'Create' : 'Update'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,32 +8,50 @@ import {
|
||||||
} from '@/api/buster_rest/data_source';
|
} from '@/api/buster_rest/data_source';
|
||||||
import { useAppForm } from '@/components/ui/form/useFormBaseHooks';
|
import { useAppForm } from '@/components/ui/form/useFormBaseHooks';
|
||||||
import { MultipleInlineFields } from '@/components/ui/form/FormBase';
|
import { MultipleInlineFields } from '@/components/ui/form/FormBase';
|
||||||
|
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { BusterRoutes, createBusterRoute } from '@/routes/busterRoutes';
|
||||||
|
import { useConfetti } from '@/hooks/useConfetti';
|
||||||
|
|
||||||
export const PostgresForm: React.FC<{
|
export const PostgresForm: React.FC<{
|
||||||
dataSource?: DataSource;
|
dataSource?: DataSource;
|
||||||
}> = ({ dataSource }) => {
|
}> = ({ dataSource }) => {
|
||||||
|
const { fireConfetti } = useConfetti();
|
||||||
|
const { openSuccessMessage, openConfirmModal } = useBusterNotifications();
|
||||||
|
const router = useRouter();
|
||||||
const { mutateAsync: createDataSource } = useCreatePostgresDataSource();
|
const { mutateAsync: createDataSource } = useCreatePostgresDataSource();
|
||||||
const { mutateAsync: updateDataSource } = useUpdatePostgresDataSource();
|
const { mutateAsync: updateDataSource } = useUpdatePostgresDataSource();
|
||||||
const credentials = dataSource?.credentials as PostgresCredentials;
|
const credentials = dataSource?.credentials as PostgresCredentials | undefined;
|
||||||
|
|
||||||
const flow = dataSource?.id ? 'update' : 'create';
|
const flow = dataSource?.id ? 'update' : 'create';
|
||||||
|
|
||||||
const form = useAppForm({
|
const form = useAppForm({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
host: credentials.host || '',
|
host: credentials?.host || '',
|
||||||
port: credentials.port || 5432,
|
port: credentials?.port || 5432,
|
||||||
username: credentials.username || '',
|
username: credentials?.username || '',
|
||||||
password: credentials.password || '',
|
password: credentials?.password || '',
|
||||||
default_database: credentials.default_database || '',
|
default_database: credentials?.default_database || '',
|
||||||
default_schema: credentials.default_schema || '',
|
default_schema: credentials?.default_schema || '',
|
||||||
type: 'postgres' as const,
|
type: 'postgres' as const,
|
||||||
name: dataSource?.name || credentials.name || ''
|
name: dataSource?.name || credentials?.name || ''
|
||||||
} satisfies Parameters<typeof createPostgresDataSource>[0],
|
} satisfies Parameters<typeof createPostgresDataSource>[0],
|
||||||
onSubmit: async ({ value }) => {
|
onSubmit: async ({ value }) => {
|
||||||
if (flow === 'update' && dataSource?.id) {
|
if (flow === 'update' && dataSource?.id) {
|
||||||
await updateDataSource({ id: dataSource.id, ...value });
|
await updateDataSource({ id: dataSource.id, ...value });
|
||||||
|
openSuccessMessage('Datasource updated');
|
||||||
} else {
|
} else {
|
||||||
await createDataSource(value);
|
await createDataSource(value);
|
||||||
|
fireConfetti(9999);
|
||||||
|
openConfirmModal({
|
||||||
|
title: 'Datasource created',
|
||||||
|
description: 'Datasource created successfully',
|
||||||
|
content:
|
||||||
|
'Hooray! Your datasource has been created. You can now use it in your projects. You will need to create datasets to use with it.',
|
||||||
|
onOk: () => {
|
||||||
|
router.push(createBusterRoute({ route: BusterRoutes.APP_DATASETS }));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -41,7 +59,7 @@ export const PostgresForm: React.FC<{
|
||||||
const labelClassName = 'min-w-[175px]';
|
const labelClassName = 'min-w-[175px]';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormWrapper form={form} flow={dataSource?.id ? 'update' : 'create'}>
|
<FormWrapper form={form} flow={flow}>
|
||||||
<form.AppField
|
<form.AppField
|
||||||
name="name"
|
name="name"
|
||||||
children={(field) => (
|
children={(field) => (
|
||||||
|
|
|
@ -35,7 +35,7 @@ const workspaceItems: ISidebarGroup = {
|
||||||
id: createBusterRoute({ route: BusterRoutes.SETTINGS_API_KEYS })
|
id: createBusterRoute({ route: BusterRoutes.SETTINGS_API_KEYS })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Datasources',
|
label: 'Data Sources',
|
||||||
route: createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES }),
|
route: createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES }),
|
||||||
id: createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES })
|
id: createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES })
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,15 +154,17 @@ export function MultipleInlineFields({
|
||||||
|
|
||||||
export function SubscribeButton({
|
export function SubscribeButton({
|
||||||
submitLabel,
|
submitLabel,
|
||||||
useResetButton = true
|
useResetButton = true,
|
||||||
|
disableIfNotChanged = false
|
||||||
}: {
|
}: {
|
||||||
submitLabel: string;
|
submitLabel: string;
|
||||||
useResetButton?: boolean;
|
useResetButton?: boolean;
|
||||||
|
disableIfNotChanged?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form.Subscribe selector={(state) => [state.canSubmit, state.isSubmitting]}>
|
<form.Subscribe selector={(state) => [state.canSubmit, state.isSubmitting, state.isDirty]}>
|
||||||
{([canSubmit, isSubmitting]) => (
|
{([canSubmit, isSubmitting]) => (
|
||||||
<div className="flex w-full justify-end space-x-2">
|
<div className="flex w-full justify-end space-x-2">
|
||||||
{useResetButton && (
|
{useResetButton && (
|
||||||
|
@ -170,7 +172,11 @@ export function SubscribeButton({
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button variant="black" type="submit" disabled={!canSubmit} loading={isSubmitting}>
|
<Button
|
||||||
|
variant="black"
|
||||||
|
type="submit"
|
||||||
|
disabled={!canSubmit || (disableIfNotChanged && !form.state.isDirty)}
|
||||||
|
loading={isSubmitting}>
|
||||||
{submitLabel}
|
{submitLabel}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createFormHookContexts, createFormHook } from '@tanstack/react-form';
|
import { createFormHook } from '@tanstack/react-form';
|
||||||
import {
|
import {
|
||||||
NumberField,
|
NumberField,
|
||||||
PasswordField,
|
PasswordField,
|
||||||
|
|
Loading…
Reference in New Issue