Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dynamic api key dialog for magic link and embedded catalog component #545

Merged
merged 1 commit into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 71 additions & 46 deletions apps/embedded-catalog/react/src/components/PanoraDynamicCatalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,9 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import useCreateApiKeyConnection from '@/hooks/queries/useCreateApiKeyConnection';
import { LoadingSpinner } from './ui/loading-spinner';
import { Label } from './ui/label';


export interface DynamicCardProp {
Expand All @@ -39,13 +32,17 @@ export interface DynamicCardProp {
category?: ConnectorCategory;
optionalApiUrl?: string,
}
interface IApiKeyFormData {
apikey: string,
[key : string]: string
}


const formSchema = z.object({
apiKey: z.string().min(2, {
message: "Api Key must be at least 2 characters.",
})
})
// const formSchema = z.object({
// apiKey: z.string().min(2, {
// message: "Api Key must be at least 2 characters.",
// })
// })

const DynamicCatalog = ({projectId,linkedUserId, category, optionalApiUrl} : DynamicCardProp) => {

Expand All @@ -54,7 +51,10 @@ const DynamicCatalog = ({projectId,linkedUserId, category, optionalApiUrl} : Dyn
const [selectedProvider, setSelectedProvider] = useState<{
provider: string;
category: string;
}>();
}>({
provider: '',
category: ''
});

const [loading, setLoading] = useState<boolean>(false);

Expand All @@ -73,12 +73,14 @@ const DynamicCatalog = ({projectId,linkedUserId, category, optionalApiUrl} : Dyn
const {mutate : createApiKeyConnection} = useCreateApiKeyConnection();


const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
apiKey: "",
},
})
// const form = useForm<z.infer<typeof formSchema>>({
// resolver: zodResolver(formSchema),
// defaultValues: {
// apiKey: "",
// },
// })
const {register,formState: {errors},handleSubmit,reset} = useForm<IApiKeyFormData>();


const [data, setData] = useState<Provider[]>([]);

Expand Down Expand Up @@ -173,9 +175,14 @@ const DynamicCatalog = ({projectId,linkedUserId, category, optionalApiUrl} : Dyn
});
}

const onApiKeySubmit = (values: z.infer<typeof formSchema>) => {
const onCloseApiKeyDialog = (dialogState : boolean) => {
setOpenApiKeyDialog(dialogState);
reset();
}

const onApiKeySubmit = (values: IApiKeyFormData) => {
setErrorResponse({errorPresent:false,errorMessage:''});
setOpenApiKeyDialog(false);
onCloseApiKeyDialog(false);
setLoading(true);

// Creating API Key Connection
Expand All @@ -186,9 +193,7 @@ const DynamicCatalog = ({projectId,linkedUserId, category, optionalApiUrl} : Dyn
providerName: selectedProvider?.provider!,
vertical: selectedProvider?.category!
},
data: {
apikey: values.apiKey
},
data: values,
api_url: optionalApiUrl ?? config.API_URL!
},
{
Expand Down Expand Up @@ -257,41 +262,61 @@ const DynamicCatalog = ({projectId,linkedUserId, category, optionalApiUrl} : Dyn
}

{/* Dialog for apikey input */}
<Dialog open={openApiKeyDialog} onOpenChange={setOpenApiKeyDialog}>
<Dialog open={openApiKeyDialog} onOpenChange={onCloseApiKeyDialog}>
<DialogContent>
<DialogHeader>
<DialogTitle>Enter a API key</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onApiKeySubmit)}>
{/* <Form {...form}> */}
<form onSubmit={handleSubmit(onApiKeySubmit)}>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<FormField
control={form.control}
name="apiKey"
render={({ field }) => (
<FormItem>
<FormLabel>Enter your API key for {selectedProvider?.provider}</FormLabel>
<FormControl>
<Input
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Your awesome key name" {...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Label className={errors.apikey ? 'text-destructive' : ''}>Enter your API key for {selectedProvider?.provider}</Label>
<Input
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Your api key"
{...register('apikey',{
required: 'Api Key must be at least 2 characters',
minLength: {
value:2,
message: 'Api Key must be at least 2 characters'
}

})}
/>
<div>{errors.apikey && (<p className='text-sm font-medium text-destructive'>{errors.apikey.message}</p>)}</div>


{/* </div> */}
{selectedProvider.provider!=='' && selectedProvider.category!=='' && CONNECTORS_METADATA[selectedProvider.category][selectedProvider.provider].authStrategy.properties?.map((fieldName : string) =>
(
<>
<Label className={errors[fieldName] ? 'text-destructive' : ''}>Enter your {fieldName} for {selectedProvider?.provider}</Label>
<Input
type='text'
placeholder={`Your ${fieldName}`}
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
{...register(fieldName,{
required: `${fieldName} must be at least 2 characters`,
minLength:{
value:2,
message: `${fieldName} must be at least 2 characters`,
}
})}
/>
{errors[fieldName] && (<p className='text-sm font-medium text-destructive'>{errors[fieldName]?.message}</p>)}
</>
))}
</div>
</div>
<DialogFooter>
<Button variant='outline' type="reset" size="sm" className="h-7 gap-1" onClick={() => setOpenApiKeyDialog(false)}>Cancel</Button>
<Button variant='outline' type="reset" size="sm" className="h-7 gap-1" onClick={() => onCloseApiKeyDialog(false)}>Cancel</Button>
<Button type='submit' size="sm" className="h-7 gap-1">
Continue
</Button>
</DialogFooter>
</form>
</Form>
{/* </Form> */}
</DialogContent>
</Dialog>

Expand Down
116 changes: 67 additions & 49 deletions apps/embedded-catalog/react/src/components/PanoraIntegrationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,9 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import useCreateApiKeyConnection from '@/hooks/queries/useCreateApiKeyConnection';
import { LoadingSpinner } from './ui/loading-spinner';
import { Label } from './ui/label';


export interface ProviderCardProp {
Expand All @@ -40,11 +33,16 @@ export interface ProviderCardProp {
optionalApiUrl?: string,
}

const formSchema = z.object({
apiKey: z.string().min(2, {
message: "Api Key must be at least 2 characters.",
})
})
// const formSchema = z.object({
// apiKey: z.string().min(2, {
// message: "Api Key must be at least 2 characters.",
// })
// })

interface IApiKeyFormData {
apikey: string,
[key : string]: string
}

const PanoraIntegrationCard = ({name, category, projectId, linkedUserId, optionalApiUrl}: ProviderCardProp) => {
const [loading, setLoading] = useState<boolean>(false);
Expand All @@ -61,12 +59,14 @@ const PanoraIntegrationCard = ({name, category, projectId, linkedUserId, optiona
? window.location.href
: '';

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
apiKey: "",
},
})
// const form = useForm<z.infer<typeof formSchema>>({
// resolver: zodResolver(formSchema),
// defaultValues: {
// apiKey: "",
// },
// })
const {register,formState: {errors},handleSubmit,reset} = useForm<IApiKeyFormData>();



const { open, isReady } = useOAuth({
Expand Down Expand Up @@ -119,9 +119,14 @@ const PanoraIntegrationCard = ({name, category, projectId, linkedUserId, optiona

}

const onApiKeySubmit = (values: z.infer<typeof formSchema>) => {
const onCloseApiKeyDialog = (dialogState : boolean) => {
setOpenApiKeyDialog(dialogState);
reset();
}

const onApiKeySubmit = (values: IApiKeyFormData) => {
setErrorResponse({errorPresent:false,errorMessage:''});
setOpenApiKeyDialog(false);
onCloseApiKeyDialog(false);
setLoading(true);

// Creating API Key Connection
Expand All @@ -132,9 +137,7 @@ const PanoraIntegrationCard = ({name, category, projectId, linkedUserId, optiona
providerName: name.toLowerCase(),
vertical: category.toLowerCase()
},
data: {
apikey: values.apiKey
},
data: values,
api_url: optionalApiUrl ?? config.API_URL!
},
{
Expand All @@ -150,7 +153,6 @@ const PanoraIntegrationCard = ({name, category, projectId, linkedUserId, optiona
}

const CONNECTOR = CONNECTORS_METADATA[category!.toLowerCase()][name.toLowerCase()]

const img = CONNECTOR.logoPath;


Expand Down Expand Up @@ -184,53 +186,69 @@ const PanoraIntegrationCard = ({name, category, projectId, linkedUserId, optiona
)}

</div>


{errorResponse.errorPresent ? <p className='mt-2 text-xs text-red-700'>{errorResponse.errorMessage}</p> : (<></>)}


</div>
<div>
</div>
</div>
</Card>

{/* Dialog for apikey input */}
<Dialog open={openApiKeyDialog} onOpenChange={setOpenApiKeyDialog}>
<Dialog open={openApiKeyDialog} onOpenChange={onCloseApiKeyDialog}>
<DialogContent>
<DialogHeader>
<DialogTitle>Enter a API key</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onApiKeySubmit)}>
{/* <Form {...form}> */}
<form onSubmit={handleSubmit(onApiKeySubmit)}>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<FormField
control={form.control}
name="apiKey"
render={({ field }) => (
<FormItem>
<FormLabel>Enter your API key for {name}</FormLabel>
<FormControl>
<Input
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Your awesome key name" {...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
<Label className={errors.apikey ? 'text-destructive' : ''}>Enter your API key for {name}</Label>
<Input
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Your api key"
{...register('apikey',{
required: 'Api Key must be at least 2 characters',
minLength: {
value:2,
message: 'Api Key must be at least 2 characters'
}

})}
/>
<div>{errors.apikey && (<p className='text-sm font-medium text-destructive'>{errors.apikey.message}</p>)}</div>


{/* </div> */}
{CONNECTORS_METADATA[category.toLowerCase()][name.toLowerCase()].authStrategy.properties?.map((fieldName :string) =>
(
<>
<Label className={errors[fieldName] ? 'text-destructive' : ''}>Enter your {fieldName} for {name}</Label>
<Input
type='text'
placeholder={`Your ${fieldName}`}
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
{...register(fieldName,{
required: `${fieldName} must be at least 2 characters`,
minLength:{
value:2,
message: `${fieldName} must be at least 2 characters`,
}
})}
/>
{errors[fieldName] && (<p className='text-sm font-medium text-destructive'>{errors[fieldName]?.message}</p>)}
</>
))}
</div>
</div>
<DialogFooter>
<Button variant='outline' type="reset" size="sm" className="h-7 gap-1" onClick={() => setOpenApiKeyDialog(false)}>Cancel</Button>
<Button variant='outline' type="reset" size="sm" className="h-7 gap-1" onClick={() => onCloseApiKeyDialog(false)}>Cancel</Button>
<Button type='submit' size="sm" className="h-7 gap-1">
Continue
</Button>
</DialogFooter>
</form>
</Form>
{/* </Form> */}
</DialogContent>
</Dialog>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface IApiKeyConnectionDto {
},
data: {
apikey: string,
username?: string
[key : string]: string
},
api_url: string
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface IApiKeyConnectionDto {
},
data: {
apikey: string,
username?: string
[key : string]: string
}
}

Expand Down
Loading
Loading