| 1 |
import React from 'react' |
| 2 |
import { createInterpolateElement } from '@wordpress/element' |
| 3 |
import { __, sprintf } from '@wordpress/i18n' |
| 4 |
import { Notice } from '../../common/Notice' |
| 5 |
import { ImportCard } from '../common/ImportCard' |
| 6 |
import { ImporterSelector } from './ImporterSelector' |
| 7 |
import { ImportOptions } from './ImportOptions' |
| 8 |
import { SimpleSnippetTable } from './SimpleSnippetTable' |
| 9 |
import { MigrationStep, WithMigrationData, useMigrationData } from './WithMigrationData' |
| 10 |
import { WithMigrationOptions, useMigrationOptions } from './WithMigrationOptions' |
| 11 |
import type { ReactNode } from 'react' |
| 12 |
|
| 13 |
interface StatusDisplayProps { |
| 14 |
type: 'error' | 'success' |
| 15 |
title: ReactNode |
| 16 |
children: ReactNode |
| 17 |
} |
| 18 |
|
| 19 |
const StatusDisplay: React.FC<StatusDisplayProps> = ({ type, title, children }) => |
| 20 |
<ImportCard |
| 21 |
variant="controls" |
| 22 |
className="import-section-status" |
| 23 |
role={'error' === type ? 'alert' : 'status'} |
| 24 |
aria-live={'error' === type ? 'assertive' : 'polite'} |
| 25 |
> |
| 26 |
<div className={type} aria-hidden="true"><span>{'error' === type ? 'âś•' : 'âś“'}</span></div> |
| 27 |
<div> |
| 28 |
<h4>{title}</h4> |
| 29 |
<p>{children}</p> |
| 30 |
</div> |
| 31 |
</ImportCard> |
| 32 |
|
| 33 |
const StatusMessages: React.FC = () => { |
| 34 |
const { selectedImporter } = useMigrationOptions() |
| 35 |
const { error, isWorking, snippetSelection, importedIds } = useMigrationData() |
| 36 |
|
| 37 |
return ( |
| 38 |
<> |
| 39 |
{error?.step === MigrationStep.FetchSnippets && ( |
| 40 |
<StatusDisplay type="error" title={__('Error loading snippets', 'code-snippets')}> |
| 41 |
{error.message} |
| 42 |
</StatusDisplay>)} |
| 43 |
|
| 44 |
{error?.step === MigrationStep.MigrateSnippets && ( |
| 45 |
<StatusDisplay type="error" title={__('Error importing snippets', 'code-snippets')}> |
| 46 |
{error.message} |
| 47 |
</StatusDisplay>)} |
| 48 |
|
| 49 |
{0 < importedIds.length && ( |
| 50 |
// translators: %d: number of imported snippets. |
| 51 |
<StatusDisplay type="success" title={sprintf(__('%d snippets imported!', 'code-snippets'), importedIds.length)}> |
| 52 |
{createInterpolateElement( |
| 53 |
__('Selected snippets have been successfully imported to your <a>Code Snippets library</a>.', 'code-snippets'), |
| 54 |
{ |
| 55 |
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label |
| 56 |
a: <a href={window.CODE_SNIPPETS?.urls.manage} /> |
| 57 |
} |
| 58 |
)} |
| 59 |
</StatusDisplay>)} |
| 60 |
|
| 61 |
{selectedImporter && |
| 62 |
isWorking !== MigrationStep.FetchSnippets && error?.step !== MigrationStep.FetchSnippets && |
| 63 |
0 === snippetSelection.availableItems.length && 0 === importedIds.length && ( |
| 64 |
<ImportCard className="no-snippets-card" role="status" aria-live="polite"> |
| 65 |
<div className="card-inner"> |
| 66 |
<div className="card-icon" aria-hidden="true">đź“</div> |
| 67 |
<h4>{__('No snippets found', 'code-snippets')}</h4> |
| 68 |
<p>{__('No snippets were found for the selected plugin. Make sure the plugin is installed and has snippets configured.', 'code-snippets')}</p> |
| 69 |
</div> |
| 70 |
</ImportCard>)} |
| 71 |
</> |
| 72 |
) |
| 73 |
} |
| 74 |
|
| 75 |
const MigrateFormInner: React.FC = () => { |
| 76 |
const { selectedImporter } = useMigrationOptions() |
| 77 |
const { isWorking, error, importers, snippetSelection, importSnippets, changeSelectedImporter } = useMigrationData() |
| 78 |
|
| 79 |
if (isWorking === MigrationStep.LoadImporters) { |
| 80 |
return <p role="status" aria-live="polite">{__('Loading importers…', 'code-snippets')}</p> |
| 81 |
} |
| 82 |
|
| 83 |
if (error?.step === MigrationStep.LoadImporters) { |
| 84 |
return ( |
| 85 |
<Notice type="error"> |
| 86 |
{/* translators: %s: error message. */} |
| 87 |
<p>{sprintf(__('Error loading importers: %s', 'code-snippets'), error.message)}</p> |
| 88 |
</Notice> |
| 89 |
) |
| 90 |
} |
| 91 |
|
| 92 |
return ( |
| 93 |
<> |
| 94 |
<ImporterSelector |
| 95 |
value={selectedImporter} |
| 96 |
options={importers} |
| 97 |
isLoading={isWorking === MigrationStep.FetchSnippets} |
| 98 |
onChange={value => changeSelectedImporter(value)} |
| 99 |
/> |
| 100 |
|
| 101 |
<StatusMessages /> |
| 102 |
|
| 103 |
{0 < snippetSelection.availableItems.length && ( |
| 104 |
<> |
| 105 |
<ImportOptions /> |
| 106 |
|
| 107 |
<SimpleSnippetTable |
| 108 |
onImport={importSnippets} |
| 109 |
selection={snippetSelection} |
| 110 |
isImporting={isWorking === MigrationStep.MigrateSnippets} |
| 111 |
/> |
| 112 |
</>)} |
| 113 |
</> |
| 114 |
) |
| 115 |
} |
| 116 |
|
| 117 |
export const MigrateForm: React.FC = () => |
| 118 |
<WithMigrationOptions> |
| 119 |
<WithMigrationData> |
| 120 |
<MigrateFormInner /> |
| 121 |
</WithMigrationData> |
| 122 |
</WithMigrationOptions> |
| 123 |
|