/**
* External dependencies
*/
import getRedirectUrl from '@automattic/jetpack-components/tools/jp-redirect';
/**
* WordPress dependencies
*/
import { Page } from '@wordpress/admin-ui';
import apiFetch from '@wordpress/api-fetch';
import {
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalHStack as HStack,
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalVStack as VStack,
Button,
ExternalLink,
Modal,
Spinner,
Tip,
Tooltip,
} from '@wordpress/components';
import { store as coreStore, useEntityRecords } from '@wordpress/core-data';
import { useDispatch, useSelect } from '@wordpress/data';
import { dateI18n, getSettings as getDateSettings } from '@wordpress/date';
import { useCallback, useEffect, useState } from '@wordpress/element';
import { decodeEntities } from '@wordpress/html-entities';
import { __, _n, sprintf } from '@wordpress/i18n';
import { chevronUp, chevronDown, close } from '@wordpress/icons';
import { useParams, useSearch, useNavigate } from '@wordpress/route';
import * as React from 'react';
/**
* Internal dependencies
*/
import CopyClipboardButton from '../../src/dashboard/components/copy-clipboard-button';
import Flag from '../../src/dashboard/components/flag';
import Gravatar from '../../src/dashboard/components/gravatar';
import { store as dashboardStore } from '../../src/dashboard/store';
import type { DispatchActions, SelectActions } from '../../src/dashboard/inbox/stage/types.tsx';
import type { FormResponse } from '../../src/types/index.ts';
const getDisplayName = ( response: FormResponse ) => {
const { author_name, author_email, author_url, ip } = response;
return decodeEntities( author_name || author_email || author_url || ip || 'Anonymous' );
};
const isFileUploadField = ( value: unknown ): boolean => {
return !! value && typeof value === 'object' && 'files' in value;
};
const isImageSelectField = ( value: unknown ): boolean => {
return !! value && typeof value === 'object' && 'type' in value && value.type === 'image-select';
};
const isLikelyPhoneNumber = ( value: unknown ): boolean => {
if ( typeof value !== 'string' ) {
return false;
}
const normalizedValue = value.trim();
if ( ! /^[\d+\-\s().]+$/.test( normalizedValue ) ) {
return false;
}
if ( /^\d{4}[-/]\d{1,2}[-/]\d{1,2}$/.test( normalizedValue ) ) {
return false;
}
if ( /^\d{1,2}[-/]\d{1,2}[-/]\d{2,4}$/.test( normalizedValue ) ) {
return false;
}
const digits = normalizedValue.replace( /\D/g, '' );
if ( digits.length < 7 || digits.length > 15 ) {
return false;
}
return true;
};
/**
* Renders a preview of an image file.
*
* @param props - Props used while rendering the preview.
* @param props.file - The image file object.
* @param props.file.url - The URL of the image file.
* @param props.file.name - The name of the image file.
* @param props.isLoading - Whether the preview is currently loading.
* @param props.onImageLoaded - Callback fired when the image finishes loading.
*
* @return - Element containing the file preview.
*/
function PreviewFile( {
file,
isLoading,
onImageLoaded,
}: {
file: { url: string; name: string };
isLoading: boolean;
onImageLoaded: () => void;
} ) {
return (
{ isLoading && (
{ __( 'Loading preview…', 'jetpack-forms' ) }
) }
);
}
type UploadedFile = {
url: string;
name: string;
is_image?: boolean;
};
/**
* Renders a list of uploaded files.
*
* @param props - Props used while rendering the list of uploaded files.
* @param props.files - The list of uploaded files.
* @param props.handleFilePreview - Callback fired when a file is clicked.
*
* @return - Element containing the list of uploaded files.
*/
function FieldFile( {
files,
handleFilePreview,
}: {
files: Array< UploadedFile >;
handleFilePreview: ( file: UploadedFile ) => () => void;
} ) {
return (
);
}
/**
* Renders an email address.
*
* @param props - Props used while rendering the email address.
* @param props.email - The email address to render.
*
* @return - Element containing the email address.
*/
function FieldEmail( { email }: { email: string } ) {
return (
{ email }
);
}
type ImageSelectChoice = {
url: string;
name: string;
selected?: boolean;
};
/**
* Creates a handler for the enter key.
*
* @param handler - The handler to call when the enter key is pressed.
*
* @return - Function that handles the enter key press.
*/
function createEnterKeyHandler( handler: () => void ) {
return function handleEnterKeyDown( event: React.KeyboardEvent< HTMLDivElement > ) {
if ( event.key === 'Enter' ) {
handler();
}
};
}
/**
* Renders a list of image choices.
*
* @param props - Props used while rendering the list of image choices.
* @param props.choices - The list of image choices.
* @param props.handleFilePreview - Callback fired when a image choice is clicked.
*
* @return - Element containing the list of image choices.
*/
function FieldImageSelect( {
choices,
handleFilePreview,
}: {
choices: Array< ImageSelectChoice >;
handleFilePreview: ( choice: ImageSelectChoice ) => () => void;
} ) {
return (
);
}
/**
* Renders the navigation for a response.
*
* @param props - Props used while rendering the navigation for a response.
* @param props.hasNext - Whether there is a next response.
* @param props.hasPrevious - Whether there is a previous response.
* @param props.onNext - Callback fired when the next response is clicked.
* @param props.onPrevious - Callback fired when the previous response is clicked.
* @param props.onClose - Callback fired when the navigation is closed.
*
* @return - Element containing the navigation for a response.
*/
function ResponseNavigation( {
hasNext,
hasPrevious,
onNext,
onPrevious,
onClose,
}: {
hasNext: boolean;
hasPrevious: boolean;
onNext: () => void;
onPrevious: () => void;
onClose: () => void;
} ) {
const sharedProps = {
accessibleWhenDisabled: true,
iconSize: 24,
showTooltip: true,
size: 'compact' as const,
};
return (