import { FC, useEffect, useState } from 'react'; import { InputType } from 'reactstrap/lib/Input'; import { Button, FormGroup, Input, Row } from 'reactstrap'; import { cond, isEmpty, pipe, replace, trim, T } from 'ramda'; import classNames from 'classnames'; import { parseISO } from 'date-fns'; import DateInput, { DateInputProps } from '../utils/DateInput'; import { supportsCrawlableVisits, supportsForwardQuery, supportsShortUrlTitle } from '../utils/helpers/features'; import { SimpleCard } from '../utils/SimpleCard'; import { handleEventPreventingDefault, hasValue, OptionalString } from '../utils/utils'; import Checkbox from '../utils/Checkbox'; import { SelectedServer } from '../servers/data'; import { TagsSelectorProps } from '../tags/helpers/TagsSelector'; import { DomainSelectorProps } from '../domains/DomainSelector'; import { formatIsoDate } from '../utils/helpers/date'; import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon'; import { ShortUrlData } from './data'; import { ShortUrlFormCheckboxGroup } from './helpers/ShortUrlFormCheckboxGroup'; import './ShortUrlForm.scss'; export type Mode = 'create' | 'create-basic' | 'edit'; type DateFields = 'validSince' | 'validUntil'; type NonDateFields = 'longUrl' | 'customSlug' | 'shortCodeLength' | 'domain' | 'maxVisits' | 'title'; export interface ShortUrlFormProps { mode: Mode; saving: boolean; initialState: ShortUrlData; onSave: (shortUrlData: ShortUrlData) => Promise; selectedServer: SelectedServer; } const normalizeTag = pipe(trim, replace(/ /g, '-')); const toDate = (date?: string | Date): Date | undefined => typeof date === 'string' ? parseISO(date) : date; const dynamicColClasses = (flag: boolean) => ({ 'col-sm-6': flag, 'col-sm-12': !flag }); export const ShortUrlForm = ( TagsSelector: FC, DomainSelector: FC, ): FC => ({ mode, saving, onSave, initialState, selectedServer }) => { const [ shortUrlData, setShortUrlData ] = useState(initialState); const isEdit = mode === 'edit'; const hadTitleOriginally = hasValue(initialState.title); const changeTags = (tags: string[]) => setShortUrlData({ ...shortUrlData, tags: tags.map(normalizeTag) }); const reset = () => setShortUrlData(initialState); const resolveNewTitle = (): OptionalString => { const hasNewTitle = hasValue(shortUrlData.title); const matcher = cond([ [ () => !hasNewTitle && !hadTitleOriginally, () => undefined ], [ () => !hasNewTitle && hadTitleOriginally, () => null ], [ T, () => shortUrlData.title ], ]); return matcher(); }; const submit = handleEventPreventingDefault(async () => onSave({ ...shortUrlData, validSince: formatIsoDate(shortUrlData.validSince) ?? null, validUntil: formatIsoDate(shortUrlData.validUntil) ?? null, maxVisits: !hasValue(shortUrlData.maxVisits) ? null : Number(shortUrlData.maxVisits), title: resolveNewTitle(), }).then(() => !isEdit && reset()).catch(() => {})); useEffect(() => { setShortUrlData(initialState); }, [ initialState ]); const renderOptionalInput = (id: NonDateFields, placeholder: string, type: InputType = 'text', props = {}) => ( setShortUrlData({ ...shortUrlData, [id]: e.target.value })} {...props} /> ); const renderDateInput = (id: DateFields, placeholder: string, props: Partial = {}) => (
setShortUrlData({ ...shortUrlData, [id]: date })} {...props} />
); const basicComponents = ( <> setShortUrlData({ ...shortUrlData, longUrl: e.target.value })} /> ); const supportsTitle = supportsShortUrlTitle(selectedServer); const showCustomizeCard = supportsTitle || !isEdit; const limitAccessCardClasses = classNames('mb-3', dynamicColClasses(showCustomizeCard)); const showCrawlableControl = supportsCrawlableVisits(selectedServer); const showForwardQueryControl = supportsForwardQuery(selectedServer); const showBehaviorCard = showCrawlableControl || showForwardQueryControl; const extraChecksCardClasses = classNames('mb-3', dynamicColClasses(showBehaviorCard)); return (
{mode === 'create-basic' && basicComponents} {mode !== 'create-basic' && ( <> {basicComponents} {showCustomizeCard && (
{supportsTitle && renderOptionalInput('title', 'Title')} {!isEdit && ( <>
{renderOptionalInput('customSlug', 'Custom slug', 'text', { disabled: hasValue(shortUrlData.shortCodeLength), })}
{renderOptionalInput('shortCodeLength', 'Short code length', 'number', { min: 4, disabled: hasValue(shortUrlData.customSlug), })}
setShortUrlData({ ...shortUrlData, domain })} /> )}
)}
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })} {renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil ? toDate(shortUrlData.validUntil) : undefined })} {renderDateInput('validUntil', 'Enabled until...', { minDate: shortUrlData.validSince ? toDate(shortUrlData.validSince) : undefined })}
setShortUrlData({ ...shortUrlData, validateUrl })} > Validate URL {!isEdit && (

setShortUrlData({ ...shortUrlData, findIfExists })} > Use existing URL if found

)}
{showBehaviorCard && (
{showCrawlableControl && ( setShortUrlData({ ...shortUrlData, crawlable })} > Make it crawlable )} {showForwardQueryControl && ( setShortUrlData({ ...shortUrlData, forwardQuery })} > Forward query params on redirect )}
)}
)}
); };