@tscircuit/fake-snippets 0.0.53 → 0.0.55
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/bun-tests/fake-snippets-api/routes/{orders/order-with-circuit-json-or-package-release.test.ts → order_quotes/create.test.ts} +4 -4
- package/dist/bundle.js +7 -10
- package/fake-snippets-api/routes/api/order_quotes/create.ts +7 -10
- package/package.json +1 -1
- package/src/components/Header2.tsx +1 -1
- package/src/components/SearchComponent.tsx +5 -1
- package/src/components/ViewPackagePage/components/markdown-viewer.tsx +40 -23
- package/src/components/package-port/CodeAndPreview.tsx +1 -1
- package/src/components/package-port/EditorNav.tsx +3 -2
- package/src/index.css +14 -0
- package/src/lib/handleManualEditsImportWithSupportForMultipleFiles.ts +0 -4
- package/src/lib/posthog.ts +10 -5
- package/src/pages/dashboard.tsx +147 -66
- package/src/pages/user-profile.tsx +16 -5
|
@@ -32,17 +32,14 @@ export default withRouteSpec({
|
|
|
32
32
|
error: z.string().optional(),
|
|
33
33
|
}),
|
|
34
34
|
})(async (req, ctx) => {
|
|
35
|
-
const { package_release_id, vendor_name } = req.jsonBody
|
|
35
|
+
const { package_release_id, vendor_name, circuit_json } = req.jsonBody
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
},
|
|
44
|
-
{ status: 404 },
|
|
45
|
-
)
|
|
37
|
+
if (package_release_id) {
|
|
38
|
+
// check package release exists
|
|
39
|
+
const packageRelease = ctx.db.getPackageReleaseById(package_release_id)
|
|
40
|
+
if (!packageRelease) {
|
|
41
|
+
return ctx.json({ error: "Package release not found" }, { status: 404 })
|
|
42
|
+
}
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
const orderQuoteId = ctx.db.addOrderQuote({
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import { PrefetchPageLink } from "./PrefetchPageLink"
|
|
|
9
9
|
|
|
10
10
|
interface SearchComponentProps {
|
|
11
11
|
onResultsFetched?: (results: any[]) => void // optional
|
|
12
|
+
autofocus?: boolean
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const LinkWithNewTabHandling = ({
|
|
@@ -43,6 +44,7 @@ const LinkWithNewTabHandling = ({
|
|
|
43
44
|
|
|
44
45
|
const SearchComponent: React.FC<SearchComponentProps> = ({
|
|
45
46
|
onResultsFetched,
|
|
47
|
+
autofocus = false,
|
|
46
48
|
}) => {
|
|
47
49
|
const [searchQuery, setSearchQuery] = useState("")
|
|
48
50
|
const [showResults, setShowResults] = useState(false)
|
|
@@ -74,7 +76,9 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
|
|
|
74
76
|
|
|
75
77
|
// Focus input on mount
|
|
76
78
|
useEffect(() => {
|
|
77
|
-
|
|
79
|
+
if (autofocus) {
|
|
80
|
+
inputRef.current?.focus()
|
|
81
|
+
}
|
|
78
82
|
}, [])
|
|
79
83
|
|
|
80
84
|
useEffect(() => {
|
|
@@ -1,37 +1,54 @@
|
|
|
1
|
+
import React, { useEffect } from "react"
|
|
1
2
|
import Markdown from "react-markdown"
|
|
2
3
|
import remarkGfm from "remark-gfm"
|
|
3
4
|
|
|
5
|
+
import { useShikiHighlighter } from "@/hooks/use-shiki-highlighter"
|
|
6
|
+
|
|
4
7
|
export default function MarkdownViewer({
|
|
5
8
|
markdownContent,
|
|
6
9
|
}: {
|
|
7
10
|
markdownContent: string
|
|
8
11
|
}) {
|
|
12
|
+
const { highlighter } = useShikiHighlighter()
|
|
9
13
|
return (
|
|
10
|
-
<div className="
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
<div className="markdown-code">
|
|
15
|
+
<div className="prose dark:prose-invert prose-pre:py-0 prose-pre:px-6 prose-pre:bg-white dark:prose-pre:bg-gray-800 prose-code:font-mono markdown-content">
|
|
16
|
+
<Markdown
|
|
17
|
+
remarkPlugins={[remarkGfm]}
|
|
18
|
+
components={{
|
|
19
|
+
code({ node, className, children, ...props }) {
|
|
20
|
+
const isCodeBlock =
|
|
21
|
+
className?.includes("language-") || /\n/.test(String(children))
|
|
22
|
+
const dom = document.createElement("div")
|
|
23
|
+
if (highlighter) {
|
|
24
|
+
dom.innerHTML = highlighter.codeToHtml(
|
|
25
|
+
children?.toString() || "",
|
|
26
|
+
{
|
|
27
|
+
lang: "tsx",
|
|
28
|
+
themes: {
|
|
29
|
+
light: "github-light",
|
|
30
|
+
dark: "github-dark",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
// Don’t use <code> tags (they leave backticks intact)
|
|
36
|
+
return isCodeBlock ? (
|
|
37
|
+
<div
|
|
38
|
+
dangerouslySetInnerHTML={{ __html: dom.innerHTML }}
|
|
39
|
+
className="border rounded-lg"
|
|
40
|
+
></div>
|
|
41
|
+
) : (
|
|
42
|
+
<span className="bg-gray-100 dark:bg-gray-800 text-gray-800 font-semibold font-mono dark:text-gray-200 px-1 py-0.5 rounded">
|
|
22
43
|
{children}
|
|
23
44
|
</span>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}}
|
|
32
|
-
>
|
|
33
|
-
{markdownContent}
|
|
34
|
-
</Markdown>
|
|
45
|
+
)
|
|
46
|
+
},
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
{markdownContent}
|
|
50
|
+
</Markdown>
|
|
51
|
+
</div>
|
|
35
52
|
</div>
|
|
36
53
|
)
|
|
37
54
|
}
|
|
@@ -321,7 +321,7 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
321
321
|
state.pkgFilesWithContent,
|
|
322
322
|
])
|
|
323
323
|
|
|
324
|
-
if ((!pkg && urlParams.package_id) || pkgFiles.isLoading) {
|
|
324
|
+
if ((!pkg && urlParams.package_id) || pkgFiles.isLoading || isLoadingFiles) {
|
|
325
325
|
return (
|
|
326
326
|
<div className="flex items-center justify-center h-64">
|
|
327
327
|
<div className="flex flex-col items-center justify-center">
|
|
@@ -187,7 +187,8 @@ export default function EditorNav({
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
const canSavePackage = Boolean(
|
|
190
|
-
isLoggedIn &&
|
|
190
|
+
isLoggedIn &&
|
|
191
|
+
(!pkg || pkg?.owner_github_username === session?.github_username),
|
|
191
192
|
)
|
|
192
193
|
return (
|
|
193
194
|
<nav className="lg:flex w-screen items-center justify-between px-2 py-3 border-b border-gray-200 bg-white text-sm border-t">
|
|
@@ -250,7 +251,7 @@ export default function EditorNav({
|
|
|
250
251
|
variant="outline"
|
|
251
252
|
size="sm"
|
|
252
253
|
className={"ml-1 h-6 px-2 text-xs save-button"}
|
|
253
|
-
disabled={canSavePackage ? !hasUnsavedChanges : !isLoggedIn}
|
|
254
|
+
disabled={canSavePackage && pkg ? !hasUnsavedChanges : !isLoggedIn}
|
|
254
255
|
onClick={canSavePackage ? onSave : () => forkSnippet()}
|
|
255
256
|
>
|
|
256
257
|
{canSavePackage ? (
|
package/src/index.css
CHANGED
|
@@ -40,3 +40,17 @@
|
|
|
40
40
|
text-align: right;
|
|
41
41
|
color: rgba(115, 138, 148, 0.4);
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
.markdown-code .shiki .line::before {
|
|
45
|
+
content: none !important;
|
|
46
|
+
display: none !important;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.markdown-code .shiki .line {
|
|
50
|
+
padding-left: 0 !important;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.markdown-code pre {
|
|
54
|
+
padding: 12px !important;
|
|
55
|
+
margin: 0 !important;
|
|
56
|
+
}
|
|
@@ -10,10 +10,6 @@ export const handleManualEditsImportWithSupportForMultipleFiles = (
|
|
|
10
10
|
variant?: "default" | "destructive"
|
|
11
11
|
}) => void,
|
|
12
12
|
) => {
|
|
13
|
-
console.log(
|
|
14
|
-
"handleManualEditsImportWithSupportForMultipleFiles",
|
|
15
|
-
entrypointFileName,
|
|
16
|
-
)
|
|
17
13
|
try {
|
|
18
14
|
let currentContent = files[entrypointFileName]
|
|
19
15
|
const importRegex =
|
package/src/lib/posthog.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import posthog from "posthog-js"
|
|
2
2
|
|
|
3
|
-
if (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
if (
|
|
4
|
+
!window.location.hostname.includes("localhost") &&
|
|
5
|
+
!window.location.hostname.includes("127.0.0.1")
|
|
6
|
+
) {
|
|
7
|
+
if (!posthog.__loaded) {
|
|
8
|
+
posthog.init("phc_htd8AQjSfVEsFCLQMAiUooG4Q0DKBCjqYuQglc9V3Wo", {
|
|
9
|
+
api_host: "https://postpig.tscircuit.com",
|
|
10
|
+
person_profiles: "always",
|
|
11
|
+
})
|
|
12
|
+
}
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
export { posthog }
|
package/src/pages/dashboard.tsx
CHANGED
|
@@ -6,32 +6,57 @@ import Footer from "@/components/Footer"
|
|
|
6
6
|
import { Snippet } from "fake-snippets-api/lib/db/schema"
|
|
7
7
|
import { Link } from "wouter"
|
|
8
8
|
import { CreateNewSnippetWithAiHero } from "@/components/CreateNewSnippetWithAiHero"
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Edit2,
|
|
11
|
+
Star,
|
|
12
|
+
ChevronDown,
|
|
13
|
+
ChevronUp,
|
|
14
|
+
Key,
|
|
15
|
+
KeyRound,
|
|
16
|
+
} from "lucide-react"
|
|
10
17
|
import { Button } from "@/components/ui/button"
|
|
11
18
|
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
12
19
|
import { PrefetchPageLink } from "@/components/PrefetchPageLink"
|
|
13
20
|
import { SnippetList } from "@/components/SnippetList"
|
|
14
21
|
import { Helmet } from "react-helmet-async"
|
|
22
|
+
import { useSignIn } from "@/hooks/use-sign-in"
|
|
23
|
+
import { SnippetCard } from "@/components/SnippetCard"
|
|
24
|
+
import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
|
|
25
|
+
import { useConfirmDeletePackageDialog } from "@/components/dialogs/confirm-delete-package-dialog"
|
|
15
26
|
|
|
16
27
|
export const DashboardPage = () => {
|
|
17
28
|
const axios = useAxios()
|
|
18
29
|
|
|
19
30
|
const currentUser = useGlobalStore((s) => s.session?.github_username)
|
|
31
|
+
const isLoggedIn = Boolean(currentUser)
|
|
32
|
+
const signIn = useSignIn()
|
|
33
|
+
|
|
20
34
|
const [showAllTrending, setShowAllTrending] = useState(false)
|
|
21
35
|
const [showAllLatest, setShowAllLatest] = useState(false)
|
|
36
|
+
const [snippetToDelete, setSnippetToDelete] = useState<Snippet | null>(null)
|
|
37
|
+
const { Dialog: DeleteDialog, openDialog: openDeleteDialog } =
|
|
38
|
+
useConfirmDeletePackageDialog()
|
|
22
39
|
|
|
23
40
|
const {
|
|
24
41
|
data: mySnippets,
|
|
25
42
|
isLoading,
|
|
26
43
|
error,
|
|
27
|
-
} = useQuery<Snippet[]>(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
} = useQuery<Snippet[]>(
|
|
45
|
+
"userSnippets",
|
|
46
|
+
async () => {
|
|
47
|
+
const response = await axios.get(
|
|
48
|
+
`/snippets/list?owner_name=${currentUser}`,
|
|
49
|
+
)
|
|
50
|
+
return response.data.snippets.sort(
|
|
51
|
+
(a: any, b: any) =>
|
|
52
|
+
new Date(b.updated_at || b.created_at).getTime() -
|
|
53
|
+
new Date(a.updated_at || a.created_at).getTime(),
|
|
54
|
+
)
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
enabled: isLoggedIn,
|
|
58
|
+
},
|
|
59
|
+
)
|
|
35
60
|
|
|
36
61
|
const { data: trendingSnippets } = useQuery<Snippet[]>(
|
|
37
62
|
"trendingSnippets",
|
|
@@ -49,6 +74,13 @@ export const DashboardPage = () => {
|
|
|
49
74
|
},
|
|
50
75
|
)
|
|
51
76
|
|
|
77
|
+
const baseUrl = useSnippetsBaseApiUrl()
|
|
78
|
+
|
|
79
|
+
const handleDeleteClick = (e: React.MouseEvent, snippet: Snippet) => {
|
|
80
|
+
e.preventDefault() // Prevent navigation
|
|
81
|
+
setSnippetToDelete(snippet)
|
|
82
|
+
openDeleteDialog()
|
|
83
|
+
}
|
|
52
84
|
return (
|
|
53
85
|
<div>
|
|
54
86
|
<Helmet>
|
|
@@ -59,77 +91,120 @@ export const DashboardPage = () => {
|
|
|
59
91
|
<h1 className="text-3xl font-bold mb-6">Dashboard</h1>
|
|
60
92
|
<div className="flex md:flex-row flex-col">
|
|
61
93
|
<div className="md:w-3/4 p-0 md:pr-6">
|
|
62
|
-
|
|
63
|
-
<div className="flex items-center">
|
|
64
|
-
<
|
|
65
|
-
|
|
94
|
+
{!isLoggedIn ? (
|
|
95
|
+
<div className="flex flex-col items-center justify-center h-64 rounded-md p-4 mt-[40px] mb-2 sm:mb-4">
|
|
96
|
+
<div className="p-4 mb-4 rounded-full bg-blue-50 border border-blue-100 shadow-sm">
|
|
97
|
+
<KeyRound className="text-blue-500" size={32} />
|
|
98
|
+
</div>
|
|
99
|
+
<h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-3">
|
|
100
|
+
You're not logged in
|
|
101
|
+
</h2>
|
|
102
|
+
|
|
103
|
+
<p className="text-gray-600 mb-6 text-center max-w-md text-sm sm:text-base">
|
|
104
|
+
Log in to access your dashboard and manage your snippets.
|
|
105
|
+
</p>
|
|
106
|
+
<Button onClick={() => signIn()} variant="outline">
|
|
107
|
+
Log in
|
|
108
|
+
</Button>
|
|
109
|
+
</div>
|
|
110
|
+
) : (
|
|
111
|
+
<>
|
|
112
|
+
<div className="mt-6 mb-4">
|
|
113
|
+
<div className="flex items-center">
|
|
114
|
+
<h2 className="text-sm text-gray-600 whitespace-nowrap">
|
|
115
|
+
Edit Recent
|
|
116
|
+
</h2>
|
|
117
|
+
<div className="flex gap-2 items-center overflow-x-scroll md:overflow-hidden">
|
|
118
|
+
{mySnippets &&
|
|
119
|
+
mySnippets.slice(0, 3).map((snippet) => (
|
|
120
|
+
<div key={snippet.snippet_id}>
|
|
121
|
+
<PrefetchPageLink
|
|
122
|
+
href={`/editor?snippet_id=${snippet.snippet_id}`}
|
|
123
|
+
className="text-blue-600 hover:underline"
|
|
124
|
+
>
|
|
125
|
+
<Button
|
|
126
|
+
variant="ghost"
|
|
127
|
+
size="sm"
|
|
128
|
+
className="font-medium"
|
|
129
|
+
>
|
|
130
|
+
{snippet.unscoped_name}
|
|
131
|
+
<Edit2 className="w-3 h-3 ml-2" />
|
|
132
|
+
</Button>
|
|
133
|
+
</PrefetchPageLink>
|
|
134
|
+
</div>
|
|
135
|
+
))}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
{/* <CreateNewSnippetWithAiHero/> */}
|
|
140
|
+
<h2 className="text-sm font-bold mb-2 text-gray-700 border-b border-gray-200">
|
|
141
|
+
Your Recent Packages
|
|
66
142
|
</h2>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<div
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
>
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
className="
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
143
|
+
{isLoading && (
|
|
144
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
145
|
+
{[...Array(6)].map((_, i) => (
|
|
146
|
+
<div
|
|
147
|
+
key={i}
|
|
148
|
+
className="border p-4 rounded-md animate-pulse"
|
|
149
|
+
>
|
|
150
|
+
<div className="flex items-start gap-4">
|
|
151
|
+
<div className="h-16 w-16 flex-shrink-0 rounded-md bg-slate-200"></div>
|
|
152
|
+
<div className="flex-1">
|
|
153
|
+
<div className="h-5 bg-slate-200 rounded w-3/4 mb-2"></div>
|
|
154
|
+
<div className="h-4 bg-slate-200 rounded w-1/2 mb-2"></div>
|
|
155
|
+
<div className="flex gap-2">
|
|
156
|
+
<div className="h-3 bg-slate-200 rounded w-16"></div>
|
|
157
|
+
<div className="h-3 bg-slate-200 rounded w-16"></div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
84
161
|
</div>
|
|
85
162
|
))}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<span className="text-xs text-gray-500">
|
|
108
|
-
{new Date(snippet.created_at).toLocaleDateString()}
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
165
|
+
{mySnippets && mySnippets.length > 0 ? (
|
|
166
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
167
|
+
{mySnippets.slice(0, 10).map((snippet) => (
|
|
168
|
+
<SnippetCard
|
|
169
|
+
key={snippet.snippet_id}
|
|
170
|
+
snippet={snippet}
|
|
171
|
+
baseUrl={baseUrl}
|
|
172
|
+
isCurrentUserSnippet={
|
|
173
|
+
snippet.owner_name === currentUser
|
|
174
|
+
}
|
|
175
|
+
onDeleteClick={handleDeleteClick}
|
|
176
|
+
/>
|
|
177
|
+
))}
|
|
178
|
+
</div>
|
|
179
|
+
) : (
|
|
180
|
+
!isLoading &&
|
|
181
|
+
mySnippets?.length === 0 && (
|
|
182
|
+
<span className="font-medium text-sm text-gray-500">
|
|
183
|
+
No packages found
|
|
109
184
|
</span>
|
|
110
|
-
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
185
|
+
)
|
|
186
|
+
)}
|
|
187
|
+
{mySnippets && mySnippets.length > 10 && (
|
|
188
|
+
<Link
|
|
189
|
+
href={`/${currentUser}`}
|
|
190
|
+
className="text-sm text-blue-600 hover:underline mt-2 inline-block"
|
|
191
|
+
>
|
|
192
|
+
View all packages
|
|
193
|
+
</Link>
|
|
194
|
+
)}
|
|
195
|
+
</>
|
|
121
196
|
)}
|
|
122
197
|
</div>
|
|
123
198
|
<div className="md:w-1/4">
|
|
124
199
|
<SnippetList
|
|
125
|
-
title="Trending
|
|
200
|
+
title="Trending Packages"
|
|
126
201
|
snippets={trendingSnippets}
|
|
127
202
|
showAll={showAllTrending}
|
|
128
203
|
onToggleShowAll={() => setShowAllTrending(!showAllTrending)}
|
|
129
204
|
/>
|
|
130
205
|
<div className="mt-8">
|
|
131
206
|
<SnippetList
|
|
132
|
-
title="Latest
|
|
207
|
+
title="Latest Packages"
|
|
133
208
|
snippets={latestSnippets}
|
|
134
209
|
showAll={showAllLatest}
|
|
135
210
|
onToggleShowAll={() => setShowAllLatest(!showAllLatest)}
|
|
@@ -137,6 +212,12 @@ export const DashboardPage = () => {
|
|
|
137
212
|
</div>
|
|
138
213
|
</div>
|
|
139
214
|
</div>
|
|
215
|
+
{snippetToDelete && (
|
|
216
|
+
<DeleteDialog
|
|
217
|
+
packageId={snippetToDelete.snippet_id}
|
|
218
|
+
packageName={snippetToDelete.unscoped_name}
|
|
219
|
+
/>
|
|
220
|
+
)}
|
|
140
221
|
</div>
|
|
141
222
|
<Footer />
|
|
142
223
|
</div>
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
SelectTrigger,
|
|
23
23
|
SelectValue,
|
|
24
24
|
} from "@/components/ui/select"
|
|
25
|
-
import { Star } from "lucide-react"
|
|
25
|
+
import { Box, Star } from "lucide-react"
|
|
26
26
|
|
|
27
27
|
export const UserProfilePage = () => {
|
|
28
28
|
const { username } = useParams()
|
|
@@ -188,10 +188,21 @@ export const UserProfilePage = () => {
|
|
|
188
188
|
) : (
|
|
189
189
|
<div className="col-span-full flex justify-center">
|
|
190
190
|
<div className="flex flex-col items-center py-12 text-gray-500">
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
{activeTab === "starred" ? (
|
|
192
|
+
<>
|
|
193
|
+
<Star />
|
|
194
|
+
<span className="text-lg font-medium">
|
|
195
|
+
No starred packages
|
|
196
|
+
</span>
|
|
197
|
+
</>
|
|
198
|
+
) : (
|
|
199
|
+
<>
|
|
200
|
+
<Box />
|
|
201
|
+
<span className="text-lg font-medium">
|
|
202
|
+
No packages available
|
|
203
|
+
</span>
|
|
204
|
+
</>
|
|
205
|
+
)}
|
|
195
206
|
</div>
|
|
196
207
|
</div>
|
|
197
208
|
)}
|