mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	PAT: add "never", "custom" options to expiry date (#2198)
* add DateTimePicker component * PAT expiry - custom, never * show "never" in PAT table * add alert, some styling
This commit is contained in:
		
							parent
							
								
									7524dad7e8
								
							
						
					
					
						commit
						d261097151
					
				@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					import { INPUT_ERROR_TEXT } from 'utils/testIds';
 | 
				
			||||||
 | 
					import { TextField, OutlinedTextFieldProps } from '@mui/material';
 | 
				
			||||||
 | 
					import { parseValidDate } from '../util';
 | 
				
			||||||
 | 
					import { format } from 'date-fns';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IDateTimePickerProps extends Omit<OutlinedTextFieldProps, 'variant'> {
 | 
				
			||||||
 | 
					    label: string;
 | 
				
			||||||
 | 
					    type?: 'date' | 'datetime';
 | 
				
			||||||
 | 
					    error?: boolean;
 | 
				
			||||||
 | 
					    errorText?: string;
 | 
				
			||||||
 | 
					    min?: Date;
 | 
				
			||||||
 | 
					    max?: Date;
 | 
				
			||||||
 | 
					    value: Date;
 | 
				
			||||||
 | 
					    onChange: (e: any) => any;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const formatDate = (value: string) => {
 | 
				
			||||||
 | 
					    const date = new Date(value);
 | 
				
			||||||
 | 
					    return format(date, 'yyyy-MM-dd');
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const formatDateTime = (value: string) => {
 | 
				
			||||||
 | 
					    const date = new Date(value);
 | 
				
			||||||
 | 
					    return format(date, 'yyyy-MM-dd') + 'T' + format(date, 'HH:mm');
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DateTimePicker = ({
 | 
				
			||||||
 | 
					    label,
 | 
				
			||||||
 | 
					    type = 'datetime',
 | 
				
			||||||
 | 
					    error,
 | 
				
			||||||
 | 
					    errorText,
 | 
				
			||||||
 | 
					    min,
 | 
				
			||||||
 | 
					    max,
 | 
				
			||||||
 | 
					    value,
 | 
				
			||||||
 | 
					    onChange,
 | 
				
			||||||
 | 
					    InputProps,
 | 
				
			||||||
 | 
					    ...rest
 | 
				
			||||||
 | 
					}: IDateTimePickerProps) => {
 | 
				
			||||||
 | 
					    const getDate = type === 'datetime' ? formatDateTime : formatDate;
 | 
				
			||||||
 | 
					    const inputType = type === 'datetime' ? 'datetime-local' : 'date';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <TextField
 | 
				
			||||||
 | 
					            type={inputType}
 | 
				
			||||||
 | 
					            size="small"
 | 
				
			||||||
 | 
					            variant="outlined"
 | 
				
			||||||
 | 
					            label={label}
 | 
				
			||||||
 | 
					            error={error}
 | 
				
			||||||
 | 
					            helperText={errorText}
 | 
				
			||||||
 | 
					            value={getDate(value.toISOString())}
 | 
				
			||||||
 | 
					            onChange={e => {
 | 
				
			||||||
 | 
					                const parsedDate = parseValidDate(e.target.value);
 | 
				
			||||||
 | 
					                onChange(parsedDate ?? value);
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            FormHelperTextProps={{
 | 
				
			||||||
 | 
					                ['data-testid']: INPUT_ERROR_TEXT,
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            inputProps={{
 | 
				
			||||||
 | 
					                min: min ? getDate(min.toISOString()) : min,
 | 
				
			||||||
 | 
					                max: max ? getDate(max.toISOString()) : max,
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            {...rest}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { Button, styled, Typography } from '@mui/material';
 | 
					import { Alert, Button, styled, Typography } from '@mui/material';
 | 
				
			||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
 | 
					import FormTemplate from 'component/common/FormTemplate/FormTemplate';
 | 
				
			||||||
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
 | 
					import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
 | 
				
			||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
					import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
				
			||||||
@ -13,6 +13,7 @@ import { formatDateYMD } from 'utils/formatDate';
 | 
				
			|||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
 | 
					import { useLocationSettings } from 'hooks/useLocationSettings';
 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
import { INewPersonalAPIToken } from 'interfaces/personalAPIToken';
 | 
					import { INewPersonalAPIToken } from 'interfaces/personalAPIToken';
 | 
				
			||||||
 | 
					import { DateTimePicker } from 'component/common/DateTimePicker/DateTimePicker';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledForm = styled('form')(() => ({
 | 
					const StyledForm = styled('form')(() => ({
 | 
				
			||||||
    display: 'flex',
 | 
					    display: 'flex',
 | 
				
			||||||
@ -31,18 +32,37 @@ const StyledInput = styled(Input)(({ theme }) => ({
 | 
				
			|||||||
    marginBottom: theme.spacing(2),
 | 
					    marginBottom: theme.spacing(2),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledExpirationPicker = styled('div')(({ theme }) => ({
 | 
					const StyledExpirationPicker = styled('div')<{ custom?: boolean }>(
 | 
				
			||||||
    display: 'flex',
 | 
					    ({ theme, custom }) => ({
 | 
				
			||||||
    alignItems: 'center',
 | 
					        display: 'flex',
 | 
				
			||||||
    gap: theme.spacing(1.5),
 | 
					        alignItems: custom ? 'start' : 'center',
 | 
				
			||||||
    [theme.breakpoints.down('sm')]: {
 | 
					        gap: theme.spacing(1.5),
 | 
				
			||||||
        flexDirection: 'column',
 | 
					        marginBottom: theme.spacing(2),
 | 
				
			||||||
        alignItems: 'flex-start',
 | 
					        [theme.breakpoints.down('sm')]: {
 | 
				
			||||||
    },
 | 
					            flexDirection: 'column',
 | 
				
			||||||
}));
 | 
					            alignItems: 'flex-start',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledSelectMenu = styled(SelectMenu)(({ theme }) => ({
 | 
					const StyledSelectMenu = styled(SelectMenu)(({ theme }) => ({
 | 
				
			||||||
    minWidth: theme.spacing(20),
 | 
					    minWidth: theme.spacing(20),
 | 
				
			||||||
 | 
					    marginRight: theme.spacing(0.5),
 | 
				
			||||||
 | 
					    [theme.breakpoints.down('sm')]: {
 | 
				
			||||||
 | 
					        width: theme.spacing(50),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledDateTimePicker = styled(DateTimePicker)(({ theme }) => ({
 | 
				
			||||||
 | 
					    width: theme.spacing(28),
 | 
				
			||||||
 | 
					    [theme.breakpoints.down('sm')]: {
 | 
				
			||||||
 | 
					        width: theme.spacing(50),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledAlert = styled(Alert)(({ theme }) => ({
 | 
				
			||||||
 | 
					    marginBottom: theme.spacing(2),
 | 
				
			||||||
 | 
					    maxWidth: theme.spacing(50),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledButtonContainer = styled('div')(({ theme }) => ({
 | 
					const StyledButtonContainer = styled('div')(({ theme }) => ({
 | 
				
			||||||
@ -62,6 +82,8 @@ enum ExpirationOption {
 | 
				
			|||||||
    '7DAYS' = '7d',
 | 
					    '7DAYS' = '7d',
 | 
				
			||||||
    '30DAYS' = '30d',
 | 
					    '30DAYS' = '30d',
 | 
				
			||||||
    '60DAYS' = '60d',
 | 
					    '60DAYS' = '60d',
 | 
				
			||||||
 | 
					    NEVER = 'never',
 | 
				
			||||||
 | 
					    CUSTOM = 'custom',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const expirationOptions = [
 | 
					const expirationOptions = [
 | 
				
			||||||
@ -80,8 +102,26 @@ const expirationOptions = [
 | 
				
			|||||||
        days: 60,
 | 
					        days: 60,
 | 
				
			||||||
        label: '60 days',
 | 
					        label: '60 days',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        key: ExpirationOption.NEVER,
 | 
				
			||||||
 | 
					        label: 'Never',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        key: ExpirationOption.CUSTOM,
 | 
				
			||||||
 | 
					        label: 'Custom',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum ErrorField {
 | 
				
			||||||
 | 
					    DESCRIPTION = 'description',
 | 
				
			||||||
 | 
					    EXPIRES_AT = 'expiresAt',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ICreatePersonalAPITokenErrors {
 | 
				
			||||||
 | 
					    [ErrorField.DESCRIPTION]?: string;
 | 
				
			||||||
 | 
					    [ErrorField.EXPIRES_AT]?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ICreatePersonalAPITokenProps {
 | 
					interface ICreatePersonalAPITokenProps {
 | 
				
			||||||
    open: boolean;
 | 
					    open: boolean;
 | 
				
			||||||
    setOpen: React.Dispatch<React.SetStateAction<boolean>>;
 | 
					    setOpen: React.Dispatch<React.SetStateAction<boolean>>;
 | 
				
			||||||
@ -103,10 +143,14 @@ export const CreatePersonalAPIToken: FC<ICreatePersonalAPITokenProps> = ({
 | 
				
			|||||||
    const [expiration, setExpiration] = useState<ExpirationOption>(
 | 
					    const [expiration, setExpiration] = useState<ExpirationOption>(
 | 
				
			||||||
        ExpirationOption['30DAYS']
 | 
					        ExpirationOption['30DAYS']
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    const [errors, setErrors] = useState<{ [key: string]: string }>({});
 | 
					    const [errors, setErrors] = useState<ICreatePersonalAPITokenErrors>({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const clearErrors = () => {
 | 
					    const clearError = (field: ErrorField) => {
 | 
				
			||||||
        setErrors({});
 | 
					        setErrors(errors => ({ ...errors, [field]: undefined }));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const setError = (field: ErrorField, error: string) => {
 | 
				
			||||||
 | 
					        setErrors(errors => ({ ...errors, [field]: error }));
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const calculateDate = () => {
 | 
					    const calculateDate = () => {
 | 
				
			||||||
@ -114,7 +158,11 @@ export const CreatePersonalAPIToken: FC<ICreatePersonalAPITokenProps> = ({
 | 
				
			|||||||
        const expirationOption = expirationOptions.find(
 | 
					        const expirationOption = expirationOptions.find(
 | 
				
			||||||
            ({ key }) => key === expiration
 | 
					            ({ key }) => key === expiration
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        if (expirationOption) {
 | 
					        if (expiration === ExpirationOption.NEVER) {
 | 
				
			||||||
 | 
					            expiresAt.setFullYear(expiresAt.getFullYear() + 1000);
 | 
				
			||||||
 | 
					        } else if (expiration === ExpirationOption.CUSTOM) {
 | 
				
			||||||
 | 
					            expiresAt.setMinutes(expiresAt.getMinutes() + 30);
 | 
				
			||||||
 | 
					        } else if (expirationOption?.days) {
 | 
				
			||||||
            expiresAt.setDate(expiresAt.getDate() + expirationOption.days);
 | 
					            expiresAt.setDate(expiresAt.getDate() + expirationOption.days);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return expiresAt;
 | 
					        return expiresAt;
 | 
				
			||||||
@ -124,10 +172,12 @@ export const CreatePersonalAPIToken: FC<ICreatePersonalAPITokenProps> = ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        setDescription('');
 | 
					        setDescription('');
 | 
				
			||||||
 | 
					        setErrors({});
 | 
				
			||||||
        setExpiration(ExpirationOption['30DAYS']);
 | 
					        setExpiration(ExpirationOption['30DAYS']);
 | 
				
			||||||
    }, [open]);
 | 
					    }, [open]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        clearError(ErrorField.EXPIRES_AT);
 | 
				
			||||||
        setExpiresAt(calculateDate());
 | 
					        setExpiresAt(calculateDate());
 | 
				
			||||||
    }, [expiration]);
 | 
					    }, [expiration]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -166,19 +216,26 @@ export const CreatePersonalAPIToken: FC<ICreatePersonalAPITokenProps> = ({
 | 
				
			|||||||
    const isDescriptionUnique = (description: string) =>
 | 
					    const isDescriptionUnique = (description: string) =>
 | 
				
			||||||
        !tokens?.some(token => token.description === description);
 | 
					        !tokens?.some(token => token.description === description);
 | 
				
			||||||
    const isValid =
 | 
					    const isValid =
 | 
				
			||||||
        isDescriptionEmpty(description) && isDescriptionUnique(description);
 | 
					        isDescriptionEmpty(description) &&
 | 
				
			||||||
 | 
					        isDescriptionUnique(description) &&
 | 
				
			||||||
 | 
					        expiresAt > new Date();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onSetDescription = (description: string) => {
 | 
					    const onSetDescription = (description: string) => {
 | 
				
			||||||
        clearErrors();
 | 
					        clearError(ErrorField.DESCRIPTION);
 | 
				
			||||||
        if (!isDescriptionUnique(description)) {
 | 
					        if (!isDescriptionUnique(description)) {
 | 
				
			||||||
            setErrors({
 | 
					            setError(
 | 
				
			||||||
                description:
 | 
					                ErrorField.DESCRIPTION,
 | 
				
			||||||
                    'A personal API token with that description already exists.',
 | 
					                'A personal API token with that description already exists.'
 | 
				
			||||||
            });
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        setDescription(description);
 | 
					        setDescription(description);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const customExpiration = expiration === ExpirationOption.CUSTOM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const neverExpires =
 | 
				
			||||||
 | 
					        expiresAt.getFullYear() > new Date().getFullYear() + 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <SidebarModal
 | 
					        <SidebarModal
 | 
				
			||||||
            open={open}
 | 
					            open={open}
 | 
				
			||||||
@ -215,7 +272,7 @@ export const CreatePersonalAPIToken: FC<ICreatePersonalAPITokenProps> = ({
 | 
				
			|||||||
                        <StyledInputDescription>
 | 
					                        <StyledInputDescription>
 | 
				
			||||||
                            Token expiration date
 | 
					                            Token expiration date
 | 
				
			||||||
                        </StyledInputDescription>
 | 
					                        </StyledInputDescription>
 | 
				
			||||||
                        <StyledExpirationPicker>
 | 
					                        <StyledExpirationPicker custom={customExpiration}>
 | 
				
			||||||
                            <StyledSelectMenu
 | 
					                            <StyledSelectMenu
 | 
				
			||||||
                                name="expiration"
 | 
					                                name="expiration"
 | 
				
			||||||
                                id="expiration"
 | 
					                                id="expiration"
 | 
				
			||||||
@ -229,20 +286,61 @@ export const CreatePersonalAPIToken: FC<ICreatePersonalAPITokenProps> = ({
 | 
				
			|||||||
                                options={expirationOptions}
 | 
					                                options={expirationOptions}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                            <ConditionallyRender
 | 
					                            <ConditionallyRender
 | 
				
			||||||
                                condition={Boolean(expiresAt)}
 | 
					                                condition={customExpiration}
 | 
				
			||||||
                                show={() => (
 | 
					                                show={() => (
 | 
				
			||||||
                                    <Typography variant="body2">
 | 
					                                    <StyledDateTimePicker
 | 
				
			||||||
                                        Token will expire on{' '}
 | 
					                                        label="Date"
 | 
				
			||||||
                                        <strong>
 | 
					                                        value={expiresAt}
 | 
				
			||||||
                                            {formatDateYMD(
 | 
					                                        onChange={date => {
 | 
				
			||||||
                                                expiresAt!,
 | 
					                                            clearError(ErrorField.EXPIRES_AT);
 | 
				
			||||||
                                                locationSettings.locale
 | 
					                                            if (date < new Date()) {
 | 
				
			||||||
                                            )}
 | 
					                                                setError(
 | 
				
			||||||
                                        </strong>
 | 
					                                                    ErrorField.EXPIRES_AT,
 | 
				
			||||||
                                    </Typography>
 | 
					                                                    'Invalid date, must be in the future'
 | 
				
			||||||
 | 
					                                                );
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            setExpiresAt(date);
 | 
				
			||||||
 | 
					                                        }}
 | 
				
			||||||
 | 
					                                        min={new Date()}
 | 
				
			||||||
 | 
					                                        error={Boolean(errors.expiresAt)}
 | 
				
			||||||
 | 
					                                        errorText={errors.expiresAt}
 | 
				
			||||||
 | 
					                                        required
 | 
				
			||||||
 | 
					                                    />
 | 
				
			||||||
                                )}
 | 
					                                )}
 | 
				
			||||||
 | 
					                                elseShow={
 | 
				
			||||||
 | 
					                                    <ConditionallyRender
 | 
				
			||||||
 | 
					                                        condition={neverExpires}
 | 
				
			||||||
 | 
					                                        show={
 | 
				
			||||||
 | 
					                                            <Typography variant="body2">
 | 
				
			||||||
 | 
					                                                The token will{' '}
 | 
				
			||||||
 | 
					                                                <strong>never</strong> expire!
 | 
				
			||||||
 | 
					                                            </Typography>
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        elseShow={() => (
 | 
				
			||||||
 | 
					                                            <Typography variant="body2">
 | 
				
			||||||
 | 
					                                                Token will expire on{' '}
 | 
				
			||||||
 | 
					                                                <strong>
 | 
				
			||||||
 | 
					                                                    {formatDateYMD(
 | 
				
			||||||
 | 
					                                                        expiresAt!,
 | 
				
			||||||
 | 
					                                                        locationSettings.locale
 | 
				
			||||||
 | 
					                                                    )}
 | 
				
			||||||
 | 
					                                                </strong>
 | 
				
			||||||
 | 
					                                            </Typography>
 | 
				
			||||||
 | 
					                                        )}
 | 
				
			||||||
 | 
					                                    />
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        </StyledExpirationPicker>
 | 
					                        </StyledExpirationPicker>
 | 
				
			||||||
 | 
					                        <ConditionallyRender
 | 
				
			||||||
 | 
					                            condition={neverExpires}
 | 
				
			||||||
 | 
					                            show={
 | 
				
			||||||
 | 
					                                <StyledAlert severity="warning">
 | 
				
			||||||
 | 
					                                    We strongly recommend that you set an
 | 
				
			||||||
 | 
					                                    expiration date for your token to help keep
 | 
				
			||||||
 | 
					                                    your information secure.
 | 
				
			||||||
 | 
					                                </StyledAlert>
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <StyledButtonContainer>
 | 
					                    <StyledButtonContainer>
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@ import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
 | 
				
			|||||||
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
 | 
					import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
 | 
				
			||||||
import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
 | 
					import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
 | 
				
			||||||
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
 | 
					import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
 | 
				
			||||||
 | 
					import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
 | 
				
			||||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
 | 
					import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
 | 
				
			||||||
import { usePersonalAPITokens } from 'hooks/api/getters/usePersonalAPITokens/usePersonalAPITokens';
 | 
					import { usePersonalAPITokens } from 'hooks/api/getters/usePersonalAPITokens/usePersonalAPITokens';
 | 
				
			||||||
import { useSearch } from 'hooks/useSearch';
 | 
					import { useSearch } from 'hooks/useSearch';
 | 
				
			||||||
@ -116,7 +117,13 @@ export const PersonalAPITokensTab = ({ user }: IPersonalAPITokensTabProps) => {
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                Header: 'Expires',
 | 
					                Header: 'Expires',
 | 
				
			||||||
                accessor: 'expiresAt',
 | 
					                accessor: 'expiresAt',
 | 
				
			||||||
                Cell: DateCell,
 | 
					                Cell: ({ value }: { value: string }) => {
 | 
				
			||||||
 | 
					                    const date = new Date(value);
 | 
				
			||||||
 | 
					                    if (date.getFullYear() > new Date().getFullYear() + 100) {
 | 
				
			||||||
 | 
					                        return <TextCell>Never</TextCell>;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return <DateCell value={value} />;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
                sortType: 'date',
 | 
					                sortType: 'date',
 | 
				
			||||||
                maxWidth: 150,
 | 
					                maxWidth: 150,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user