fix(edge/update): remove schedule date for old envs [EE-3023] (#8315)

pull/8283/head
Chaim Lev-Ari 2023-01-24 12:20:55 +05:30 committed by GitHub
parent c9aae27b29
commit 851a3346a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 50 deletions

View File

@ -121,11 +121,6 @@ pr-icon {
width: 20px;
}
.icon-container {
display: flex;
align-items: center;
}
.btn-only-icon {
padding: 6px;
}

View File

@ -25,14 +25,10 @@ export function InformationPanel({
<WidgetBody className={bodyClassName}>
<div style={wrapperStyle}>
{title && (
<div className="col-sm-12 form-section-title">
<span style={{ float: 'left' }}>{title}</span>
<div className="form-section-title">
<span>{title}</span>
{!!onDismiss && (
<span
className="small"
style={{ float: 'right' }}
ng-if="dismissAction"
>
<span className="small" style={{ float: 'right' }}>
<Button color="link" icon={X} onClick={() => onDismiss()}>
dismiss
</Button>

View File

@ -1,41 +1,35 @@
import clsx from 'clsx';
import { PropsWithChildren } from 'react';
import { AlertCircle } from 'lucide-react';
import { Icon } from '@@/Icon';
import { Icon, IconMode } from '@@/Icon';
type Color = 'orange' | 'blue';
export interface Props {
icon?: React.ReactNode;
color?: Color;
}
export function TextTip({
color = 'orange',
icon = AlertCircle,
children,
}: PropsWithChildren<Props>) {
let iconClass: string;
switch (color) {
case 'blue':
iconClass = 'icon-primary';
break;
case 'orange':
iconClass = 'icon-warning';
break;
default:
iconClass = 'icon-warning';
}
return (
<p className="small vertical-center">
<i className="icon-container">
<Icon
icon={AlertCircle}
className={clsx(`${iconClass}`, 'space-right')}
/>
</i>
<p className="small flex items-center gap-1">
<Icon icon={icon} mode={getMode(color)} />
<span className="text-muted">{children}</span>
</p>
);
}
function getMode(color: Color): IconMode {
switch (color) {
case 'blue':
return 'primary';
case 'orange':
default:
return 'warning';
}
}

View File

@ -4,7 +4,6 @@ import { useRouter } from '@uirouter/react';
import { notifySuccess } from '@/portainer/services/notifications';
import { withLimitToBE } from '@/react/hooks/useLimitToBE';
import { isoDate } from '@/portainer/filters/filters';
import { PageHeader } from '@@/PageHeader';
import { Widget } from '@@/Widget';
@ -21,6 +20,7 @@ import { useList } from '../queries/list';
import { NameField } from '../common/NameField';
import { EdgeGroupsField } from '../common/EdgeGroupsField';
import { BetaAlert } from '../common/BetaAlert';
import { defaultValue } from '../common/ScheduledTimeField';
export default withLimitToBE(CreateView);
@ -30,7 +30,7 @@ function CreateView() {
groupIds: [],
type: ScheduleType.Update,
version: '',
scheduledTime: isoDate(Date.now() + 24 * 60 * 60 * 1000),
scheduledTime: defaultValue(),
};
const schedulesQuery = useList();

View File

@ -25,6 +25,10 @@ export function ScheduledTimeField({ disabled }: Props) {
const dateValue = useMemo(() => parseIsoDate(value), [value]);
if (!value) {
return null;
}
return (
<FormControl label="Schedule date & time" errors={error}>
{!disabled ? (
@ -64,6 +68,10 @@ export function timeValidation() {
);
}
export function defaultValue() {
return isoDate(Date.now() + 24 * 60 * 60 * 1000);
}
function isValidDate(date: Date) {
return date instanceof Date && !Number.isNaN(date.valueOf());
}

View File

@ -1,6 +1,7 @@
import { useFormikContext } from 'formik';
import semverCompare from 'semver-compare';
import _ from 'lodash';
import { useEffect } from 'react';
import { EdgeTypes, EnvironmentId } from '@/react/portainer/environments/types';
import { useEnvironmentList } from '@/react/portainer/environments/queries/useEnvironmentList';
@ -10,10 +11,10 @@ import { TextTip } from '@@/Tip/TextTip';
import { FormValues } from './types';
import { useEdgeGroupsEnvironmentIds } from './useEdgeGroupsEnvironmentIds';
import { VersionSelect } from './VersionSelect';
import { ScheduledTimeField } from './ScheduledTimeField';
import { defaultValue, ScheduledTimeField } from './ScheduledTimeField';
export function UpdateScheduleDetailsFieldset() {
const { values } = useFormikContext<FormValues>();
const { values, setFieldValue } = useFormikContext<FormValues>();
const environmentIdsQuery = useEdgeGroupsEnvironmentIds(values.groupIds);
@ -29,6 +30,14 @@ export function UpdateScheduleDetailsFieldset() {
const hasNoTimeZone = 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 (
<>
{edgeGroupsEnvironmentIds.length > 0 ? (

View File

@ -1,27 +1,32 @@
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 { nameValidation } from './NameField';
import { typeValidation } from './ScheduleTypeSelector';
import { FormValues } from './types';
export function validation(
schedules: EdgeUpdateSchedule[],
currentId?: EdgeUpdateSchedule['id']
) {
): SchemaOf<FormValues> {
return object({
groupIds: array().min(1, 'At least one group is required'),
name: nameValidation(schedules, currentId),
type: typeValidation(),
// time: number()
// .min(Date.now() / 1000)
// .required(),
version: string().when('type', {
is: ScheduleType.Update,
// update type
then: (schema) => schema.required('Version is required'),
// rollback
otherwise: (schema) => schema.required('No rollback options available'),
}),
scheduledTime: string()
.default('')
.test('valid', (value) => !value || parseIsoDate(value) !== null),
version: string()
.default('')
.when('type', {
is: ScheduleType.Update,
// update type
then: (schema) => schema.required('Version is required'),
// rollback
otherwise: (schema) => schema.required('No rollback options available'),
}),
});
}