import { parseISO } from 'date-fns'; import { cond, isEmpty, pipe, replace, T, trim } from 'ramda'; import type { FC } from 'react'; import { useEffect, useState } from 'react'; import { Button, FormGroup, Input, Row } from 'reactstrap'; import type { InputType } from 'reactstrap/types/lib/Input'; import type { DomainSelectorProps } from '../domains/DomainSelector'; import type { SelectedServer } from '../servers/data'; import type { TagsSelectorProps } from '../tags/helpers/TagsSelector'; import { Checkbox } from '../utils/Checkbox'; import type { DateTimeInputProps } from '../utils/dates/DateTimeInput'; import { DateTimeInput } from '../utils/dates/DateTimeInput'; import { formatIsoDate } from '../utils/helpers/date'; import { useFeature } from '../utils/helpers/features'; import { SimpleCard } from '../utils/SimpleCard'; import type { OptionalString } from '../utils/utils'; import { handleEventPreventingDefault, hasValue } from '../utils/utils'; import type { ShortUrlData } from './data'; import { ShortUrlFormCheckboxGroup } from './helpers/ShortUrlFormCheckboxGroup'; import { UseExistingIfFoundInfoIcon } from './UseExistingIfFoundInfoIcon'; 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); export const ShortUrlForm = ( TagsSelector: FC, DomainSelector: FC, ): FC => ({ mode, saving, onSave, initialState, selectedServer }) => { const [shortUrlData, setShortUrlData] = useState(initialState); const isEdit = mode === 'edit'; const isBasicMode = mode === 'create-basic'; 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 = {}, fromGroupProps = {}, ) => ( 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 })} /> {isBasicMode && renderOptionalInput('customSlug', 'Custom slug', 'text', { bsSize: 'lg' }, { className: 'col-lg-6' })}
); const showForwardQueryControl = useFeature('forwardQuery', selectedServer); return (
{isBasicMode && basicComponents} {!isBasicMode && ( <> {basicComponents}
{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

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