| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- import React from 'react'
- import PropTypes from 'prop-types'
- import { useTranslation } from 'react-i18next'
- import { Dropdown } from 'react-bootstrap'
- import PreviewLogsPaneEntry from './preview-logs-pane-entry'
- import PreviewValidationIssue from './preview-validation-issue'
- import PreviewDownloadFileList from './preview-download-file-list'
- import PreviewError from './preview-error'
- import Icon from '../../../shared/components/icon'
- import usePersistedState from '../../../infrastructure/persisted-state-hook'
- function PreviewLogsPane({
- logEntries = { all: [], errors: [], warnings: [], typesetting: [] },
- rawLog = '',
- validationIssues = {},
- errors = {},
- outputFiles = [],
- isClearingCache,
- isCompiling = false,
- autoCompileHasLintingError = false,
- onLogEntryLocationClick,
- onClearCache
- }) {
- const { t } = useTranslation()
- const {
- all: allCompilerIssues = [],
- errors: compilerErrors = [],
- warnings: compilerWarnings = [],
- typesetting: compilerTypesettingIssues = []
- } = logEntries
- const errorsUI = Object.keys(errors).map((name, index) => (
- <PreviewError key={index} name={name} />
- ))
- const validationIssuesUI = Object.keys(
- validationIssues
- ).map((name, index) => (
- <PreviewValidationIssue
- key={index}
- name={name}
- details={validationIssues[name]}
- />
- ))
- const logEntriesUI = [
- ...compilerErrors,
- ...compilerWarnings,
- ...compilerTypesettingIssues
- ].map((logEntry, idx) => (
- <PreviewLogsPaneEntry
- key={idx}
- headerTitle={logEntry.message}
- rawContent={logEntry.content}
- logType={logEntry.type}
- formattedContent={logEntry.humanReadableHintComponent}
- extraInfoURL={logEntry.extraInfoURL}
- level={logEntry.level}
- entryAriaLabel={t('log_entry_description', {
- level: logEntry.level
- })}
- sourceLocation={{
- file: logEntry.file,
- line: logEntry.line,
- column: logEntry.column
- }}
- onSourceLocationClick={onLogEntryLocationClick}
- />
- ))
- const actionsUI = (
- <div className="logs-pane-actions">
- <button
- className="btn btn-sm btn-danger logs-pane-actions-clear-cache"
- onClick={onClearCache}
- disabled={isClearingCache || isCompiling}
- >
- {isClearingCache ? (
- <Icon type="refresh" spin />
- ) : (
- <Icon type="trash-o" />
- )}
-
- <span>{t('clear_cached_files')}</span>
- </button>
- <Dropdown
- id="dropdown-files-logs-pane"
- dropup
- pullRight
- disabled={isCompiling}
- >
- <Dropdown.Toggle
- className="btn btn-sm btn-info dropdown-toggle"
- title={t('other_logs_and_files')}
- bsStyle="info"
- />
- <Dropdown.Menu id="dropdown-files-logs-pane-list">
- <PreviewDownloadFileList fileList={outputFiles} />
- </Dropdown.Menu>
- </Dropdown>
- </div>
- )
- const rawLogUI = (
- <PreviewLogsPaneEntry
- headerTitle={t('raw_logs')}
- rawContent={rawLog}
- entryAriaLabel={t('raw_logs_description')}
- level="raw"
- />
- )
- return (
- <div className="logs-pane">
- <div className="logs-pane-content">
- <LogsPaneBetaNotice />
- {autoCompileHasLintingError ? <AutoCompileLintingErrorEntry /> : null}
- {errors ? errorsUI : null}
- {validationIssues ? validationIssuesUI : null}
- {allCompilerIssues.length > 0 ? logEntriesUI : null}
- {rawLog && rawLog !== '' ? rawLogUI : null}
- {actionsUI}
- </div>
- </div>
- )
- }
- function AutoCompileLintingErrorEntry() {
- const { t } = useTranslation()
- return (
- <div className="log-entry">
- <div className="log-entry-header log-entry-header-error">
- <div className="log-entry-header-icon-container">
- <Icon type="exclamation-triangle" modifier="fw" />
- </div>
- <h3 className="log-entry-header-title">
- {t('code_check_failed_explanation')}
- </h3>
- </div>
- </div>
- )
- }
- function LogsPaneBetaNotice() {
- const { t } = useTranslation()
- const [dismissedBetaNotice, setDismissedBetaNotice] = usePersistedState(
- `logs_pane.dismissed_beta_notice`,
- false
- )
- function handleDismissButtonClick() {
- setDismissedBetaNotice(true)
- }
- return dismissedBetaNotice ? null : (
- <div className="log-entry">
- <div className="log-entry-header log-entry-header-raw">
- <div className="log-entry-header-icon-container">
- <span className="beta-badge" />
- </div>
- <h3 className="log-entry-header-title">
- {t('logs_pane_beta_message')}
- </h3>
- <a
- href="/beta/participate"
- target="_blank"
- rel="noopener noreferrer"
- className="log-entry-header-link log-entry-header-link-raw"
- >
- <span className="log-entry-header-link-location">
- {t('give_feedback')}
- </span>
- </a>
- <button
- className="btn-inline-link log-entry-header-link"
- type="button"
- aria-label={t('dismiss')}
- onClick={handleDismissButtonClick}
- >
- <span aria-hidden="true">×</span>
- </button>
- </div>
- </div>
- )
- }
- PreviewLogsPane.propTypes = {
- logEntries: PropTypes.shape({
- all: PropTypes.array,
- errors: PropTypes.array,
- warning: PropTypes.array,
- typesetting: PropTypes.array
- }),
- autoCompileHasLintingError: PropTypes.bool,
- rawLog: PropTypes.string,
- outputFiles: PropTypes.array,
- isClearingCache: PropTypes.bool,
- isCompiling: PropTypes.bool,
- onLogEntryLocationClick: PropTypes.func.isRequired,
- onClearCache: PropTypes.func.isRequired,
- validationIssues: PropTypes.object,
- errors: PropTypes.object
- }
- export default PreviewLogsPane
|