Skip to content

Commit 85274c8

Browse files
committed
feat: refactor components
1 parent 2d39416 commit 85274c8

File tree

7 files changed

+371
-324
lines changed

7 files changed

+371
-324
lines changed

src/ui/App.tsx

Lines changed: 22 additions & 324 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,13 @@
11
import React, { useState, useEffect } from 'react';
22
import { createRoot } from 'react-dom/client';
3-
import ReactMarkdown from 'react-markdown';
43
import { SemVer } from 'semver';
5-
6-
interface Package {
7-
name: string;
8-
version: string;
9-
location: string;
10-
}
11-
12-
type ReleaseType =
13-
| 'major'
14-
| 'minor'
15-
| 'patch'
16-
| 'intentionally-skip'
17-
| 'custom'
18-
| string;
4+
import { ErrorMessage } from './ErrorMessage.js';
5+
import { PackageItem } from './PackageItem.js';
6+
import { Package, ReleaseType } from './types.js';
197

208
function App() {
219
const [packages, setPackages] = useState<Package[]>([]);
22-
const [selections, setSelections] = useState<Record<string, any>>({});
10+
const [selections, setSelections] = useState<Record<string, string>>({});
2311
const [isSubmitting, setIsSubmitting] = useState(false);
2412
const [error, setError] = useState<string | null>(null);
2513
const [changelogs, setChangelogs] = useState<Record<string, string>>({});
@@ -61,7 +49,7 @@ function App() {
6149
});
6250
}, []);
6351

64-
const checkDependencies = async (selectionData: Record<string, any>) => {
52+
const checkDependencies = async (selectionData: Record<string, string>) => {
6553
if (Object.keys(selectionData).length === 0) return;
6654

6755
try {
@@ -145,6 +133,9 @@ function App() {
145133
setSelections((prev) => {
146134
if (value === '') {
147135
const { [packageName]: _, ...rest } = prev;
136+
const { [packageName]: __, ...remainingErrors } =
137+
packageDependencyErrors;
138+
setPackageDependencyErrors(remainingErrors);
148139
return rest;
149140
}
150141
return {
@@ -246,296 +237,20 @@ function App() {
246237

247238
<div className="space-y-4">
248239
{packages.map((pkg) => (
249-
<div
240+
<PackageItem
250241
key={pkg.name}
251-
id={`package-${pkg.name}`}
252-
className={`border p-4 rounded-lg ${
253-
selections[pkg.name] &&
254-
selections[pkg.name] !== 'intentionally-skip'
255-
? 'border-gray-500'
256-
: 'border-gray-200'
257-
} ${
258-
packageDependencyErrors[pkg.name] &&
259-
packageDependencyErrors[pkg.name].missingDependencies.length > 0
260-
? 'border-red-500'
261-
: ''
262-
}`}
263-
>
264-
<h2 className="text-xl font-semibold">{pkg.name}</h2>
265-
<div className="flex items-center justify-between">
266-
<div>
267-
<p className="text-gray-600">Current version: {pkg.version}</p>
268-
{selections[pkg.name] &&
269-
selections[pkg.name] !== 'intentionally-skip' &&
270-
selections[pkg.name] !== 'custom' &&
271-
!versionErrors[pkg.name] && (
272-
<p className="text-yellow-700">
273-
New version:{' '}
274-
{!['patch', 'minor', 'major'].includes(
275-
selections[pkg.name],
276-
)
277-
? selections[pkg.name]
278-
: new SemVer(pkg.version)
279-
.inc(
280-
selections[pkg.name] as Exclude<
281-
ReleaseType,
282-
'intentionally-skip' | 'custom' | string
283-
>,
284-
)
285-
.toString()}
286-
</p>
287-
)}
288-
{versionErrors[pkg.name] && (
289-
<p className="text-red-500 text-sm mt-1">
290-
{versionErrors[pkg.name]}
291-
</p>
292-
)}
293-
</div>
294-
<div className="flex items-center space-x-2">
295-
<select
296-
value={selections[pkg.name]}
297-
onChange={(e) =>
298-
handleSelectionChange(
299-
pkg.name,
300-
e.target.value as ReleaseType,
301-
)
302-
}
303-
className="border rounded px-2 py-1"
304-
>
305-
<option value="">Select version bump</option>
306-
<option value="major">Major</option>
307-
<option value="minor">Minor</option>
308-
<option value="patch">Patch</option>
309-
<option value="intentionally-skip">Skip</option>
310-
<option value="custom">Custom Version</option>
311-
{selections[pkg.name] &&
312-
![
313-
'major',
314-
'minor',
315-
'patch',
316-
'intentionally-skip',
317-
'custom',
318-
'',
319-
].includes(selections[pkg.name]) && (
320-
<option value={selections[pkg.name]}>
321-
Current: {selections[pkg.name]}
322-
</option>
323-
)}
324-
</select>
325-
{selections[pkg.name] === 'custom' && (
326-
<input
327-
type="text"
328-
placeholder="Enter version (e.g., 1.2.3)"
329-
onChange={(e) =>
330-
handleCustomVersionChange(pkg.name, e.target.value)
331-
}
332-
className="border rounded px-2 py-1"
333-
/>
334-
)}
335-
<button
336-
onClick={() => void fetchChangelog(pkg.name)}
337-
disabled={loadingChangelogs[pkg.name] === true}
338-
className="bg-gray-500 text-white px-3 py-1 rounded hover:bg-gray-600 disabled:bg-gray-400"
339-
>
340-
{loadingChangelogs[pkg.name]
341-
? 'Loading...'
342-
: 'View Changelog'}
343-
</button>
344-
</div>
345-
</div>
346-
347-
{packageDependencyErrors[pkg.name] && (
348-
<div className="mt-2 p-3 bg-red-50 border border-red-200 rounded">
349-
<div className="flex-grow">
350-
{packageDependencyErrors[pkg.name].missingDependencies
351-
.length > 0 && (
352-
<div className="text-red-800">
353-
<div className="flex justify-between items-center mb-2">
354-
<p className="font-semibold">Missing Dependencies:</p>
355-
<button
356-
onClick={() => {
357-
const missingDeps =
358-
packageDependencyErrors[pkg.name]
359-
.missingDependencies;
360-
setSelections((prev) => ({
361-
...prev,
362-
...missingDeps.reduce(
363-
(acc, dep) => ({
364-
...acc,
365-
[dep]: 'intentionally-skip',
366-
}),
367-
{},
368-
),
369-
}));
370-
}}
371-
className="px-3 py-1 bg-gray-500 text-white text-sm rounded hover:bg-gray-600"
372-
>
373-
Skip All
374-
</button>
375-
</div>
376-
<p className="text-sm mb-2">
377-
Please either include these packages in your release
378-
selections, or choose "Skip" if you are absolutely sure
379-
they are safe to omit:
380-
</p>
381-
<ul className="list-disc ml-4">
382-
{packageDependencyErrors[
383-
pkg.name
384-
].missingDependencies.map((dep) => (
385-
<li
386-
key={dep}
387-
className="flex justify-between items-center mb-2"
388-
>
389-
<span
390-
onClick={() => {
391-
document
392-
.getElementById(`package-${dep}`)
393-
?.scrollIntoView({ behavior: 'smooth' });
394-
}}
395-
className="cursor-pointer hover:underline"
396-
>
397-
{dep}
398-
</span>
399-
<button
400-
onClick={() =>
401-
setSelections((prev) => ({
402-
...prev,
403-
[dep]: 'intentionally-skip',
404-
}))
405-
}
406-
className="ml-4 px-2 py-0.5 text-sm bg-gray-500 text-white rounded hover:bg-gray-600"
407-
>
408-
Skip
409-
</button>
410-
</li>
411-
))}
412-
</ul>
413-
</div>
414-
)}
415-
{packageDependencyErrors[pkg.name].missingDependentNames
416-
.length > 0 && (
417-
<div className="text-red-800 mt-4">
418-
<div className="flex justify-between items-center mb-2">
419-
<p className="font-semibold">Missing Dependents:</p>
420-
<button
421-
onClick={() => {
422-
const missingDependents =
423-
packageDependencyErrors[pkg.name]
424-
.missingDependentNames;
425-
setSelections((prev) => ({
426-
...prev,
427-
...missingDependents.reduce(
428-
(acc, dep) => ({
429-
...acc,
430-
[dep]: 'intentionally-skip',
431-
}),
432-
{},
433-
),
434-
}));
435-
}}
436-
className="px-3 py-1 bg-gray-500 text-white text-sm rounded hover:bg-gray-600"
437-
>
438-
Skip All
439-
</button>
440-
</div>
441-
<p className="text-sm mb-2">
442-
Please either include these packages in your release
443-
selections, or choose "Skip" if you are absolutely sure
444-
they are safe to omit:
445-
</p>
446-
<ul className="list-disc ml-4">
447-
{packageDependencyErrors[
448-
pkg.name
449-
].missingDependentNames.map((dep) => (
450-
<li
451-
key={dep}
452-
className="flex justify-between items-center mb-2"
453-
>
454-
<span
455-
onClick={() => {
456-
document
457-
.getElementById(`package-${dep}`)
458-
?.scrollIntoView({ behavior: 'smooth' });
459-
}}
460-
className="cursor-pointer hover:underline"
461-
>
462-
{dep}
463-
</span>
464-
<button
465-
onClick={() =>
466-
setSelections((prev) => ({
467-
...prev,
468-
[dep]: 'intentionally-skip',
469-
}))
470-
}
471-
className="ml-4 px-2 py-0.5 text-sm bg-gray-500 text-white rounded hover:bg-gray-600"
472-
>
473-
Skip
474-
</button>
475-
</li>
476-
))}
477-
</ul>
478-
</div>
479-
)}
480-
</div>
481-
</div>
482-
)}
483-
484-
<div className="mt-2 space-y-2">
485-
{changelogs[pkg.name] && (
486-
<div className="mt-4 p-4 bg-gray-50 rounded-lg border">
487-
<div className="flex justify-end mb-2">
488-
<button
489-
onClick={() =>
490-
setChangelogs((prev) => ({ ...prev, [pkg.name]: '' }))
491-
}
492-
className="text-gray-500 hover:text-gray-700"
493-
>
494-
495-
</button>
496-
</div>
497-
<ReactMarkdown
498-
children={changelogs[pkg.name]}
499-
components={{
500-
h1: ({ node, ...props }) => (
501-
<h1 className="text-2xl font-bold my-4" {...props} />
502-
),
503-
h2: ({ node, ...props }) => (
504-
<h2 className="text-xl font-bold my-3" {...props} />
505-
),
506-
h3: ({ node, ...props }) => (
507-
<h3 className="text-lg font-bold my-2" {...props} />
508-
),
509-
p: ({ node, ...props }) => (
510-
<p className="my-2" {...props} />
511-
),
512-
ul: ({ node, ...props }) => (
513-
<ul className="list-disc ml-4 my-2" {...props} />
514-
),
515-
ol: ({ node, ...props }) => (
516-
<ol className="list-decimal ml-4 my-2" {...props} />
517-
),
518-
li: ({ node, ...props }) => (
519-
<li className="my-1" {...props} />
520-
),
521-
code: ({ node, ...props }) => (
522-
<code
523-
className="bg-gray-100 rounded px-1 py-0.5 text-sm font-mono"
524-
{...props}
525-
/>
526-
),
527-
pre: ({ node, ...props }) => (
528-
<pre
529-
className="bg-gray-100 rounded p-2 my-2 overflow-x-auto font-mono text-sm"
530-
{...props}
531-
/>
532-
),
533-
}}
534-
/>
535-
</div>
536-
)}
537-
</div>
538-
</div>
242+
pkg={pkg}
243+
selections={selections}
244+
versionErrors={versionErrors}
245+
packageDependencyErrors={packageDependencyErrors}
246+
loadingChangelogs={loadingChangelogs}
247+
changelogs={changelogs}
248+
onSelectionChange={handleSelectionChange}
249+
onCustomVersionChange={handleCustomVersionChange}
250+
onFetchChangelog={fetchChangelog}
251+
setSelections={setSelections}
252+
setChangelogs={setChangelogs}
253+
/>
539254
))}
540255
</div>
541256

@@ -561,24 +276,7 @@ function App() {
561276

562277
{error && <div className="text-red-600 p-4">Error: {error}</div>}
563278

564-
{submitErrors.length > 0 && (
565-
<div className="mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
566-
<h3 className="text-red-700 font-semibold mb-2">
567-
Your release spec could not be processed due to the following
568-
issues:
569-
</h3>
570-
<ul className="list-disc pl-5">
571-
{submitErrors.map((error, index) => (
572-
<li
573-
key={index}
574-
className="text-red-600 whitespace-pre-wrap font-mono mb-2"
575-
>
576-
{error}
577-
</li>
578-
))}
579-
</ul>
580-
</div>
581-
)}
279+
{submitErrors.length > 0 && <ErrorMessage errors={submitErrors} />}
582280
</div>
583281
);
584282
}

0 commit comments

Comments
 (0)