mirror of https://github.com/portainer/portainer
feat(ask-ai): integrate kapa-ai page [BE-11409] (#214)
parent
783ab253af
commit
441afead10
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>({
|
||||
|
|
|
@ -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>({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
import { useDocsUrl } from '../PageHeader/ContextHelp/ContextHelp';
|
||||
import { useDocsUrl } from '@@/PageHeader/ContextHelp';
|
||||
|
||||
type HelpLinkProps = {
|
||||
docLink: string;
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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"
|
||||
>
|
|
@ -1,5 +0,0 @@
|
|||
.menu-icon {
|
||||
background: var(--user-menu-icon-color);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { ContextHelp } from './ContextHelp';
|
|
@ -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 {
|
||||
|
|
|
@ -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 />}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: '',
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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>
|
||||
.
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -13,6 +13,7 @@ export interface VersionResponse {
|
|||
// The latest version available
|
||||
LatestVersion: string;
|
||||
ServerVersion: string;
|
||||
VersionSupport: 'STS' | 'LTS';
|
||||
DatabaseVersion: string;
|
||||
Build: {
|
||||
BuildNumber: string;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
17
yarn.lock
17
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue