1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-25 00:09:40 +02:00

feat(system/upgrade): add get license dialog [EE-4743] (#8249)

This commit is contained in:
Chaim Lev-Ari 2023-01-19 15:31:49 +02:00 committed by GitHub
parent 5942f4ff58
commit 406ff8812c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 369 additions and 25 deletions

View file

@ -0,0 +1,43 @@
import { Meta, Story } from '@storybook/react';
import { Alert } from './Alert';
export default {
component: Alert,
title: 'Components/Alert',
} as Meta;
interface Args {
color: 'success' | 'error' | 'info';
title: string;
text: string;
}
function Template({ text, color, title }: Args) {
return (
<Alert color={color} title={title}>
{text}
</Alert>
);
}
export const Success: Story<Args> = Template.bind({});
Success.args = {
color: 'success',
title: 'Success',
text: 'This is a success alert. Very long text, Very long text,Very long text ,Very long text ,Very long text, Very long text',
};
export const Error: Story<Args> = Template.bind({});
Error.args = {
color: 'error',
title: 'Error',
text: 'This is an error alert',
};
export const Info: Story<Args> = Template.bind({});
Info.args = {
color: 'info',
title: 'Info',
text: 'This is an info alert',
};

View file

@ -0,0 +1,83 @@
import clsx from 'clsx';
import { AlertCircle, CheckCircle, XCircle } from 'lucide-react';
import { PropsWithChildren, ReactNode } from 'react';
import { Icon } from '@@/Icon';
type AlertType = 'success' | 'error' | 'info';
const alertSettings: Record<
AlertType,
{ container: string; header: string; body: string; icon: ReactNode }
> = {
success: {
container:
'border-green-4 bg-green-2 th-dark:bg-green-3 th-dark:border-green-5',
header: 'text-green-8',
body: 'text-green-7',
icon: CheckCircle,
},
error: {
container:
'border-error-4 bg-error-2 th-dark:bg-error-3 th-dark:border-error-5',
header: 'text-error-8',
body: 'text-error-7',
icon: XCircle,
},
info: {
container:
'border-blue-4 bg-blue-2 th-dark:bg-blue-3 th-dark:border-blue-5',
header: 'text-blue-8',
body: 'text-blue-7',
icon: AlertCircle,
},
};
export function Alert({
color,
title,
children,
}: PropsWithChildren<{ color: AlertType; title: string }>) {
const { container, header, body, icon } = alertSettings[color];
return (
<AlertContainer className={container}>
<AlertHeader className={header}>
<Icon icon={icon} />
{title}
</AlertHeader>
<AlertBody className={body}>{children}</AlertBody>
</AlertContainer>
);
}
function AlertContainer({
className,
children,
}: PropsWithChildren<{ className?: string }>) {
return (
<div className={clsx('border-2 border-solid rounded-md', 'p-3', className)}>
{children}
</div>
);
}
function AlertHeader({
className,
children,
}: PropsWithChildren<{ className?: string }>) {
return (
<h4
className={clsx('text-base', 'flex gap-2 items-center !m-0', className)}
>
{children}
</h4>
);
}
function AlertBody({
className,
children,
}: PropsWithChildren<{ className?: string }>) {
return <div className={clsx('ml-6 mt-2 text-sm', className)}>{children}</div>;
}

View file

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

View file

@ -0,0 +1,112 @@
import { ReactNode, useRef } from 'react';
import { useQuery } from 'react-query';
let globalId = 0;
interface Props {
portalId: HubSpotCreateFormOptions['portalId'];
formId: HubSpotCreateFormOptions['formId'];
region: HubSpotCreateFormOptions['region'];
onSubmitted: () => void;
loading?: ReactNode;
}
export function HubspotForm({
loading,
portalId,
region,
formId,
onSubmitted,
}: Props) {
const elRef = useRef<HTMLDivElement>(null);
const id = useRef(`reactHubspotForm${globalId++}`);
const { isLoading } = useHubspotForm({
elId: id.current,
formId,
portalId,
region,
onSubmitted,
});
return (
<>
<div
ref={elRef}
id={id.current}
style={{ display: isLoading ? 'none' : 'block' }}
/>
{isLoading && loading}
</>
);
}
function useHubspotForm({
elId,
formId,
portalId,
region,
onSubmitted,
}: {
elId: string;
portalId: HubSpotCreateFormOptions['portalId'];
formId: HubSpotCreateFormOptions['formId'];
region: HubSpotCreateFormOptions['region'];
onSubmitted: () => void;
}) {
return useQuery(
['hubspot', { elId, formId, portalId, region }],
async () => {
await loadHubspot();
await createForm(`#${elId}`, {
formId,
portalId,
region,
onFormSubmit: onSubmitted,
});
},
{
refetchOnWindowFocus: false,
}
);
}
async function loadHubspot() {
return new Promise<void>((resolve) => {
if (window.hbspt) {
resolve();
return;
}
const script = document.createElement(`script`);
script.defer = true;
script.onload = () => {
resolve();
};
script.src = `//js.hsforms.net/forms/v2.js`;
document.head.appendChild(script);
});
}
async function createForm(
target: string,
options: Omit<HubSpotCreateFormOptions, 'target'>
) {
return new Promise<void>((resolve) => {
if (!window.hbspt) {
throw new Error('hbspt object is missing');
}
window.hbspt.forms.create({
...options,
target,
onFormReady(...rest) {
options.onFormReady?.(...rest);
resolve();
},
});
});
}

View file

@ -12,7 +12,11 @@ export function CloseButton({
return (
<button
type="button"
className={clsx(styles.close, className, 'absolute top-4 right-5')}
className={clsx(
styles.close,
className,
'absolute top-4 right-5 close-button'
)}
onClick={() => onClose()}
>
×

View file

@ -3,7 +3,6 @@
}
.modal-dialog {
width: 450px;
display: inline-block;
text-align: left;
vertical-align: middle;

View file

@ -21,6 +21,8 @@ interface Props {
onDismiss?(): void;
'aria-label'?: string;
'aria-labelledby'?: string;
size?: 'md' | 'lg';
className?: string;
}
export function Modal({
@ -28,6 +30,8 @@ export function Modal({
onDismiss,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
size = 'md',
className,
}: PropsWithChildren<Props>) {
return (
<Context.Provider value>
@ -43,9 +47,12 @@ export function Modal({
<DialogContent
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
className={clsx(styles.modalDialog, 'p-0 bg-transparent')}
className={clsx(styles.modalDialog, 'p-0 bg-transparent', {
'w-[450px]': size === 'md',
'w-[700px]': size === 'lg',
})}
>
<div className={clsx(styles.modalContent, 'relative')}>
<div className={clsx(styles.modalContent, 'relative', className)}>
{children}
{onDismiss && <CloseButton onClose={onDismiss} />}
</div>