feat(ask-ai): integrate kapa-ai page [BE-11409] (#214)

release/2.25
Ali 2024-12-06 18:41:32 +13:00 committed by GitHub
parent 783ab253af
commit 441afead10
24 changed files with 102 additions and 50 deletions

View File

@ -22,6 +22,7 @@ type versionResponse struct {
LatestVersion string `json:"LatestVersion" example:"2.0.0"`
ServerVersion string
VersionSupport string `json:"VersionSupport" example:"STS/LTS"`
ServerEdition string `json:"ServerEdition" example:"CE/EE"`
DatabaseVersion string
Build build.BuildInfo
@ -47,6 +48,7 @@ func (handler *Handler) version(w http.ResponseWriter, r *http.Request) *httperr
result := &versionResponse{
ServerVersion: portainer.APIVersion,
VersionSupport: portainer.APIVersionSupport,
DatabaseVersion: portainer.APIVersion,
ServerEdition: portainer.Edition.GetEditionLabel(),
Build: build.GetBuildInfo(),

View File

@ -1631,6 +1631,8 @@ type (
const (
// APIVersion is the version number of the Portainer API
APIVersion = "2.24.0"
// Support annotation for the API version ("STS" for Short-Term Support or "LTS" for Long-Term Support)
APIVersionSupport = "STS"
// Edition is what this edition of Portainer is called
Edition = PortainerCE
// ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { PropsWithChildren } from 'react';
import type { Icon } from 'lucide-react';
import type { LucideIcon } from 'lucide-react';
import { TooltipWithChildren } from '@@/Tip/TooltipWithChildren';
@ -16,7 +16,7 @@ interface Props<T extends Value> {
tooltip?: string;
className?: string;
type?: 'radio' | 'checkbox';
checkIcon: Icon;
checkIcon: LucideIcon;
}
export function BoxOption<T extends Value>({

View File

@ -1,5 +1,5 @@
import clsx from 'clsx';
import { Icon as ReactFeatherComponentType, Check } from 'lucide-react';
import { type LucideIcon, Check } from 'lucide-react';
import { Fragment } from 'react';
import { Icon } from '@/react/components/Icon';
@ -22,7 +22,7 @@ type Props<T extends Value> = {
isSelected(value: T): boolean;
type?: 'radio' | 'checkbox';
slim?: boolean;
checkIcon?: ReactFeatherComponentType;
checkIcon?: LucideIcon;
};
export function BoxSelectorItem<T extends Value>({

View File

@ -1,6 +1,6 @@
import { ReactNode } from 'react';
import { useDocsUrl } from '../PageHeader/ContextHelp/ContextHelp';
import { useDocsUrl } from '@@/PageHeader/ContextHelp';
type HelpLinkProps = {
docLink: string;

View File

@ -0,0 +1,29 @@
import { BotMessageSquare } from 'lucide-react';
import clsx from 'clsx';
import headerStyles from './HeaderTitle.module.css';
const docsUrl = 'https://www.portainer.io/ask-the-ai';
export function AskAILink() {
return (
<div className={headerStyles.menuButton}>
<a
href={docsUrl}
target="_blank"
color="none"
className={clsx(
headerStyles.menuIcon,
'icon-badge mr-1 !p-2 text-lg cursor-pointer',
'text-gray-8',
'th-dark:text-gray-warm-7'
)}
title="Ask AI"
rel="noreferrer"
data-cy="ask-ai-button"
>
<BotMessageSquare className="lucide" />
</a>
</div>
);
}

View File

@ -4,8 +4,7 @@ import { useCurrentStateAndParams } from '@uirouter/react';
import { useSystemVersion } from '@/react/portainer/system/useSystemVersion';
import headerStyles from '../HeaderTitle.module.css';
import './ContextHelp.css';
import headerStyles from './HeaderTitle.module.css';
export function ContextHelp() {
const docsUrl = useDocsUrl();
@ -18,12 +17,11 @@ export function ContextHelp() {
color="none"
className={clsx(
headerStyles.menuIcon,
'menu-icon',
'icon-badge mr-1 !p-2 text-lg',
'icon-badge mr-1 !p-2 text-lg cursor-pointer',
'text-gray-8',
'th-dark:text-gray-warm-7'
)}
title="Help"
title="Documentation"
rel="noreferrer"
data-cy="context-help-button"
>

View File

@ -1,5 +0,0 @@
.menu-icon {
background: var(--user-menu-icon-color);
cursor: pointer;
flex-shrink: 0;
}

View File

@ -1 +0,0 @@
export { ContextHelp } from './ContextHelp';

View File

@ -10,6 +10,12 @@
.menu-icon {
background: var(--user-menu-icon-color);
position: relative;
flex-shrink: 0;
}
.menu-icon:hover {
/* keep the links and button icon colors consistent on hover */
@apply text-gray-8 th-dark:text-gray-warm-7;
}
.menu-list {

View File

@ -5,6 +5,7 @@ import { ContextHelp } from '@@/PageHeader/ContextHelp';
import { useHeaderContext } from './HeaderContainer';
import { NotificationsMenu } from './NotificationsMenu';
import { UserMenu } from './UserMenu';
import { AskAILink } from './AskAILink';
interface Props {
title: string;
@ -25,6 +26,7 @@ export function HeaderTitle({ title, children }: PropsWithChildren<Props>) {
{children && <>{children}</>}
</div>
<div className="flex items-end">
<AskAILink />
<NotificationsMenu />
<ContextHelp />
{!window.ddExtension && <UserMenu />}

View File

@ -1,7 +1,7 @@
import { ComponentProps } from 'react';
import { Alert } from '@@/Alert';
import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp';
import { useDocsUrl } from '@@/PageHeader/ContextHelp';
import { EnvironmentVariablesFieldset } from './EnvironmentVariablesFieldset';
import { EnvironmentVariablesPanel } from './EnvironmentVariablesPanel';

View File

@ -2,7 +2,7 @@ import _ from 'lodash';
import {
AlertTriangle,
CheckCircle,
type Icon as IconType,
type LucideIcon,
Loader2,
XCircle,
MinusCircle,
@ -51,7 +51,7 @@ function getStatus(
hasOldVersion: boolean
): {
label: string;
icon?: IconType;
icon?: LucideIcon;
spin?: boolean;
mode?: IconMode;
tooltip?: string;

View File

@ -7,7 +7,7 @@ import { FormSectionTitle } from '@@/form-components/FormSectionTitle';
import { Input } from '@@/form-components/Input';
import { Button } from '@@/buttons';
import { TextTip } from '@@/Tip/TextTip';
import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp';
import { useDocsUrl } from '@@/PageHeader/ContextHelp';
const initialValues = {
kubeConfig: '',

View File

@ -9,7 +9,7 @@ import { SwitchField } from '@@/form-components/SwitchField';
import { TextTip } from '@@/Tip/TextTip';
import { FormControl } from '@@/form-components/FormControl';
import { Input, Select } from '@@/form-components/Input';
import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp';
import { useDocsUrl } from '@@/PageHeader/ContextHelp';
import { RelativePathModel, getPerDevConfigsFilterType } from './types';

View File

@ -1,8 +1,10 @@
import { useField } from 'formik';
import { SwitchField } from '@@/form-components/SwitchField';
import { useDocsUrl } from '@@/PageHeader/ContextHelp';
export function EnableTelemetryField() {
const privacyPolicy = useDocsUrl('/in-app-analytics-and-privacy-policy');
const [{ value }, , { setValue }] = useField<boolean>('enableTelemetry');
return (
@ -20,11 +22,7 @@ export function EnableTelemetryField() {
<div className="col-sm-12 text-muted small mt-2">
You can find more information about this in our{' '}
<a
href="https://www.portainer.io/documentation/in-app-analytics-and-privacy-policy/"
target="_blank"
rel="noreferrer"
>
<a href={privacyPolicy} target="_blank" rel="noreferrer">
privacy policy
</a>
.

View File

@ -3,7 +3,7 @@ import { useField, Field } from 'formik';
import { FormControl } from '@@/form-components/FormControl';
import { FormSection } from '@@/form-components/FormSection';
import { Input } from '@@/form-components/Input';
import { useDocsUrl } from '@@/PageHeader/ContextHelp/ContextHelp';
import { useDocsUrl } from '@@/PageHeader/ContextHelp';
// this value is taken from https://github.com/portainer/portainer/blob/develop/api/portainer.go#L1628
const DEFAULT_URL =

View File

@ -13,6 +13,7 @@ export interface VersionResponse {
// The latest version available
LatestVersion: string;
ServerVersion: string;
VersionSupport: 'STS' | 'LTS';
DatabaseVersion: string;
Build: {
BuildNumber: string;

View File

@ -23,22 +23,25 @@ import styles from './Footer.module.css';
export function BuildInfoModalButton() {
const [isBuildInfoVisible, setIsBuildInfoVisible] = useState(false);
const statusQuery = useSystemStatus();
const versionQuery = useSystemVersion();
if (!statusQuery.data) {
if (!statusQuery.data || !versionQuery.data) {
return null;
}
const { Version } = statusQuery.data;
const { VersionSupport } = versionQuery.data;
return (
<>
<button
type="button"
data-cy="portainerSidebar-versionNumber"
className="btn-none"
className="btn-none hover:underline"
onClick={() => setIsBuildInfoVisible(true)}
title="About Portainer"
>
{Version}
{`${Version} (${VersionSupport})`}
</button>
{isBuildInfoVisible && (
<BuildInfoModal closeModal={() => setIsBuildInfoVisible(false)} />
@ -57,8 +60,14 @@ function BuildInfoModal({ closeModal }: { closeModal: () => void }) {
}
const { Edition } = statusQuery.data;
const { ServerVersion, DatabaseVersion, Build, Dependencies, Runtime } =
versionQuery.data;
const {
ServerVersion,
DatabaseVersion,
Build,
Dependencies,
Runtime,
VersionSupport,
} = versionQuery.data;
return (
<Modal onDismiss={closeModal} aria-label="build-info-modal">
@ -69,13 +78,13 @@ function BuildInfoModal({ closeModal }: { closeModal: () => void }) {
<tbody>
<tr>
<td>
<span className="inline-flex items-center">
<span className="inline-flex items-center flex-wrap">
<Server size="13" className="space-right" />
Server Version: {ServerVersion}
Server Version: {ServerVersion} ({VersionSupport})
</span>
</td>
<td>
<span className="inline-flex items-center">
<span className="inline-flex items-center flex-wrap">
<Database size="13" className="space-right" />
Database Version: {DatabaseVersion}
</span>
@ -83,13 +92,13 @@ function BuildInfoModal({ closeModal }: { closeModal: () => void }) {
</tr>
<tr>
<td>
<span className="inline-flex items-center">
<span className="inline-flex items-center flex-wrap">
<Hash size="13" className="space-right" />
CI Build Number: {Build.BuildNumber}
</span>
</td>
<td>
<span>
<span className="inline-flex items-center flex-wrap">
<Tag size="13" className="space-right" />
Image Tag: {Build.ImageTag}
</span>
@ -97,8 +106,10 @@ function BuildInfoModal({ closeModal }: { closeModal: () => void }) {
</tr>
<tr>
<td>
<GitCommit size="13" className="space-right" />
Git Commit: {Build.GitCommit}
<span className="inline-flex items-center flex-wrap">
<GitCommit size="13" className="space-right" />
Git Commit: {Build.GitCommit}
</span>
</td>
<td />
</tr>

View File

@ -205,7 +205,7 @@ export function SettingsSidebar({ isPureAdmin, isAdmin, isTeamLeader }: Props) {
data-cy="portainerSidebar-edgeCompute"
/>
<SidebarItem.Wrapper label="Help / About">
<SidebarItem.Wrapper label="Get Help">
<a
href={
process.env.PORTAINER_EDITION === 'CE'
@ -216,7 +216,7 @@ export function SettingsSidebar({ isPureAdmin, isAdmin, isTeamLeader }: Props) {
rel="noreferrer"
className="hover:!underline focus:no-underline text-sm flex h-8 w-full items-center rounded px-3 transition-colors duration-200 hover:bg-blue-5/20 be:hover:bg-gray-5/20 th-dark:hover:bg-gray-true-5/20"
>
Help / About
Get Help
</a>
</SidebarItem.Wrapper>
</SidebarParent>

View File

@ -1,5 +1,5 @@
import { Meta, Story } from '@storybook/react';
import { Clock, Icon } from 'lucide-react';
import { Clock, type LucideIcon } from 'lucide-react';
import { SidebarItem } from '.';
@ -10,7 +10,7 @@ const meta: Meta = {
export default meta;
interface StoryProps {
icon?: Icon;
icon?: LucideIcon;
label: string;
}

View File

@ -1,4 +1,4 @@
import { Icon as IconTest } from 'lucide-react';
import { type LucideIcon } from 'lucide-react';
import clsx from 'clsx';
import { MouseEventHandler, PropsWithChildren } from 'react';
@ -13,7 +13,7 @@ import { SidebarTooltip } from './SidebarTooltip';
import { useSidebarSrefActive } from './useSidebarSrefActive';
interface Props extends AutomationTestingProps {
icon?: IconTest;
icon?: LucideIcon;
to: string;
params?: object;
label: string;

View File

@ -102,7 +102,7 @@
"js-yaml": "^3.14.0",
"jsdom": "^24.0.0",
"lodash": "^4.17.21",
"lucide-react": "^0.258.0",
"lucide-react": "^0.468.0",
"moment": "^2.29.1",
"moment-timezone": "^0.5.40",
"mustache": "^4.2.0",

View File

@ -12621,10 +12621,10 @@ lru-cache@^6.0.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484"
integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==
lucide-react@^0.258.0:
version "0.258.0"
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.258.0.tgz#dbdabafad2835a0a452e8f677531a877cdd93ae8"
integrity sha512-3evnpKadBrjLr2HHJ66eDZ1y0vPS6pm8NiNDaLqhddUUyJGnA+lfDPZfbVkuAFq7Xaa1TEy7Sg17sM7mHpMKrA==
lucide-react@^0.468.0:
version "0.468.0"
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.468.0.tgz#830c1bfd905575ddd23b832baa420c87db166910"
integrity sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==
lz-string@^1.4.4:
version "1.4.4"
@ -17596,6 +17596,15 @@ wrap-ansi@^7.0.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"