@tscircuit/fake-snippets 0.0.77 → 0.0.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/formatbot.yml +63 -0
- package/bun-tests/fake-snippets-api/routes/accounts/get.test.ts +11 -0
- package/dist/bundle.js +258 -163
- package/fake-snippets-api/lib/db/autoload-packages.json +0 -28
- package/fake-snippets-api/routes/api/accounts/get.ts +2 -1
- package/fake-snippets-api/routes/api/packages/generate_from_jlcpcb.ts +111 -0
- package/package.json +1 -1
- package/src/App.tsx +0 -3
- package/src/components/CircuitJsonImportDialog.tsx +20 -3
- package/src/components/JLCPCBImportDialog.tsx +31 -27
- package/src/components/PackageBuildsPage/LogContent.tsx +25 -9
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +5 -5
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +2 -2
- package/src/components/PackageBuildsPage/package-build-header.tsx +1 -9
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -0
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +1 -0
- package/src/components/dialogs/edit-package-details-dialog.tsx +45 -11
- package/src/components/dialogs/rename-package-dialog.tsx +5 -0
- package/src/components/package-port/CodeEditor.tsx +28 -16
- package/src/hooks/use-current-package-release.ts +6 -2
- package/src/hooks/use-package-details-form.ts +9 -1
- package/src/hooks/use-package-release.ts +16 -3
- package/src/pages/latest.tsx +29 -87
- package/src/pages/quickstart.tsx +2 -2
- package/src/pages/user-profile.tsx +50 -15
- package/src/pages/beta.tsx +0 -367
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packages": [
|
|
3
|
-
{
|
|
4
|
-
"owner": "Abse2001",
|
|
5
|
-
"name": "Arduino-Nano-Servo-Breakout"
|
|
6
|
-
},
|
|
7
|
-
{
|
|
8
|
-
"owner": "ShiboSoftwareDev",
|
|
9
|
-
"name": "Wifi-Camera-Module"
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"owner": "imrishabh18",
|
|
13
|
-
"name": "Arduino-nano"
|
|
14
|
-
},
|
|
15
3
|
{
|
|
16
4
|
"owner": "seveibar",
|
|
17
5
|
"name": "usb-c-flashlight"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"owner": "seveibar",
|
|
21
|
-
"name": "nine-key-keyboard"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"owner": "AnasSarkiz",
|
|
25
|
-
"name": "grid-of-LEDs-with-an-ESP32"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"owner": "seveibar",
|
|
29
|
-
"name": "keyboard-default60"
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"owner": "imrishabh18",
|
|
33
|
-
"name": "motor-driver-breakout"
|
|
34
6
|
}
|
|
35
7
|
]
|
|
36
8
|
}
|
|
@@ -17,7 +17,8 @@ export default withRouteSpec({
|
|
|
17
17
|
if (req.method === "POST") {
|
|
18
18
|
const { github_username } = req.jsonBody
|
|
19
19
|
account = ctx.db.accounts.find(
|
|
20
|
-
(acc: Account) =>
|
|
20
|
+
(acc: Account) =>
|
|
21
|
+
acc.github_username.toLowerCase() === github_username.toLowerCase(),
|
|
21
22
|
)
|
|
22
23
|
} else {
|
|
23
24
|
account = ctx.db.getAccount(ctx.auth.account_id)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { packageSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
import {
|
|
5
|
+
fetchEasyEDAComponent,
|
|
6
|
+
convertRawEasyEdaToTs,
|
|
7
|
+
normalizeManufacturerPartNumber,
|
|
8
|
+
EasyEdaJsonSchema,
|
|
9
|
+
} from "easyeda"
|
|
10
|
+
|
|
11
|
+
export default withRouteSpec({
|
|
12
|
+
methods: ["POST"],
|
|
13
|
+
auth: "session",
|
|
14
|
+
jsonBody: z.object({
|
|
15
|
+
jlcpcb_part_number: z.string().min(1, "JLCPCB part number is required"),
|
|
16
|
+
}),
|
|
17
|
+
jsonResponse: z.object({
|
|
18
|
+
ok: z.boolean(),
|
|
19
|
+
package: packageSchema,
|
|
20
|
+
}),
|
|
21
|
+
})(async (req, ctx) => {
|
|
22
|
+
const { jlcpcb_part_number } = req.jsonBody
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const rawEasyJson = await fetchEasyEDAComponent(jlcpcb_part_number)
|
|
26
|
+
|
|
27
|
+
if (!rawEasyJson) {
|
|
28
|
+
return ctx.error(404, {
|
|
29
|
+
error_code: "component_not_found",
|
|
30
|
+
message: `Component with JLCPCB part number ${jlcpcb_part_number} not found`,
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const betterEasy = await EasyEdaJsonSchema.parse(rawEasyJson)
|
|
35
|
+
|
|
36
|
+
const tsxComponent = await convertRawEasyEdaToTs(rawEasyJson)
|
|
37
|
+
|
|
38
|
+
const componentName = normalizeManufacturerPartNumber(
|
|
39
|
+
betterEasy.dataStr.head.c_para["Manufacturer Part"] ?? "",
|
|
40
|
+
)
|
|
41
|
+
.replace(/[^a-zA-Z0-9-_]/g, "-")
|
|
42
|
+
.replace(/--/g, "-")
|
|
43
|
+
|
|
44
|
+
const packageName = `${ctx.auth.github_username}/${componentName}`
|
|
45
|
+
|
|
46
|
+
const existingPackage = ctx.db.packages.find((p) => p.name === packageName)
|
|
47
|
+
|
|
48
|
+
if (existingPackage) {
|
|
49
|
+
return ctx.json({
|
|
50
|
+
ok: true,
|
|
51
|
+
package: existingPackage,
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const newPackage = {
|
|
56
|
+
name: packageName,
|
|
57
|
+
unscoped_name: componentName,
|
|
58
|
+
owner_name: ctx.auth.github_username,
|
|
59
|
+
code: tsxComponent,
|
|
60
|
+
created_at: new Date().toISOString(),
|
|
61
|
+
updated_at: new Date().toISOString(),
|
|
62
|
+
description: `Generated from JLCPCB part number ${jlcpcb_part_number}`,
|
|
63
|
+
creator_account_id: ctx.auth.account_id,
|
|
64
|
+
owner_org_id: ctx.auth.personal_org_id,
|
|
65
|
+
owner_github_username: ctx.auth.github_username,
|
|
66
|
+
latest_package_release_id: null,
|
|
67
|
+
latest_package_release_fs_sha: null,
|
|
68
|
+
latest_version: null,
|
|
69
|
+
license: null,
|
|
70
|
+
ai_description: "placeholder ai description",
|
|
71
|
+
ai_usage_instructions: "placeholder ai usage instructions",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const createdPackage = ctx.db.addPackage(newPackage)
|
|
75
|
+
|
|
76
|
+
const createdPackageRelease = ctx.db.addPackageRelease({
|
|
77
|
+
package_id: createdPackage.package_id,
|
|
78
|
+
version: "1.0.0",
|
|
79
|
+
created_at: new Date().toISOString(),
|
|
80
|
+
is_latest: true,
|
|
81
|
+
is_locked: false,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
ctx.db.updatePackage(createdPackage.package_id, {
|
|
85
|
+
latest_package_release_id: createdPackageRelease.package_release_id,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
ctx.db.addPackageFile({
|
|
89
|
+
package_release_id: createdPackageRelease.package_release_id,
|
|
90
|
+
file_path: "index.tsx",
|
|
91
|
+
content_text: String(tsxComponent),
|
|
92
|
+
created_at: new Date().toISOString(),
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
return ctx.json({
|
|
96
|
+
ok: true,
|
|
97
|
+
package: createdPackage as any,
|
|
98
|
+
})
|
|
99
|
+
} catch (error: any) {
|
|
100
|
+
if (String(error).includes("Component not found")) {
|
|
101
|
+
return ctx.error(404, {
|
|
102
|
+
error_code: "component_not_found",
|
|
103
|
+
message: `Component with JLCPCB part number ${jlcpcb_part_number} not found`,
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
return ctx.error(500, {
|
|
107
|
+
error_code: "package_generation_failed",
|
|
108
|
+
message: `Failed to generate package from JLCPCB part: ${error.message || String(error)}`,
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
})
|
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -66,7 +66,6 @@ const SearchPage = lazyImport(() => import("@/pages/search"))
|
|
|
66
66
|
const SettingsPage = lazyImport(() => import("@/pages/settings"))
|
|
67
67
|
const UserProfilePage = lazyImport(() => import("@/pages/user-profile"))
|
|
68
68
|
const DevLoginPage = lazyImport(() => import("@/pages/dev-login"))
|
|
69
|
-
const BetaPage = lazyImport(() => import("@/pages/beta"))
|
|
70
69
|
const ViewPackagePage = lazyImport(() => import("@/pages/view-package"))
|
|
71
70
|
const PackageBuildsPage = lazyImport(() => import("@/pages/package-builds"))
|
|
72
71
|
const TrendingPage = lazyImport(() => import("@/pages/trending"))
|
|
@@ -106,8 +105,6 @@ function App() {
|
|
|
106
105
|
<Suspense fallback={<FullPageLoader />}>
|
|
107
106
|
<Switch>
|
|
108
107
|
<Route path="/" component={LandingPage} />
|
|
109
|
-
<Route path="/beta" component={BetaPage} />
|
|
110
|
-
<Route path="/beta/:author/:packageName" component={BetaPage} />
|
|
111
108
|
<Route
|
|
112
109
|
path="/view-package/:author/:packageName"
|
|
113
110
|
component={ViewPackagePage}
|
|
@@ -174,14 +174,31 @@ export function CircuitJsonImportDialog({
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
177
|
-
if (event.ctrlKey && event.key === "Enter") {
|
|
178
|
-
|
|
177
|
+
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
|
|
178
|
+
event.preventDefault()
|
|
179
|
+
if (!isLoading && isLoggedIn && (circuitJson.trim() || file)) {
|
|
180
|
+
handleImport()
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const handleDialogKeyDown = (event: React.KeyboardEvent) => {
|
|
186
|
+
if (
|
|
187
|
+
event.key === "Enter" &&
|
|
188
|
+
!event.ctrlKey &&
|
|
189
|
+
!event.metaKey &&
|
|
190
|
+
event.target !== document.querySelector("textarea")
|
|
191
|
+
) {
|
|
192
|
+
event.preventDefault()
|
|
193
|
+
if (!isLoading && isLoggedIn && (circuitJson.trim() || file)) {
|
|
194
|
+
handleImport()
|
|
195
|
+
}
|
|
179
196
|
}
|
|
180
197
|
}
|
|
181
198
|
|
|
182
199
|
return (
|
|
183
200
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
184
|
-
<DialogContent>
|
|
201
|
+
<DialogContent onKeyDown={handleDialogKeyDown}>
|
|
185
202
|
<DialogHeader>
|
|
186
203
|
<DialogTitle>Import Circuit JSON</DialogTitle>
|
|
187
204
|
<DialogDescription>
|
|
@@ -50,31 +50,16 @@ export function JLCPCBImportDialog({
|
|
|
50
50
|
setHasBeenImportedToAccountAlready(false)
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
const existingPackageRes = await axios.get(apiUrl, {
|
|
56
|
-
validateStatus: (status) => true,
|
|
53
|
+
const response = await axios.post("/packages/generate_from_jlcpcb", {
|
|
54
|
+
jlcpcb_part_number: partNumber,
|
|
57
55
|
})
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
setHasBeenImportedToAccountAlready(true)
|
|
56
|
+
if (!response.data.ok) {
|
|
57
|
+
setError("Failed to generate package from JLCPCB part")
|
|
61
58
|
setIsLoading(false)
|
|
62
59
|
return
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
const
|
|
66
|
-
.post("/snippets/generate_from_jlcpcb", {
|
|
67
|
-
jlcpcb_part_number: partNumber,
|
|
68
|
-
})
|
|
69
|
-
.catch((e) => e)
|
|
70
|
-
|
|
71
|
-
const { snippet, error } = response.data
|
|
72
|
-
|
|
73
|
-
if (error) {
|
|
74
|
-
setError(error.message)
|
|
75
|
-
setIsLoading(false)
|
|
76
|
-
return
|
|
77
|
-
}
|
|
62
|
+
const { package: generatedPackage } = response.data
|
|
78
63
|
|
|
79
64
|
toast({
|
|
80
65
|
title: "Import Successful",
|
|
@@ -82,13 +67,21 @@ export function JLCPCBImportDialog({
|
|
|
82
67
|
})
|
|
83
68
|
|
|
84
69
|
onOpenChange(false)
|
|
85
|
-
navigate(`/editor?package_id=${
|
|
86
|
-
} catch (error) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
70
|
+
navigate(`/editor?package_id=${generatedPackage.package_id}`)
|
|
71
|
+
} catch (error: any) {
|
|
72
|
+
if (error.response?.status === 404) {
|
|
73
|
+
setError(`Component with JLCPCB part number ${partNumber} not found`)
|
|
74
|
+
} else if (error.response?.data?.message) {
|
|
75
|
+
setError(error.response.data.message)
|
|
76
|
+
} else {
|
|
77
|
+
setError("Failed to import the JLCPCB component. Please try again.")
|
|
78
|
+
toast({
|
|
79
|
+
title: "Import Failed",
|
|
80
|
+
description:
|
|
81
|
+
"Failed to import the JLCPCB component. Please try again.",
|
|
82
|
+
variant: "destructive",
|
|
83
|
+
})
|
|
84
|
+
}
|
|
92
85
|
} finally {
|
|
93
86
|
setIsLoading(false)
|
|
94
87
|
}
|
|
@@ -116,11 +109,22 @@ export function JLCPCBImportDialog({
|
|
|
116
109
|
className="mt-3"
|
|
117
110
|
placeholder="Enter JLCPCB part number (e.g., C46749)"
|
|
118
111
|
value={partNumber}
|
|
112
|
+
disabled={isLoading}
|
|
119
113
|
onChange={(e) => {
|
|
120
114
|
setPartNumber(e.target.value)
|
|
121
115
|
setError(null)
|
|
122
116
|
setHasBeenImportedToAccountAlready(false)
|
|
123
117
|
}}
|
|
118
|
+
onKeyDown={(e) => {
|
|
119
|
+
if (
|
|
120
|
+
e.key === "Enter" &&
|
|
121
|
+
!isLoading &&
|
|
122
|
+
isLoggedIn &&
|
|
123
|
+
partNumber.trim()
|
|
124
|
+
) {
|
|
125
|
+
handleImport()
|
|
126
|
+
}
|
|
127
|
+
}}
|
|
124
128
|
/>
|
|
125
129
|
{error && !hasBeenImportedToAccountAlready && (
|
|
126
130
|
<p className="bg-red-100 p-2 mt-2 pre-wrap">{error}</p>
|
|
@@ -14,17 +14,33 @@ const getErrorText = (error: ErrorObject | string) => {
|
|
|
14
14
|
export const LogContent = ({
|
|
15
15
|
logs,
|
|
16
16
|
error,
|
|
17
|
-
}: {
|
|
17
|
+
}: {
|
|
18
|
+
logs: Array<{
|
|
19
|
+
type: "info" | "success" | "error"
|
|
20
|
+
message: string
|
|
21
|
+
timestamp: string
|
|
22
|
+
}>
|
|
23
|
+
error?: ErrorObject | string | null
|
|
24
|
+
}) => {
|
|
18
25
|
return (
|
|
19
26
|
<div className="whitespace-pre-wrap font-mono text-xs">
|
|
20
|
-
{logs.map(
|
|
21
|
-
log
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
{logs.map(
|
|
28
|
+
(log, i) =>
|
|
29
|
+
log.timestamp &&
|
|
30
|
+
log.message && (
|
|
31
|
+
<div
|
|
32
|
+
key={i}
|
|
33
|
+
className={
|
|
34
|
+
log.type === "error"
|
|
35
|
+
? "text-red-600"
|
|
36
|
+
: log.type === "success"
|
|
37
|
+
? "text-green-600"
|
|
38
|
+
: "text-gray-600"
|
|
39
|
+
}
|
|
40
|
+
>
|
|
41
|
+
{new Date(log.timestamp).toLocaleTimeString()} {log.message}
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
28
44
|
)}
|
|
29
45
|
{error && <div className="text-red-600">{getErrorText(error)}</div>}
|
|
30
46
|
</div>
|
|
@@ -18,7 +18,7 @@ function computeDuration(
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export const PackageBuildDetailsPage = () => {
|
|
21
|
-
const { packageRelease } = useCurrentPackageRelease()
|
|
21
|
+
const { packageRelease } = useCurrentPackageRelease({ include_logs: true })
|
|
22
22
|
const [openSections, setOpenSections] = useState<Record<string, boolean>>({})
|
|
23
23
|
|
|
24
24
|
const {
|
|
@@ -79,8 +79,8 @@ export const PackageBuildDetailsPage = () => {
|
|
|
79
79
|
>
|
|
80
80
|
<LogContent
|
|
81
81
|
logs={
|
|
82
|
-
|
|
83
|
-
{
|
|
82
|
+
transpilation_logs ?? [
|
|
83
|
+
{ message: "No transpilation logs available" },
|
|
84
84
|
]
|
|
85
85
|
}
|
|
86
86
|
error={transpilation_error}
|
|
@@ -100,8 +100,8 @@ export const PackageBuildDetailsPage = () => {
|
|
|
100
100
|
>
|
|
101
101
|
<LogContent
|
|
102
102
|
logs={
|
|
103
|
-
|
|
104
|
-
{
|
|
103
|
+
circuit_json_build_logs ?? [
|
|
104
|
+
{ message: "No Circuit JSON logs available" },
|
|
105
105
|
]
|
|
106
106
|
}
|
|
107
107
|
error={circuit_json_build_error!}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Globe, Clock } from "lucide-react"
|
|
1
|
+
import { Globe, GitBranch, GitCommit, Clock } from "lucide-react"
|
|
2
2
|
import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
|
|
3
3
|
import { useParams } from "wouter"
|
|
4
4
|
import { timeAgo } from "@/lib/utils/timeAgo"
|
|
@@ -66,7 +66,7 @@ export function PackageBuildDetailsPanel() {
|
|
|
66
66
|
</div>
|
|
67
67
|
<span className="text-sm">{author}</span>
|
|
68
68
|
<span className="text-sm text-gray-500">
|
|
69
|
-
{timeAgo(
|
|
69
|
+
{timeAgo(created_at, "")}
|
|
70
70
|
</span>
|
|
71
71
|
</div>
|
|
72
72
|
</div>
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Github, RefreshCw } from "lucide-react"
|
|
2
2
|
import { Button } from "@/components/ui/button"
|
|
3
|
-
import {
|
|
4
|
-
DropdownMenu,
|
|
5
|
-
DropdownMenuContent,
|
|
6
|
-
DropdownMenuItem,
|
|
7
|
-
DropdownMenuTrigger,
|
|
8
|
-
} from "@/components/ui/dropdown-menu"
|
|
9
3
|
import { useParams } from "wouter"
|
|
10
|
-
import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
|
|
11
4
|
import { DownloadButtonAndMenu } from "../DownloadButtonAndMenu"
|
|
12
5
|
|
|
13
6
|
export function PackageBuildHeader() {
|
|
14
7
|
const { author, packageName } = useParams()
|
|
15
|
-
const { packageRelease } = useCurrentPackageRelease()
|
|
16
8
|
|
|
17
9
|
return (
|
|
18
10
|
<div className="border-b border-gray-200 bg-white px-6 py-4">
|
|
@@ -211,6 +211,7 @@ const MobileSidebar = ({
|
|
|
211
211
|
packageAuthor={packageInfo.owner_github_username}
|
|
212
212
|
onUpdate={handlePackageUpdate}
|
|
213
213
|
packageName={packageInfo.name}
|
|
214
|
+
unscopedPackageName={packageInfo.unscoped_name}
|
|
214
215
|
currentDefaultView={packageInfo.default_view}
|
|
215
216
|
/>
|
|
216
217
|
)}
|
|
@@ -161,6 +161,7 @@ export default function SidebarAboutSection() {
|
|
|
161
161
|
|
|
162
162
|
{packageInfo && (
|
|
163
163
|
<EditPackageDetailsDialog
|
|
164
|
+
unscopedPackageName={packageInfo.unscoped_name}
|
|
164
165
|
packageReleaseId={packageInfo.latest_package_release_id}
|
|
165
166
|
packageId={packageInfo.package_id}
|
|
166
167
|
currentDescription={
|
|
@@ -38,6 +38,7 @@ interface EditPackageDetailsDialogProps {
|
|
|
38
38
|
currentDefaultView?: string
|
|
39
39
|
isPrivate?: boolean
|
|
40
40
|
packageName: string
|
|
41
|
+
unscopedPackageName: string
|
|
41
42
|
packageReleaseId: string | null
|
|
42
43
|
packageAuthor?: string | null
|
|
43
44
|
onUpdate?: (
|
|
@@ -57,7 +58,7 @@ export const EditPackageDetailsDialog = ({
|
|
|
57
58
|
currentLicense,
|
|
58
59
|
currentDefaultView = "files",
|
|
59
60
|
isPrivate = false,
|
|
60
|
-
|
|
61
|
+
unscopedPackageName,
|
|
61
62
|
packageReleaseId,
|
|
62
63
|
packageAuthor,
|
|
63
64
|
onUpdate,
|
|
@@ -65,13 +66,11 @@ export const EditPackageDetailsDialog = ({
|
|
|
65
66
|
const axios = useAxios()
|
|
66
67
|
const { toast } = useToast()
|
|
67
68
|
const qc = useQueryClient()
|
|
68
|
-
|
|
69
69
|
const {
|
|
70
70
|
formData,
|
|
71
71
|
setFormData,
|
|
72
72
|
websiteError,
|
|
73
73
|
hasLicenseChanged,
|
|
74
|
-
hasDefaultViewChanged,
|
|
75
74
|
hasChanges,
|
|
76
75
|
isFormValid,
|
|
77
76
|
} = usePackageDetailsForm({
|
|
@@ -79,6 +78,7 @@ export const EditPackageDetailsDialog = ({
|
|
|
79
78
|
initialWebsite: currentWebsite,
|
|
80
79
|
initialLicense: currentLicense || null,
|
|
81
80
|
initialDefaultView: currentDefaultView,
|
|
81
|
+
initialUnscopedPackageName: unscopedPackageName,
|
|
82
82
|
isDialogOpen: open,
|
|
83
83
|
initialVisibility: isPrivate ? "private" : "public",
|
|
84
84
|
})
|
|
@@ -114,16 +114,15 @@ export const EditPackageDetailsDialog = ({
|
|
|
114
114
|
website: formData.website,
|
|
115
115
|
is_private: formData.visibility == "private",
|
|
116
116
|
default_view: formData.defaultView,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
is_private: formData.visibility === "private",
|
|
117
|
+
...(formData.unscopedPackageName !== unscopedPackageName && {
|
|
118
|
+
name: formData.unscopedPackageName,
|
|
119
|
+
}),
|
|
121
120
|
})
|
|
122
121
|
if (response.status !== 200)
|
|
123
122
|
throw new Error("Failed to update package details")
|
|
124
123
|
|
|
125
124
|
const filesRes = await axios.post("/package_files/list", {
|
|
126
|
-
package_name_with_version:
|
|
125
|
+
package_name_with_version: `${packageAuthor}/${formData.unscopedPackageName}`,
|
|
127
126
|
})
|
|
128
127
|
const packageFiles: string[] =
|
|
129
128
|
filesRes.status === 200
|
|
@@ -137,18 +136,32 @@ export const EditPackageDetailsDialog = ({
|
|
|
137
136
|
if (hasLicenseChanged) {
|
|
138
137
|
if (packageFiles.includes("LICENSE") && !licenseContent) {
|
|
139
138
|
await axios.post("/package_files/delete", {
|
|
140
|
-
package_name_with_version:
|
|
139
|
+
package_name_with_version: `${packageAuthor}/${formData.unscopedPackageName}`,
|
|
141
140
|
file_path: "LICENSE",
|
|
142
141
|
})
|
|
143
142
|
}
|
|
144
143
|
if (licenseContent) {
|
|
145
144
|
await axios.post("/package_files/create_or_update", {
|
|
146
|
-
package_name_with_version:
|
|
145
|
+
package_name_with_version: `${packageAuthor}/${formData.unscopedPackageName}`,
|
|
147
146
|
file_path: "LICENSE",
|
|
148
147
|
content_text: licenseContent,
|
|
149
148
|
})
|
|
150
149
|
}
|
|
151
150
|
}
|
|
151
|
+
console.log(
|
|
152
|
+
"formData.unscopedPackageName",
|
|
153
|
+
formData.unscopedPackageName,
|
|
154
|
+
"unscopedPackageName",
|
|
155
|
+
unscopedPackageName,
|
|
156
|
+
)
|
|
157
|
+
if (formData.unscopedPackageName !== unscopedPackageName) {
|
|
158
|
+
// Use router for client-side navigation
|
|
159
|
+
window.history.replaceState(
|
|
160
|
+
{},
|
|
161
|
+
"",
|
|
162
|
+
`/${packageAuthor}/${formData.unscopedPackageName}`,
|
|
163
|
+
)
|
|
164
|
+
}
|
|
152
165
|
|
|
153
166
|
return {
|
|
154
167
|
description: formData.description,
|
|
@@ -187,7 +200,9 @@ export const EditPackageDetailsDialog = ({
|
|
|
187
200
|
qc.setQueryData(["packages", packageId], context?.previous)
|
|
188
201
|
toast({
|
|
189
202
|
title: "Error",
|
|
190
|
-
description:
|
|
203
|
+
description:
|
|
204
|
+
(error as any)?.data?.error?.message ||
|
|
205
|
+
"Failed to update package details. Please try again.",
|
|
191
206
|
variant: "destructive",
|
|
192
207
|
})
|
|
193
208
|
},
|
|
@@ -237,11 +252,29 @@ export const EditPackageDetailsDialog = ({
|
|
|
237
252
|
</DialogHeader>
|
|
238
253
|
<div className="">
|
|
239
254
|
<div className="grid gap-2">
|
|
255
|
+
<div className="space-y-1">
|
|
256
|
+
<Label htmlFor="packageName">Package Name</Label>
|
|
257
|
+
<Input
|
|
258
|
+
id="packageName"
|
|
259
|
+
value={formData.unscopedPackageName}
|
|
260
|
+
onChange={(e) =>
|
|
261
|
+
setFormData((prev) => ({
|
|
262
|
+
...prev,
|
|
263
|
+
unscopedPackageName: e.target.value,
|
|
264
|
+
}))
|
|
265
|
+
}
|
|
266
|
+
placeholder="Enter package name"
|
|
267
|
+
disabled={updatePackageDetailsMutation.isLoading}
|
|
268
|
+
className="w-full"
|
|
269
|
+
autoComplete="off"
|
|
270
|
+
/>
|
|
271
|
+
</div>
|
|
240
272
|
<div className="space-y-1">
|
|
241
273
|
<Label htmlFor="website">Website</Label>
|
|
242
274
|
<Input
|
|
243
275
|
id="website"
|
|
244
276
|
value={formData.website}
|
|
277
|
+
autoComplete="off"
|
|
245
278
|
onChange={(e) =>
|
|
246
279
|
setFormData((prev) => ({
|
|
247
280
|
...prev,
|
|
@@ -282,6 +315,7 @@ export const EditPackageDetailsDialog = ({
|
|
|
282
315
|
<Label htmlFor="description">Description</Label>
|
|
283
316
|
<Textarea
|
|
284
317
|
id="description"
|
|
318
|
+
spellCheck={false}
|
|
285
319
|
value={formData.description}
|
|
286
320
|
onChange={(e) =>
|
|
287
321
|
setFormData((prev) => ({
|
|
@@ -66,6 +66,11 @@ export const RenamePackageDialog = ({
|
|
|
66
66
|
onChange={(e) => setNewName(e.target.value.replace(" ", "").trim())}
|
|
67
67
|
placeholder="Enter new name"
|
|
68
68
|
disabled={renamePackageMutation.isLoading}
|
|
69
|
+
onKeyDown={(e) => {
|
|
70
|
+
if (e.key === "Enter" && !renamePackageMutation.isLoading) {
|
|
71
|
+
renamePackageMutation.mutate()
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
69
74
|
/>
|
|
70
75
|
<Button
|
|
71
76
|
disabled={renamePackageMutation.isLoading}
|