mirror of https://github.com/portainer/portainer
fix(edge/update): remove schedule date for old envs [EE-3023] (#8315)
parent
c9aae27b29
commit
851a3346a9
|
@ -121,11 +121,6 @@ pr-icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-only-icon {
|
.btn-only-icon {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,10 @@ export function InformationPanel({
|
||||||
<WidgetBody className={bodyClassName}>
|
<WidgetBody className={bodyClassName}>
|
||||||
<div style={wrapperStyle}>
|
<div style={wrapperStyle}>
|
||||||
{title && (
|
{title && (
|
||||||
<div className="col-sm-12 form-section-title">
|
<div className="form-section-title">
|
||||||
<span style={{ float: 'left' }}>{title}</span>
|
<span>{title}</span>
|
||||||
{!!onDismiss && (
|
{!!onDismiss && (
|
||||||
<span
|
<span className="small" style={{ float: 'right' }}>
|
||||||
className="small"
|
|
||||||
style={{ float: 'right' }}
|
|
||||||
ng-if="dismissAction"
|
|
||||||
>
|
|
||||||
<Button color="link" icon={X} onClick={() => onDismiss()}>
|
<Button color="link" icon={X} onClick={() => onDismiss()}>
|
||||||
dismiss
|
dismiss
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,41 +1,35 @@
|
||||||
import clsx from 'clsx';
|
|
||||||
import { PropsWithChildren } from 'react';
|
import { PropsWithChildren } from 'react';
|
||||||
import { AlertCircle } from 'lucide-react';
|
import { AlertCircle } from 'lucide-react';
|
||||||
|
|
||||||
import { Icon } from '@@/Icon';
|
import { Icon, IconMode } from '@@/Icon';
|
||||||
|
|
||||||
type Color = 'orange' | 'blue';
|
type Color = 'orange' | 'blue';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
icon?: React.ReactNode;
|
||||||
color?: Color;
|
color?: Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextTip({
|
export function TextTip({
|
||||||
color = 'orange',
|
color = 'orange',
|
||||||
|
icon = AlertCircle,
|
||||||
children,
|
children,
|
||||||
}: PropsWithChildren<Props>) {
|
}: PropsWithChildren<Props>) {
|
||||||
let iconClass: string;
|
|
||||||
|
|
||||||
switch (color) {
|
|
||||||
case 'blue':
|
|
||||||
iconClass = 'icon-primary';
|
|
||||||
break;
|
|
||||||
case 'orange':
|
|
||||||
iconClass = 'icon-warning';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
iconClass = 'icon-warning';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p className="small vertical-center">
|
<p className="small flex items-center gap-1">
|
||||||
<i className="icon-container">
|
<Icon icon={icon} mode={getMode(color)} />
|
||||||
<Icon
|
|
||||||
icon={AlertCircle}
|
|
||||||
className={clsx(`${iconClass}`, 'space-right')}
|
|
||||||
/>
|
|
||||||
</i>
|
|
||||||
<span className="text-muted">{children}</span>
|
<span className="text-muted">{children}</span>
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMode(color: Color): IconMode {
|
||||||
|
switch (color) {
|
||||||
|
case 'blue':
|
||||||
|
return 'primary';
|
||||||
|
case 'orange':
|
||||||
|
default:
|
||||||
|
return 'warning';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { useRouter } from '@uirouter/react';
|
||||||
|
|
||||||
import { notifySuccess } from '@/portainer/services/notifications';
|
import { notifySuccess } from '@/portainer/services/notifications';
|
||||||
import { withLimitToBE } from '@/react/hooks/useLimitToBE';
|
import { withLimitToBE } from '@/react/hooks/useLimitToBE';
|
||||||
import { isoDate } from '@/portainer/filters/filters';
|
|
||||||
|
|
||||||
import { PageHeader } from '@@/PageHeader';
|
import { PageHeader } from '@@/PageHeader';
|
||||||
import { Widget } from '@@/Widget';
|
import { Widget } from '@@/Widget';
|
||||||
|
@ -21,6 +20,7 @@ import { useList } from '../queries/list';
|
||||||
import { NameField } from '../common/NameField';
|
import { NameField } from '../common/NameField';
|
||||||
import { EdgeGroupsField } from '../common/EdgeGroupsField';
|
import { EdgeGroupsField } from '../common/EdgeGroupsField';
|
||||||
import { BetaAlert } from '../common/BetaAlert';
|
import { BetaAlert } from '../common/BetaAlert';
|
||||||
|
import { defaultValue } from '../common/ScheduledTimeField';
|
||||||
|
|
||||||
export default withLimitToBE(CreateView);
|
export default withLimitToBE(CreateView);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ function CreateView() {
|
||||||
groupIds: [],
|
groupIds: [],
|
||||||
type: ScheduleType.Update,
|
type: ScheduleType.Update,
|
||||||
version: '',
|
version: '',
|
||||||
scheduledTime: isoDate(Date.now() + 24 * 60 * 60 * 1000),
|
scheduledTime: defaultValue(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const schedulesQuery = useList();
|
const schedulesQuery = useList();
|
||||||
|
|
|
@ -25,6 +25,10 @@ export function ScheduledTimeField({ disabled }: Props) {
|
||||||
|
|
||||||
const dateValue = useMemo(() => parseIsoDate(value), [value]);
|
const dateValue = useMemo(() => parseIsoDate(value), [value]);
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl label="Schedule date & time" errors={error}>
|
<FormControl label="Schedule date & time" errors={error}>
|
||||||
{!disabled ? (
|
{!disabled ? (
|
||||||
|
@ -64,6 +68,10 @@ export function timeValidation() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function defaultValue() {
|
||||||
|
return isoDate(Date.now() + 24 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
function isValidDate(date: Date) {
|
function isValidDate(date: Date) {
|
||||||
return date instanceof Date && !Number.isNaN(date.valueOf());
|
return date instanceof Date && !Number.isNaN(date.valueOf());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import semverCompare from 'semver-compare';
|
import semverCompare from 'semver-compare';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { EdgeTypes, EnvironmentId } from '@/react/portainer/environments/types';
|
import { EdgeTypes, EnvironmentId } from '@/react/portainer/environments/types';
|
||||||
import { useEnvironmentList } from '@/react/portainer/environments/queries/useEnvironmentList';
|
import { useEnvironmentList } from '@/react/portainer/environments/queries/useEnvironmentList';
|
||||||
|
@ -10,10 +11,10 @@ import { TextTip } from '@@/Tip/TextTip';
|
||||||
import { FormValues } from './types';
|
import { FormValues } from './types';
|
||||||
import { useEdgeGroupsEnvironmentIds } from './useEdgeGroupsEnvironmentIds';
|
import { useEdgeGroupsEnvironmentIds } from './useEdgeGroupsEnvironmentIds';
|
||||||
import { VersionSelect } from './VersionSelect';
|
import { VersionSelect } from './VersionSelect';
|
||||||
import { ScheduledTimeField } from './ScheduledTimeField';
|
import { defaultValue, ScheduledTimeField } from './ScheduledTimeField';
|
||||||
|
|
||||||
export function UpdateScheduleDetailsFieldset() {
|
export function UpdateScheduleDetailsFieldset() {
|
||||||
const { values } = useFormikContext<FormValues>();
|
const { values, setFieldValue } = useFormikContext<FormValues>();
|
||||||
|
|
||||||
const environmentIdsQuery = useEdgeGroupsEnvironmentIds(values.groupIds);
|
const environmentIdsQuery = useEdgeGroupsEnvironmentIds(values.groupIds);
|
||||||
|
|
||||||
|
@ -29,6 +30,14 @@ export function UpdateScheduleDetailsFieldset() {
|
||||||
const hasNoTimeZone = environments.some((env) => !env.LocalTimeZone);
|
const hasNoTimeZone = environments.some((env) => !env.LocalTimeZone);
|
||||||
const hasTimeZone = environments.some((env) => env.LocalTimeZone);
|
const hasTimeZone = environments.some((env) => env.LocalTimeZone);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!hasTimeZone) {
|
||||||
|
setFieldValue('scheduledTime', '');
|
||||||
|
} else if (!values.scheduledTime) {
|
||||||
|
setFieldValue('scheduledTime', defaultValue());
|
||||||
|
}
|
||||||
|
}, [setFieldValue, hasTimeZone, values.scheduledTime]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{edgeGroupsEnvironmentIds.length > 0 ? (
|
{edgeGroupsEnvironmentIds.length > 0 ? (
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
import { array, object, string } from 'yup';
|
import { array, object, SchemaOf, string } from 'yup';
|
||||||
|
|
||||||
|
import { parseIsoDate } from '@/portainer/filters/filters';
|
||||||
|
|
||||||
import { EdgeUpdateSchedule, ScheduleType } from '../types';
|
import { EdgeUpdateSchedule, ScheduleType } from '../types';
|
||||||
|
|
||||||
import { nameValidation } from './NameField';
|
import { nameValidation } from './NameField';
|
||||||
import { typeValidation } from './ScheduleTypeSelector';
|
import { typeValidation } from './ScheduleTypeSelector';
|
||||||
|
import { FormValues } from './types';
|
||||||
|
|
||||||
export function validation(
|
export function validation(
|
||||||
schedules: EdgeUpdateSchedule[],
|
schedules: EdgeUpdateSchedule[],
|
||||||
currentId?: EdgeUpdateSchedule['id']
|
currentId?: EdgeUpdateSchedule['id']
|
||||||
) {
|
): SchemaOf<FormValues> {
|
||||||
return object({
|
return object({
|
||||||
groupIds: array().min(1, 'At least one group is required'),
|
groupIds: array().min(1, 'At least one group is required'),
|
||||||
name: nameValidation(schedules, currentId),
|
name: nameValidation(schedules, currentId),
|
||||||
type: typeValidation(),
|
type: typeValidation(),
|
||||||
// time: number()
|
scheduledTime: string()
|
||||||
// .min(Date.now() / 1000)
|
.default('')
|
||||||
// .required(),
|
.test('valid', (value) => !value || parseIsoDate(value) !== null),
|
||||||
version: string().when('type', {
|
version: string()
|
||||||
|
.default('')
|
||||||
|
.when('type', {
|
||||||
is: ScheduleType.Update,
|
is: ScheduleType.Update,
|
||||||
// update type
|
// update type
|
||||||
then: (schema) => schema.required('Version is required'),
|
then: (schema) => schema.required('Version is required'),
|
||||||
|
|
Loading…
Reference in New Issue