diff --git a/src/App.jsx b/src/App.jsx index 01ce35e..afa6b52 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,7 +8,7 @@ import { toCitationCff } from './services/citation.js'; import { validateCitationCffText } from './services/citationValidation.js'; import { toZenodoJson } from './services/zenodo.js'; import { validateZenodoJsonText } from './services/zenodoValidation.js'; -import { normalizeFormInput, validateMetadata } from './validation/validation.js'; +import { hasMeaningfulMetadataValues, normalizeFormInput, validateMetadata } from './validation/validation.js'; const CITATION_FILENAME = 'CITATION.cff'; const ZENODO_FILENAME = '.zenodo.json'; @@ -179,6 +179,11 @@ function formatComparisonStatus(value) { return 'UNKNOWN'; } +function comparisonStatusClass(value) { + const status = String(value ?? '').toLowerCase().replace(/\s+/g, '-'); + return `comparison-status comparison-status-${status}`; +} + function summarizeComparisonIssue(item) { if (!item) { return ''; @@ -381,6 +386,7 @@ export default function App() { const [activePage, setActivePage] = useState('generator'); const [form, setForm] = useState(initialForm); const [githubUrl, setGithubUrl] = useState(''); + const [githubToken, setGithubToken] = useState(''); const [importStatus, setImportStatus] = useState({ loading: false, warnings: [], errors: [], review: null, healthScan: [], comparisons: [] }); const [isZipping, setIsZipping] = useState(false); const [isDownloadingCitation, setIsDownloadingCitation] = useState(false); @@ -389,6 +395,8 @@ export default function App() { const [copyState, setCopyState] = useState('idle'); const [orcidSuggestions, setOrcidSuggestions] = useState({}); const [exportNotice, setExportNotice] = useState({ kind: '', message: '', details: [] }); + const [touchedFields, setTouchedFields] = useState({}); + const [hasImportedMetadata, setHasImportedMetadata] = useState(false); const importRequestIdRef = useRef(0); const normalizedForm = useMemo(() => normalizeFormInput(form), [form]); const normalizedMetadata = useMemo(() => normalizeMetadata(normalizedForm), [normalizedForm]); @@ -496,6 +504,7 @@ export default function App() { function updateField(event) { const { name, value } = event.target; + setTouchedFields((current) => ({ ...current, [name]: true })); setForm((current) => ({ ...current, [name]: value })); } @@ -543,6 +552,11 @@ export default function App() { } function updateAuthorField(index, field, value) { + setTouchedFields((current) => ({ + ...current, + authors: true, + [`authors.${index}.${field}`]: true, + })); setForm((current) => ({ ...current, authors: current.authors.map((author, i) => (i === index ? { ...author, [field]: value } : author)), @@ -682,6 +696,7 @@ export default function App() { try { const result = await importGithubMetadata(repoUrl, { contributorFallbackLimit: 5, + authToken: githubToken.trim(), }); if (importRequestIdRef.current !== requestId) { @@ -690,8 +705,9 @@ export default function App() { let nextForm = metadataToForm(result.metadata); let nextSuggestions = {}; + const importedMeaningfulMetadata = hasMeaningfulMetadataValues(nextForm); - if (result.errors.length === 0) { + if (result.errors.length === 0 && importedMeaningfulMetadata) { const resolved = await resolveOrcidSuggestionsForAuthors(nextForm); nextForm = resolved.form; nextSuggestions = resolved.suggestions; @@ -701,15 +717,29 @@ export default function App() { return; } + if (!importedMeaningfulMetadata) { + setTouchedFields((current) => ({ + ...current, + title: true, + authors: true, + license: true, + version: true, + typeOfWork: true, + publicationDate: true, + })); + } + setImportStatus({ loading: false, warnings: result.warnings, errors: result.errors, - review: result.review || null, - healthScan: Array.isArray(result.healthScan) ? result.healthScan : [], - comparisons: Array.isArray(result.comparisons) ? result.comparisons : [], + review: importedMeaningfulMetadata ? (result.review || null) : null, + healthScan: importedMeaningfulMetadata ? (Array.isArray(result.healthScan) ? result.healthScan : []) : [], + comparisons: importedMeaningfulMetadata ? (Array.isArray(result.comparisons) ? result.comparisons : []) : [], }); + setHasImportedMetadata(importedMeaningfulMetadata); + if (result.errors.length === 0) { setForm(nextForm); setOrcidSuggestions(nextSuggestions); @@ -1033,8 +1063,8 @@ export default function App() {
Reviewed metadata loaded in editor.
@@ -1050,8 +1080,18 @@ export default function App() { placeholder="https://github.com/imageomics/OpenCite" /> +

- This works even when the repository does not have a CITATION.cff file. + Use a fine-grained token with public repository read access for higher API limits. This works even when the repository does not have a CITATION.cff file.

- {errors.authors ? {errors.authors} : null} + {(hasTouchedField('authors') || importedMetadataAvailable) && errors.authors ? {errors.authors} : null} @@ -213,14 +219,14 @@ export function MetadataForm({

Use release-aligned values so your exports match what users see on GitHub and Zenodo.

- )} -