Skip to content

Commit

Permalink
Merge pull request #23 from yansavitski/ai-playground-view-code
Browse files Browse the repository at this point in the history
Ai playground view code
  • Loading branch information
joemcelroy authored Feb 29, 2024
2 parents f55031b + 623ff05 commit 88c2b02
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const SourcesPanelForStartChat: React.FC = () => {
const defaultFields = getDefaultQueryFields(fields);
elasticsearchQueryOnChange(createQuery(defaultFields, fields));
}
}, [selectedIndices, fields, elasticsearchQueryOnChange]);
}, [fields, elasticsearchQueryOnChange]);

return (
<StartChatPanel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
EuiButton,
EuiButtonEmpty,
EuiFieldText,
EuiFlexGroup,
EuiForm,
EuiFormControlLayout,
EuiFormRow,
EuiText,
} from '@elastic/eui';
import React, { Controller, useForm } from 'react-hook-form';
import { i18n } from '@kbn/i18n';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useCreateApiKeyQuery } from '../../hooks/useCreateApiKeyQuery';
import { AIPlaygroundPluginStartDeps } from '@kbn/ai-playground/types';
import { FormattedMessage } from '@kbn/i18n-react';

enum ApiKeyFormFields {
Name = 'name',
ExpireInDays = 'expiresInDays',
}

interface ApiKeyForm {
[ApiKeyFormFields.Name]: string;
[ApiKeyFormFields.ExpireInDays]: number;
}

export const CreateApiKeyForm = () => {
const { http } = useKibana<AIPlaygroundPluginStartDeps>().services;
const managementApiKeysLinks = http.basePath.prepend('/app/management/security/api_keys');
const {
control,
getValues,
reset,
formState: { isDirty, isValid },
handleSubmit,
} = useForm<ApiKeyForm>();
const { action, isLoading, isSuccess, isError } = useCreateApiKeyQuery();
const onSubmit = async (data: ApiKeyForm) => {
await action(data);

reset(getValues());
};

return (
<EuiForm>
<EuiFormRow
fullWidth
label={i18n.translate('aiPlayground.viewCode.apiForm.name.label', {
defaultMessage: 'Name',
})}
>
<Controller
name={ApiKeyFormFields.Name}
control={control}
defaultValue=""
rules={{ required: true }}
render={({ field }) => (
<EuiFieldText
fullWidth
placeholder={i18n.translate('aiPlayground.viewCode.apiForm.name.placeholder', {
defaultMessage: 'Enter a name for your API key',
})}
value={field.value}
onChange={field.onChange}
/>
)}
/>
</EuiFormRow>

<EuiFormRow fullWidth label="Lifetime">
<Controller
name={ApiKeyFormFields.ExpireInDays}
control={control}
rules={{ min: 0, required: true }}
render={({ field }) => (
<EuiFormControlLayout
fullWidth
append={
<EuiText size="xs">
<strong>
<FormattedMessage
id="aiPlayground.viewCode.apiForm.expire.days"
defaultMessage="Days"
/>
</strong>
</EuiText>
}
>
<EuiFieldText
fullWidth
type="number"
placeholder={i18n.translate('aiPlayground.viewCode.apiForm.expire.placeholder', {
defaultMessage: 'Set expiry in days',
})}
value={field.value || ''}
onChange={field.onChange}
/>
</EuiFormControlLayout>
)}
/>
</EuiFormRow>

<EuiFormRow fullWidth>
<EuiFlexGroup gutterSize="m">
{isSuccess && !isDirty ? (
<EuiButton color="success" iconType="check">
<FormattedMessage
id="aiPlayground.viewCode.apiForm.createdButton"
defaultMessage="Created"
/>
</EuiButton>
) : (
<EuiButton
isDisabled={!isValid || isLoading}
isLoading={isLoading}
onClick={handleSubmit(onSubmit)}
color={isError ? 'danger' : 'primary'}
>
<FormattedMessage
id="aiPlayground.viewCode.apiForm.createButton"
defaultMessage="Create API key"
/>
</EuiButton>
)}

<EuiButtonEmpty
iconSide="left"
iconType="popout"
href={managementApiKeysLinks}
target="_blank"
>
<FormattedMessage
id="aiPlayground.viewCode.apiForm.viewKeysButton"
defaultMessage="View all API keys"
/>
</EuiButtonEmpty>
</EuiFlexGroup>
</EuiFormRow>
</EuiForm>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
EuiFormLabel,
EuiCodeBlock,
EuiFlyout,
EuiFlyoutBody,
EuiFlyoutHeader,
EuiSpacer,
EuiSteps,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React, { useMemo } from 'react';
import { CreateApiKeyForm } from './create_api_key_form';

interface VideCodeFlyoutProps {
onClose: () => void;
}

export const VideCodeFlyout: React.FC<VideCodeFlyoutProps> = ({ onClose }) => {
const steps = useMemo(
() => [
{
title: i18n.translate('aiPlayground.viewCode.flyout.step.apiKeyTitle', {
defaultMessage: 'Generate and copy an API key',
}),
children: (
<>
<EuiText>
<p>
<FormattedMessage
id="aiPlayground.viewCode.flyout.step.apiKeyDescription"
defaultMessage="You will only be able to see this API key once after creation."
/>
</p>
</EuiText>
<EuiSpacer />
<CreateApiKeyForm />
</>
),
},
{
title: i18n.translate('aiPlayground.viewCode.flyout.step.createApplication', {
defaultMessage: 'Create application',
}),
children: (
<>
<EuiFormLabel>
<FormattedMessage
id="aiPlayground.viewCode.flyout.step.installLabel"
defaultMessage="Use this code in your CLI"
/>
</EuiFormLabel>
<EuiSpacer size="s" />
<EuiCodeBlock language="bash" isCopyable>
npm install
</EuiCodeBlock>
</>
),
},
],
[]
);

return (
<EuiFlyout ownFocus onClose={onClose}>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<h2>
<FormattedMessage id="aiPlayground.viewCode.flyout.title" defaultMessage="Export" />
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText color="subdued">
<p>
<FormattedMessage
id="aiPlayground.viewCode.flyout.subtitle"
defaultMessage="Use this custom built playground experience in your application"
/>
</p>
</EuiText>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiSteps steps={steps} headingElement="h2" />
</EuiFlyoutBody>
</EuiFlyout>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,88 +7,30 @@
*/

import React, { useState } from 'react';
import {
EuiFlyout,
EuiFlyoutHeader,
EuiFlyoutBody,
EuiTitle,
EuiCodeBlock,
EuiButton,
EuiText,
EuiSpacer,
EuiSteps,
EuiCode,
} from '@elastic/eui';
import { EuiButton } from '@elastic/eui';
import { useFormContext } from 'react-hook-form';
import { ChatForm } from '../../types';
import { ChatForm, ChatFormFields } from '../../types';
import { VideCodeFlyout } from './vide_code_flyout';
import { FormattedMessage } from '@kbn/i18n-react';

interface ViewCodeActionProps {}

export const ViewCodeAction: React.FC<ViewCodeActionProps> = () => {
const { getValues } = useFormContext<ChatForm>();
const { watch } = useFormContext<ChatForm>();
const [showFlyout, setShowFlyout] = useState(false);
const selectedIndices: string[] = getValues('indices');

let flyout;

const steps = [
{
title: 'Generate and copy an API key',
children: (
<>
<EuiText>
<p>Run this code snippet to install things.</p>
</EuiText>
<EuiSpacer />
<EuiCodeBlock language="bash">npm install</EuiCodeBlock>
</>
),
},
{
title: 'Create application',
children: (
<EuiText>
<p>
Now that you&apos;ve completed step 2, go find the <EuiCode>thing</EuiCode>.
</p>
<p>
Go to <strong>Overview &gt;&gt; Endpoints</strong> note <strong>Elasticsearch</strong>{' '}
as <EuiCode>&lt;thing&gt;</EuiCode>.
</p>
</EuiText>
),
},
];

if (showFlyout) {
flyout = (
<EuiFlyout ownFocus onClose={() => setShowFlyout(false)}>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<h2>Download Code</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText color="subdued">
<p>Download the code to use in your application.</p>
</EuiText>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiSteps steps={steps} headingElement="h2" />
</EuiFlyoutBody>
</EuiFlyout>
);
}
const selectedIndices = watch(ChatFormFields.indices);

return (
<>
{flyout}
{showFlyout && <VideCodeFlyout onClose={() => setShowFlyout(false)} />}
<EuiButton
iconType="editorCodeBlock"
color="primary"
fill
onClick={() => setShowFlyout(true)}
disabled={!selectedIndices || selectedIndices?.length === 0}
>
Download Code
<FormattedMessage id="aiPlayground.viewCode.actionButtonLabel" defaultMessage="View code" />
</EuiButton>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import { createQuery, getDefaultQueryFields } from '../../lib/create_query';
interface ViewQueryActionProps {}

export const ViewQueryAction: React.FC<ViewQueryActionProps> = () => {
const { getValues } = useFormContext<ChatForm>();
const { watch } = useFormContext<ChatForm>();
const [showFlyout, setShowFlyout] = useState(false);
const selectedIndices: string[] = getValues('indices');
const selectedIndices: string[] = watch('indices');
const { fields } = useIndicesFields(selectedIndices || []);
const defaultFields = useMemo(() => getDefaultQueryFields(fields), [fields]);
const [queryFields, setQueryFields] = useState(defaultFields);
Expand Down
Loading

0 comments on commit 88c2b02

Please sign in to comment.