Skip to content
This repository was archived by the owner on Apr 24, 2026. It is now read-only.

Commit 2d38f81

Browse files
authored
Merge pull request #27 from renderghost/feature/issue-23-doi-formats
Fix #23: Support DOI input in all common formats
2 parents 8b038fd + 6b310ac commit 2d38f81

2 files changed

Lines changed: 50 additions & 4 deletions

File tree

‎src/components/research/ResearchForm.tsx‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useState } from 'react';
44
import { useRouter } from 'next/navigation';
55
import type { LinkWork } from '@/types';
66
import { formatWorkType } from '@/lib/utils';
7+
import { normalizeDOI } from '@/lib/data/doi';
78

89
interface ResearchFormProps {
910
mode: 'create' | 'edit';
@@ -31,6 +32,15 @@ export default function ResearchForm({ mode, initialData }: ResearchFormProps) {
3132
rkey: initialData?.rkey,
3233
});
3334

35+
const handleDOIBlur = () => {
36+
if (formData.doi) {
37+
const normalized = normalizeDOI(formData.doi);
38+
if (normalized !== formData.doi) {
39+
setFormData({ ...formData, doi: normalized });
40+
}
41+
}
42+
};
43+
3444
const handleResolveDOI = async () => {
3545
if (!formData.doi) return;
3646

@@ -140,9 +150,10 @@ export default function ResearchForm({ mode, initialData }: ResearchFormProps) {
140150
onChange={(e) =>
141151
setFormData({ ...formData, doi: e.target.value })
142152
}
153+
onBlur={handleDOIBlur}
143154
required
144155
disabled={mode === 'edit'}
145-
placeholder="10.1234/example"
156+
placeholder="10.1234/example or https://doi.org/10.1234/example"
146157
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100"
147158
/>
148159
{mode === 'create' && (
@@ -158,7 +169,7 @@ export default function ResearchForm({ mode, initialData }: ResearchFormProps) {
158169
</div>
159170
<p className="mt-1 text-sm text-gray-500">
160171
{mode === 'create'
161-
? 'Metadata will be automatically fetched from the DOI'
172+
? 'Paste any DOI format - URLs will be automatically cleaned'
162173
: 'DOI cannot be changed after creation'}
163174
</p>
164175
</div>

‎src/lib/data/doi.ts‎

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,41 @@ function stripJATSTags(text: string): string {
1414
return text.replace(/<[^>]*>/g, '').trim();
1515
}
1616

17+
/**
18+
* Normalize a DOI to its canonical form (e.g., "10.1234/example")
19+
*
20+
* Supported input formats:
21+
* - Plain DOI: 10.1234/example
22+
* - With doi: prefix: doi:10.1234/example
23+
* - HTTPS doi.org: https://doi.org/10.1234/example
24+
* - HTTP doi.org: http://doi.org/10.1234/example
25+
* - With www: https://www.doi.org/10.1234/example
26+
* - dx.doi.org: https://dx.doi.org/10.1234/example
27+
* - Handle.net: https://hdl.handle.net/10.1234/example
28+
* - dx.hdl.handle.net: https://dx.hdl.handle.net/10.1234/example
29+
* - URL with doi: prefix: https://doi.org/doi:10.1234/example
30+
*
31+
* @param input - DOI in any supported format
32+
* @returns Normalized DOI starting with "10." or the original input if not recognized
33+
*/
34+
export function normalizeDOI(input: string): string {
35+
if (!input) return input;
36+
37+
let doi = input.trim();
38+
39+
// Remove URL prefixes (http/https, www, dx subdomain, various hosts)
40+
// Handles: doi.org, dx.doi.org, www.doi.org, hdl.handle.net, dx.hdl.handle.net
41+
doi = doi.replace(
42+
/^https?:\/\/(?:www\.)?(?:(?:dx\.)?doi\.org|(?:dx\.)?hdl\.handle\.net)\//i,
43+
''
44+
);
45+
46+
// Remove doi: prefix (may appear after URL stripping or standalone)
47+
doi = doi.replace(/^doi:/i, '');
48+
49+
return doi.trim();
50+
}
51+
1752
export interface DOIMetadata {
1853
title?: string;
1954
authors?: string[];
@@ -26,8 +61,8 @@ export interface DOIMetadata {
2661

2762
export async function resolveDOI(doi: string): Promise<DOIMetadata | null> {
2863
try {
29-
// Clean DOI
30-
const cleanDOI = doi.replace(/^(https?:\/\/)?(dx\.)?doi\.org\//, '');
64+
// Clean DOI using normalizeDOI for comprehensive format support
65+
const cleanDOI = normalizeDOI(doi);
3166

3267
// Try CrossRef first
3368
const response = await fetch(

0 commit comments

Comments
 (0)