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;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-only-icon {
|
||||
padding: 6px;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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 ? (
|
||||
|
|
|
@ -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'),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue