@medialane/ui 0.286.0 → 0.288.0

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.
Files changed (53) hide show
  1. package/dist/components/badge.d.cts +1 -1
  2. package/dist/components/badge.d.ts +1 -1
  3. package/dist/components/button.d.cts +1 -1
  4. package/dist/components/button.d.ts +1 -1
  5. package/dist/components/collection-settings.cjs +350 -0
  6. package/dist/components/collection-settings.cjs.map +1 -0
  7. package/dist/components/collection-settings.d.cts +7 -0
  8. package/dist/components/collection-settings.d.ts +7 -0
  9. package/dist/components/collection-settings.js +335 -0
  10. package/dist/components/collection-settings.js.map +1 -0
  11. package/dist/components/collection-slug-claim.cjs +219 -0
  12. package/dist/components/collection-slug-claim.cjs.map +1 -0
  13. package/dist/components/collection-slug-claim.d.cts +10 -0
  14. package/dist/components/collection-slug-claim.d.ts +10 -0
  15. package/dist/components/collection-slug-claim.js +185 -0
  16. package/dist/components/collection-slug-claim.js.map +1 -0
  17. package/dist/components/launchpad/edition-mint-schema.d.cts +2 -2
  18. package/dist/components/launchpad/edition-mint-schema.d.ts +2 -2
  19. package/dist/components/medialane-app.cjs.map +1 -1
  20. package/dist/components/medialane-app.d.cts +1 -0
  21. package/dist/components/medialane-app.d.ts +1 -0
  22. package/dist/components/medialane-app.js.map +1 -1
  23. package/dist/components/mint-landing.cjs +232 -0
  24. package/dist/components/mint-landing.cjs.map +1 -0
  25. package/dist/components/mint-landing.d.cts +77 -0
  26. package/dist/components/mint-landing.d.ts +77 -0
  27. package/dist/components/mint-landing.js +212 -0
  28. package/dist/components/mint-landing.js.map +1 -0
  29. package/dist/components/settings-section.cjs +65 -0
  30. package/dist/components/settings-section.cjs.map +1 -0
  31. package/dist/components/settings-section.d.cts +24 -0
  32. package/dist/components/settings-section.d.ts +24 -0
  33. package/dist/components/settings-section.js +38 -0
  34. package/dist/components/settings-section.js.map +1 -0
  35. package/dist/index.cjs +6 -0
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +2 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +4 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/utils/collection-settings-form.cjs +77 -0
  42. package/dist/utils/collection-settings-form.cjs.map +1 -0
  43. package/dist/utils/collection-settings-form.d.cts +21 -0
  44. package/dist/utils/collection-settings-form.d.ts +21 -0
  45. package/dist/utils/collection-settings-form.js +51 -0
  46. package/dist/utils/collection-settings-form.js.map +1 -0
  47. package/dist/utils/use-collection-settings.cjs +83 -0
  48. package/dist/utils/use-collection-settings.cjs.map +1 -0
  49. package/dist/utils/use-collection-settings.d.cts +58 -0
  50. package/dist/utils/use-collection-settings.d.ts +58 -0
  51. package/dist/utils/use-collection-settings.js +59 -0
  52. package/dist/utils/use-collection-settings.js.map +1 -0
  53. package/package.json +1 -1
@@ -0,0 +1,335 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ User,
5
+ Image as ImageIcon,
6
+ Globe,
7
+ Twitter,
8
+ MessageCircle,
9
+ Send,
10
+ Lock,
11
+ Unlock,
12
+ Sparkles,
13
+ ShieldCheck,
14
+ CheckCircle2,
15
+ ExternalLink,
16
+ Video,
17
+ Music,
18
+ Radio,
19
+ FileText,
20
+ Link2,
21
+ Info
22
+ } from "lucide-react";
23
+ import { collectionHref } from "@medialane/sdk";
24
+ import { Button } from "./button.js";
25
+ import { Input } from "./input.js";
26
+ import { Label } from "./label.js";
27
+ import { Textarea } from "./textarea.js";
28
+ import { Switch } from "./switch.js";
29
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
30
+ import { Badge } from "./badge.js";
31
+ import Link from "./link.js";
32
+ import { cn } from "../utils/cn.js";
33
+ import { SectionCard, SectionHeader, FieldRow, HelperText } from "./settings-section.js";
34
+ import { CollectionSlugClaimSection } from "./collection-slug-claim.js";
35
+ import { useCollectionSettings } from "../utils/use-collection-settings.js";
36
+ const DESC_MAX = 500;
37
+ const CONTENT_TYPES = [
38
+ { value: "VIDEO", label: "Video", icon: Video, hint: "YouTube, Vimeo, or any video URL" },
39
+ { value: "AUDIO", label: "Audio", icon: Music, hint: "Spotify, SoundCloud, MP3 link\u2026" },
40
+ { value: "STREAM", label: "Live stream", icon: Radio, hint: "Twitch, YouTube Live, etc." },
41
+ { value: "DOCUMENT", label: "Document", icon: FileText, hint: "PDF, Notion, Google Doc\u2026" },
42
+ { value: "LINK", label: "Link", icon: Link2, hint: "Any URL" }
43
+ ];
44
+ function CollectionSettings({ contract }) {
45
+ const {
46
+ collection,
47
+ profile,
48
+ collectionLoading,
49
+ isLoading,
50
+ isOwner,
51
+ form,
52
+ set,
53
+ update,
54
+ saving,
55
+ saveStatus,
56
+ saveError,
57
+ save
58
+ } = useCollectionSettings(contract);
59
+ const descLen = form.description.length;
60
+ if (!collectionLoading && collection && !isOwner) {
61
+ return /* @__PURE__ */ jsxs("div", { className: "py-12 flex flex-col items-center gap-3 text-center", children: [
62
+ /* @__PURE__ */ jsx("div", { className: "h-12 w-12 rounded-full bg-muted flex items-center justify-center", children: /* @__PURE__ */ jsx(Lock, { className: "h-5 w-5 text-muted-foreground" }) }),
63
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Owner access only" }),
64
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground max-w-xs", children: "Only the collection owner can access settings. If you believe this is yours, visit your portfolio to claim it." }),
65
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", asChild: true, children: /* @__PURE__ */ jsx(Link, { href: "/portfolio/collections", children: "Go to my collections" }) })
66
+ ] });
67
+ }
68
+ const selectedContentType = CONTENT_TYPES.find((t) => t.value === form.gatedContentType);
69
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6 max-w-2xl pb-16", children: [
70
+ /* @__PURE__ */ jsxs("div", { children: [
71
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: "Collection Settings" }),
72
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Customize how your collection appears on Medialane and manage exclusive content for your community." }),
73
+ collection && /* @__PURE__ */ jsxs(
74
+ Link,
75
+ {
76
+ href: collectionHref("STARKNET", contract),
77
+ className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground mt-2 transition-colors",
78
+ children: [
79
+ /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3" }),
80
+ "View collection page"
81
+ ]
82
+ }
83
+ )
84
+ ] }),
85
+ /* @__PURE__ */ jsxs(SectionCard, { id: "identity", children: [
86
+ /* @__PURE__ */ jsx(
87
+ SectionHeader,
88
+ {
89
+ icon: User,
90
+ title: "Identity",
91
+ description: "Your collection's public name and description. A complete profile builds trust with collectors.",
92
+ badge: profile?.displayName ? /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: "text-2xs h-4 px-1.5 gap-1 text-emerald-500 border-emerald-500/30", children: [
93
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "h-2.5 w-2.5" }),
94
+ "Named"
95
+ ] }) : /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: "text-2xs h-4 px-1.5 gap-1 text-amber-500 border-amber-500/30", children: [
96
+ /* @__PURE__ */ jsx(Info, { className: "h-2.5 w-2.5" }),
97
+ "Add a name"
98
+ ] })
99
+ }
100
+ ),
101
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 pt-1 border-t border-border", children: [
102
+ /* @__PURE__ */ jsxs(FieldRow, { children: [
103
+ /* @__PURE__ */ jsx(Label, { htmlFor: "displayName", children: "Display name" }),
104
+ /* @__PURE__ */ jsx(
105
+ Input,
106
+ {
107
+ id: "displayName",
108
+ placeholder: collection?.name ?? "My Collection",
109
+ value: form.displayName,
110
+ onChange: set("displayName"),
111
+ disabled: isLoading
112
+ }
113
+ ),
114
+ /* @__PURE__ */ jsx(HelperText, { children: "Shown on your collection page and search results. Uses the on-chain name by default." })
115
+ ] }),
116
+ /* @__PURE__ */ jsxs(FieldRow, { children: [
117
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
118
+ /* @__PURE__ */ jsx(Label, { htmlFor: "description", children: "Description" }),
119
+ /* @__PURE__ */ jsxs("span", { className: cn("text-2xs tabular-nums", descLen > DESC_MAX ? "text-destructive" : "text-muted-foreground"), children: [
120
+ descLen,
121
+ "/",
122
+ DESC_MAX
123
+ ] })
124
+ ] }),
125
+ /* @__PURE__ */ jsx(
126
+ Textarea,
127
+ {
128
+ id: "description",
129
+ value: form.description,
130
+ onChange: (e) => {
131
+ if (e.target.value.length <= DESC_MAX) set("description")(e);
132
+ },
133
+ rows: 4,
134
+ placeholder: "Tell collectors what makes this collection special \u2014 the story, the art style, the community\u2026",
135
+ disabled: isLoading
136
+ }
137
+ )
138
+ ] })
139
+ ] })
140
+ ] }),
141
+ /* @__PURE__ */ jsxs(SectionCard, { id: "media", children: [
142
+ /* @__PURE__ */ jsx(
143
+ SectionHeader,
144
+ {
145
+ icon: ImageIcon,
146
+ title: "Media",
147
+ description: "Images shown on your collection page. Use IPFS URIs (ipfs://Qm\u2026) or direct HTTPS URLs."
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 pt-1 border-t border-border", children: [
151
+ /* @__PURE__ */ jsxs(FieldRow, { children: [
152
+ /* @__PURE__ */ jsx(Label, { htmlFor: "image", children: "Cover image" }),
153
+ /* @__PURE__ */ jsx(
154
+ Input,
155
+ {
156
+ id: "image",
157
+ placeholder: "ipfs://Qm\u2026 or https://\u2026",
158
+ value: form.image,
159
+ onChange: set("image"),
160
+ disabled: isLoading
161
+ }
162
+ ),
163
+ /* @__PURE__ */ jsx(HelperText, { children: "Square or portrait image shown on collection cards across the marketplace." })
164
+ ] }),
165
+ /* @__PURE__ */ jsxs(FieldRow, { children: [
166
+ /* @__PURE__ */ jsx(Label, { htmlFor: "bannerImage", children: "Banner image" }),
167
+ /* @__PURE__ */ jsx(
168
+ Input,
169
+ {
170
+ id: "bannerImage",
171
+ placeholder: "ipfs://Qm\u2026 or https://\u2026",
172
+ value: form.bannerImage,
173
+ onChange: set("bannerImage"),
174
+ disabled: isLoading
175
+ }
176
+ ),
177
+ /* @__PURE__ */ jsx(HelperText, { children: "Wide image displayed as the hero banner at the top of your collection page. Recommended 1500\xD7500 px." })
178
+ ] })
179
+ ] })
180
+ ] }),
181
+ /* @__PURE__ */ jsxs(SectionCard, { id: "exclusive", className: form.gatedEnabled ? "border-emerald-500/30 bg-emerald-500/[0.03]" : "", children: [
182
+ /* @__PURE__ */ jsx(
183
+ SectionHeader,
184
+ {
185
+ icon: Lock,
186
+ title: "Exclusive Content",
187
+ description: "Reward your holders with exclusive content \u2014 only wallets that own a token from this collection can access it.",
188
+ iconClass: form.gatedEnabled ? "text-emerald-500" : "text-muted-foreground",
189
+ bgClass: form.gatedEnabled ? "bg-emerald-500/15" : "bg-muted",
190
+ badge: form.gatedEnabled ? /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: "text-2xs h-4 px-1.5 gap-1 text-emerald-500 border-emerald-500/30", children: [
191
+ /* @__PURE__ */ jsx(Unlock, { className: "h-2.5 w-2.5" }),
192
+ "Active"
193
+ ] }) : void 0
194
+ }
195
+ ),
196
+ /* @__PURE__ */ jsxs("div", { className: "pt-1 border-t border-border space-y-5", children: [
197
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
198
+ /* @__PURE__ */ jsxs("div", { children: [
199
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: form.gatedEnabled ? "Exclusive content is enabled" : "Enable exclusive content" }),
200
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: form.gatedEnabled ? "Holders will see and unlock your exclusive content on the collection page." : "Add exclusive content that only token holders can access \u2014 video, audio, links, or documents." })
201
+ ] }),
202
+ /* @__PURE__ */ jsx(
203
+ Switch,
204
+ {
205
+ checked: form.gatedEnabled,
206
+ onCheckedChange: (v) => update({ gatedEnabled: v }),
207
+ disabled: isLoading
208
+ }
209
+ )
210
+ ] }),
211
+ form.gatedEnabled && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
212
+ /* @__PURE__ */ jsxs(FieldRow, { children: [
213
+ /* @__PURE__ */ jsx(Label, { htmlFor: "gatedContentTitle", children: "Content title" }),
214
+ /* @__PURE__ */ jsx(
215
+ Input,
216
+ {
217
+ id: "gatedContentTitle",
218
+ placeholder: "e.g. Exclusive Track, Behind the Scenes, Holders Chat\u2026",
219
+ value: form.gatedContentTitle,
220
+ onChange: set("gatedContentTitle")
221
+ }
222
+ ),
223
+ /* @__PURE__ */ jsx(HelperText, { children: "Displayed publicly as a teaser on your collection page \u2014 this title is visible to everyone, but the actual content is only accessible to verified holders." })
224
+ ] }),
225
+ /* @__PURE__ */ jsxs(FieldRow, { children: [
226
+ /* @__PURE__ */ jsx(Label, { htmlFor: "gatedContentUrl", children: "Content URL" }),
227
+ /* @__PURE__ */ jsx(
228
+ Input,
229
+ {
230
+ id: "gatedContentUrl",
231
+ placeholder: "https://\u2026",
232
+ value: form.gatedContentUrl,
233
+ onChange: set("gatedContentUrl")
234
+ }
235
+ ),
236
+ /* @__PURE__ */ jsx(HelperText, { children: "The link holders will receive. This is kept private by the backend and never exposed publicly. Works with any URL \u2014 video, audio, document, community link, etc." })
237
+ ] }),
238
+ /* @__PURE__ */ jsxs(FieldRow, { children: [
239
+ /* @__PURE__ */ jsx(Label, { children: "Content type" }),
240
+ /* @__PURE__ */ jsxs(
241
+ Select,
242
+ {
243
+ value: form.gatedContentType,
244
+ onValueChange: (v) => update({ gatedContentType: v }),
245
+ children: [
246
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "What kind of content is this?" }) }),
247
+ /* @__PURE__ */ jsx(SelectContent, { children: CONTENT_TYPES.map(({ value, label, icon: Icon }) => /* @__PURE__ */ jsx(SelectItem, { value, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
248
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 text-muted-foreground" }),
249
+ label
250
+ ] }) }, value)) })
251
+ ]
252
+ }
253
+ ),
254
+ selectedContentType && /* @__PURE__ */ jsx(HelperText, { children: selectedContentType.hint })
255
+ ] }),
256
+ form.gatedContentTitle && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-emerald-500/20 bg-emerald-500/5 p-4 space-y-1.5", children: [
257
+ /* @__PURE__ */ jsx("p", { className: "text-2xs font-semibold text-emerald-500 uppercase tracking-widest", children: "Preview \u2014 what holders see" }),
258
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: form.gatedContentTitle || "\u2014" }),
259
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Verified holders can access this content on your collection page." })
260
+ ] })
261
+ ] }),
262
+ !form.gatedEnabled && /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-muted/50 border border-border p-4 space-y-2", children: [
263
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-foreground", children: "How it works" }),
264
+ /* @__PURE__ */ jsxs("ul", { className: "text-xs text-muted-foreground space-y-1.5", children: [
265
+ /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
266
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3 text-emerald-500 mt-0.5 shrink-0" }),
267
+ "Set a URL \u2014 a private video, Discord invite, audio file, or any link."
268
+ ] }),
269
+ /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
270
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "h-3 w-3 text-emerald-500 mt-0.5 shrink-0" }),
271
+ "Medialane verifies on-chain ownership before granting access. No spoofing."
272
+ ] }),
273
+ /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
274
+ /* @__PURE__ */ jsx(Lock, { className: "h-3 w-3 text-emerald-500 mt-0.5 shrink-0" }),
275
+ "The content URL is never exposed publicly \u2014 only verified holders receive it."
276
+ ] })
277
+ ] })
278
+ ] })
279
+ ] })
280
+ ] }),
281
+ /* @__PURE__ */ jsxs(SectionCard, { id: "links", children: [
282
+ /* @__PURE__ */ jsx(
283
+ SectionHeader,
284
+ {
285
+ icon: Globe,
286
+ title: "Links",
287
+ description: "Social and community links shown to collectors visiting your page."
288
+ }
289
+ ),
290
+ /* @__PURE__ */ jsx("div", { className: "space-y-4 pt-1 border-t border-border", children: [
291
+ { key: "websiteUrl", icon: Globe, label: "Website", placeholder: "https://myproject.xyz" },
292
+ { key: "twitterUrl", icon: Twitter, label: "Twitter / X", placeholder: "https://twitter.com/\u2026" },
293
+ { key: "discordUrl", icon: MessageCircle, label: "Discord", placeholder: "https://discord.gg/\u2026" },
294
+ { key: "telegramUrl", icon: Send, label: "Telegram", placeholder: "https://t.me/\u2026" }
295
+ ].map(({ key, icon: Icon, label, placeholder }) => /* @__PURE__ */ jsxs(FieldRow, { children: [
296
+ /* @__PURE__ */ jsxs(Label, { htmlFor: key, className: "flex items-center gap-1.5", children: [
297
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 text-muted-foreground" }),
298
+ label
299
+ ] }),
300
+ /* @__PURE__ */ jsx(
301
+ Input,
302
+ {
303
+ id: key,
304
+ placeholder,
305
+ value: form[key],
306
+ onChange: set(key),
307
+ disabled: isLoading
308
+ }
309
+ )
310
+ ] }, key)) })
311
+ ] }),
312
+ /* @__PURE__ */ jsx(CollectionSlugClaimSection, { contract, profile }),
313
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pt-2", children: [
314
+ /* @__PURE__ */ jsx(
315
+ Button,
316
+ {
317
+ onClick: save,
318
+ disabled: saving || isLoading || descLen > DESC_MAX,
319
+ className: "min-w-[120px]",
320
+ children: saving ? "Saving\u2026" : "Save changes"
321
+ }
322
+ ),
323
+ saveStatus === "saved" && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-sm text-emerald-500", children: [
324
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "h-4 w-4" }),
325
+ " Saved"
326
+ ] }),
327
+ saveStatus === "error" && /* @__PURE__ */ jsx("span", { className: "text-sm text-destructive", children: saveError }),
328
+ descLen > DESC_MAX && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: "Description is too long." })
329
+ ] })
330
+ ] });
331
+ }
332
+ export {
333
+ CollectionSettings
334
+ };
335
+ //# sourceMappingURL=collection-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/collection-settings.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n User, Image as ImageIcon, Globe, Twitter, MessageCircle, Send,\n Lock, Unlock, Sparkles, ShieldCheck, CheckCircle2, ExternalLink,\n Video, Music, Radio, FileText, Link2, Info,\n} from \"lucide-react\";\nimport { collectionHref } from \"@medialane/sdk\";\nimport { Button } from \"./button.js\";\nimport { Input } from \"./input.js\";\nimport { Label } from \"./label.js\";\nimport { Textarea } from \"./textarea.js\";\nimport { Switch } from \"./switch.js\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"./select.js\";\nimport { Badge } from \"./badge.js\";\nimport Link from \"./link.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { SectionCard, SectionHeader, FieldRow, HelperText } from \"./settings-section.js\";\nimport { CollectionSlugClaimSection } from \"./collection-slug-claim.js\";\nimport { useCollectionSettings } from \"../utils/use-collection-settings.js\";\n\nconst DESC_MAX = 500;\n\nconst CONTENT_TYPES = [\n { value: \"VIDEO\", label: \"Video\", icon: Video, hint: \"YouTube, Vimeo, or any video URL\" },\n { value: \"AUDIO\", label: \"Audio\", icon: Music, hint: \"Spotify, SoundCloud, MP3 link…\" },\n { value: \"STREAM\", label: \"Live stream\", icon: Radio, hint: \"Twitch, YouTube Live, etc.\" },\n { value: \"DOCUMENT\", label: \"Document\", icon: FileText, hint: \"PDF, Notion, Google Doc…\" },\n { value: \"LINK\", label: \"Link\", icon: Link2, hint: \"Any URL\" },\n];\n\nexport function CollectionSettings({ contract }: { contract: string }) {\n const {\n collection, profile, collectionLoading, isLoading, isOwner,\n form, set, update, saving, saveStatus, saveError, save,\n } = useCollectionSettings(contract);\n const descLen = form.description.length;\n\n if (!collectionLoading && collection && !isOwner) {\n return (\n <div className=\"py-12 flex flex-col items-center gap-3 text-center\">\n <div className=\"h-12 w-12 rounded-full bg-muted flex items-center justify-center\">\n <Lock className=\"h-5 w-5 text-muted-foreground\" />\n </div>\n <p className=\"text-sm font-medium\">Owner access only</p>\n <p className=\"text-xs text-muted-foreground max-w-xs\">\n Only the collection owner can access settings. If you believe this is yours,\n visit your portfolio to claim it.\n </p>\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link href=\"/portfolio/collections\">Go to my collections</Link>\n </Button>\n </div>\n );\n }\n\n const selectedContentType = CONTENT_TYPES.find(t => t.value === form.gatedContentType);\n\n return (\n <div className=\"space-y-6 max-w-2xl pb-16\">\n\n <div>\n <h1 className=\"text-xl font-semibold\">Collection Settings</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Customize how your collection appears on Medialane and manage exclusive content for your community.\n </p>\n {collection && (\n <Link\n href={collectionHref(\"STARKNET\", contract)}\n className=\"inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground mt-2 transition-colors\"\n >\n <ExternalLink className=\"h-3 w-3\" />\n View collection page\n </Link>\n )}\n </div>\n\n <SectionCard id=\"identity\">\n <SectionHeader\n icon={User}\n title=\"Identity\"\n description=\"Your collection's public name and description. A complete profile builds trust with collectors.\"\n badge={\n profile?.displayName ? (\n <Badge variant=\"outline\" className=\"text-2xs h-4 px-1.5 gap-1 text-emerald-500 border-emerald-500/30\">\n <CheckCircle2 className=\"h-2.5 w-2.5\" />\n Named\n </Badge>\n ) : (\n <Badge variant=\"outline\" className=\"text-2xs h-4 px-1.5 gap-1 text-amber-500 border-amber-500/30\">\n <Info className=\"h-2.5 w-2.5\" />\n Add a name\n </Badge>\n )\n }\n />\n <div className=\"space-y-4 pt-1 border-t border-border\">\n <FieldRow>\n <Label htmlFor=\"displayName\">Display name</Label>\n <Input\n id=\"displayName\"\n placeholder={collection?.name ?? \"My Collection\"}\n value={form.displayName}\n onChange={set(\"displayName\")}\n disabled={isLoading}\n />\n <HelperText>\n Shown on your collection page and search results. Uses the on-chain name by default.\n </HelperText>\n </FieldRow>\n <FieldRow>\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"description\">Description</Label>\n <span className={cn(\"text-2xs tabular-nums\", descLen > DESC_MAX ? \"text-destructive\" : \"text-muted-foreground\")}>\n {descLen}/{DESC_MAX}\n </span>\n </div>\n <Textarea\n id=\"description\"\n value={form.description}\n onChange={(e) => {\n if (e.target.value.length <= DESC_MAX) set(\"description\")(e);\n }}\n rows={4}\n placeholder=\"Tell collectors what makes this collection special — the story, the art style, the community…\"\n disabled={isLoading}\n />\n </FieldRow>\n </div>\n </SectionCard>\n\n <SectionCard id=\"media\">\n <SectionHeader\n icon={ImageIcon}\n title=\"Media\"\n description=\"Images shown on your collection page. Use IPFS URIs (ipfs://Qm…) or direct HTTPS URLs.\"\n />\n <div className=\"space-y-4 pt-1 border-t border-border\">\n <FieldRow>\n <Label htmlFor=\"image\">Cover image</Label>\n <Input\n id=\"image\"\n placeholder=\"ipfs://Qm… or https://…\"\n value={form.image}\n onChange={set(\"image\")}\n disabled={isLoading}\n />\n <HelperText>Square or portrait image shown on collection cards across the marketplace.</HelperText>\n </FieldRow>\n <FieldRow>\n <Label htmlFor=\"bannerImage\">Banner image</Label>\n <Input\n id=\"bannerImage\"\n placeholder=\"ipfs://Qm… or https://…\"\n value={form.bannerImage}\n onChange={set(\"bannerImage\")}\n disabled={isLoading}\n />\n <HelperText>Wide image displayed as the hero banner at the top of your collection page. Recommended 1500×500 px.</HelperText>\n </FieldRow>\n </div>\n </SectionCard>\n\n <SectionCard id=\"exclusive\" className={form.gatedEnabled ? \"border-emerald-500/30 bg-emerald-500/[0.03]\" : \"\"}>\n <SectionHeader\n icon={Lock}\n title=\"Exclusive Content\"\n description=\"Reward your holders with exclusive content — only wallets that own a token from this collection can access it.\"\n iconClass={form.gatedEnabled ? \"text-emerald-500\" : \"text-muted-foreground\"}\n bgClass={form.gatedEnabled ? \"bg-emerald-500/15\" : \"bg-muted\"}\n badge={\n form.gatedEnabled ? (\n <Badge variant=\"outline\" className=\"text-2xs h-4 px-1.5 gap-1 text-emerald-500 border-emerald-500/30\">\n <Unlock className=\"h-2.5 w-2.5\" />\n Active\n </Badge>\n ) : undefined\n }\n />\n\n <div className=\"pt-1 border-t border-border space-y-5\">\n\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {form.gatedEnabled ? \"Exclusive content is enabled\" : \"Enable exclusive content\"}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {form.gatedEnabled\n ? \"Holders will see and unlock your exclusive content on the collection page.\"\n : \"Add exclusive content that only token holders can access — video, audio, links, or documents.\"}\n </p>\n </div>\n <Switch\n checked={form.gatedEnabled}\n onCheckedChange={(v) => update({ gatedEnabled: v })}\n disabled={isLoading}\n />\n </div>\n\n {form.gatedEnabled && (\n <div className=\"space-y-4\">\n\n <FieldRow>\n <Label htmlFor=\"gatedContentTitle\">Content title</Label>\n <Input\n id=\"gatedContentTitle\"\n placeholder=\"e.g. Exclusive Track, Behind the Scenes, Holders Chat…\"\n value={form.gatedContentTitle}\n onChange={set(\"gatedContentTitle\")}\n />\n <HelperText>\n Displayed publicly as a teaser on your collection page — this title is visible to everyone,\n but the actual content is only accessible to verified holders.\n </HelperText>\n </FieldRow>\n\n <FieldRow>\n <Label htmlFor=\"gatedContentUrl\">Content URL</Label>\n <Input\n id=\"gatedContentUrl\"\n placeholder=\"https://…\"\n value={form.gatedContentUrl}\n onChange={set(\"gatedContentUrl\")}\n />\n <HelperText>\n The link holders will receive. This is kept private by the backend and never exposed publicly.\n Works with any URL — video, audio, document, community link, etc.\n </HelperText>\n </FieldRow>\n\n <FieldRow>\n <Label>Content type</Label>\n <Select\n value={form.gatedContentType}\n onValueChange={(v) => update({ gatedContentType: v })}\n >\n <SelectTrigger>\n <SelectValue placeholder=\"What kind of content is this?\" />\n </SelectTrigger>\n <SelectContent>\n {CONTENT_TYPES.map(({ value, label, icon: Icon }) => (\n <SelectItem key={value} value={value}>\n <div className=\"flex items-center gap-2\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {label}\n </div>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {selectedContentType && (\n <HelperText>{selectedContentType.hint}</HelperText>\n )}\n </FieldRow>\n\n {form.gatedContentTitle && (\n <div className=\"rounded-lg border border-emerald-500/20 bg-emerald-500/5 p-4 space-y-1.5\">\n <p className=\"text-2xs font-semibold text-emerald-500 uppercase tracking-widest\">\n Preview — what holders see\n </p>\n <p className=\"text-sm font-semibold\">{form.gatedContentTitle || \"—\"}</p>\n <p className=\"text-xs text-muted-foreground\">\n Verified holders can access this content on your collection page.\n </p>\n </div>\n )}\n </div>\n )}\n\n {!form.gatedEnabled && (\n <div className=\"rounded-lg bg-muted/50 border border-border p-4 space-y-2\">\n <p className=\"text-xs font-semibold text-foreground\">How it works</p>\n <ul className=\"text-xs text-muted-foreground space-y-1.5\">\n <li className=\"flex items-start gap-2\">\n <Sparkles className=\"h-3 w-3 text-emerald-500 mt-0.5 shrink-0\" />\n Set a URL — a private video, Discord invite, audio file, or any link.\n </li>\n <li className=\"flex items-start gap-2\">\n <ShieldCheck className=\"h-3 w-3 text-emerald-500 mt-0.5 shrink-0\" />\n Medialane verifies on-chain ownership before granting access. No spoofing.\n </li>\n <li className=\"flex items-start gap-2\">\n <Lock className=\"h-3 w-3 text-emerald-500 mt-0.5 shrink-0\" />\n The content URL is never exposed publicly — only verified holders receive it.\n </li>\n </ul>\n </div>\n )}\n </div>\n </SectionCard>\n\n <SectionCard id=\"links\">\n <SectionHeader\n icon={Globe}\n title=\"Links\"\n description=\"Social and community links shown to collectors visiting your page.\"\n />\n <div className=\"space-y-4 pt-1 border-t border-border\">\n {([\n { key: \"websiteUrl\" as const, icon: Globe, label: \"Website\", placeholder: \"https://myproject.xyz\" },\n { key: \"twitterUrl\" as const, icon: Twitter, label: \"Twitter / X\", placeholder: \"https://twitter.com/…\" },\n { key: \"discordUrl\" as const, icon: MessageCircle, label: \"Discord\", placeholder: \"https://discord.gg/…\" },\n { key: \"telegramUrl\" as const, icon: Send, label: \"Telegram\", placeholder: \"https://t.me/…\" },\n ] as const).map(({ key, icon: Icon, label, placeholder }) => (\n <FieldRow key={key}>\n <Label htmlFor={key} className=\"flex items-center gap-1.5\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {label}\n </Label>\n <Input\n id={key}\n placeholder={placeholder}\n value={form[key]}\n onChange={set(key)}\n disabled={isLoading}\n />\n </FieldRow>\n ))}\n </div>\n </SectionCard>\n\n <CollectionSlugClaimSection contract={contract} profile={profile} />\n\n <div className=\"flex items-center gap-3 pt-2\">\n <Button\n onClick={save}\n disabled={saving || isLoading || descLen > DESC_MAX}\n className=\"min-w-[120px]\"\n >\n {saving ? \"Saving…\" : \"Save changes\"}\n </Button>\n {saveStatus === \"saved\" && (\n <span className=\"flex items-center gap-1.5 text-sm text-emerald-500\">\n <CheckCircle2 className=\"h-4 w-4\" /> Saved\n </span>\n )}\n {saveStatus === \"error\" && (\n <span className=\"text-sm text-destructive\">{saveError}</span>\n )}\n {descLen > DESC_MAX && (\n <p className=\"text-xs text-destructive\">Description is too long.</p>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var collection_slug_claim_exports = {};
31
+ __export(collection_slug_claim_exports, {
32
+ CollectionSlugClaimSection: () => CollectionSlugClaimSection
33
+ });
34
+ module.exports = __toCommonJS(collection_slug_claim_exports);
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var import_react = require("react");
37
+ var import_sdk = require("@medialane/sdk");
38
+ var import_lucide_react = require("lucide-react");
39
+ var import_button = require("./button.js");
40
+ var import_badge = require("./badge.js");
41
+ var import_link = __toESM(require("./link.js"), 1);
42
+ var import_medialane_app = require("./medialane-app.js");
43
+ var import_settings_section = require("./settings-section.js");
44
+ var import_describe_error = require("../utils/describe-error.js");
45
+ function CollectionSlugClaimSection({
46
+ contract,
47
+ profile
48
+ }) {
49
+ const { getClient, ensureSignedIn, verifyEmailHref } = (0, import_medialane_app.useMedialaneApp)();
50
+ const [slugInput, setSlugInput] = (0, import_react.useState)("");
51
+ const [checkState, setCheckState] = (0, import_react.useState)("idle");
52
+ const [checkReason, setCheckReason] = (0, import_react.useState)(null);
53
+ const [submitState, setSubmitState] = (0, import_react.useState)("idle");
54
+ const [submitError, setSubmitError] = (0, import_react.useState)(null);
55
+ const [pendingSlug, setPendingSlug] = (0, import_react.useState)(null);
56
+ const loadedRef = (0, import_react.useRef)(false);
57
+ (0, import_react.useEffect)(() => {
58
+ if (loadedRef.current) return;
59
+ loadedRef.current = true;
60
+ (async () => {
61
+ try {
62
+ const { claims } = await getClient().api.collections.mySlugClaims();
63
+ const match = claims.find(
64
+ (c) => (0, import_sdk.normalizeAddress)("STARKNET", c.contractAddress) === (0, import_sdk.normalizeAddress)("STARKNET", contract) && c.status === "PENDING"
65
+ );
66
+ if (match) setPendingSlug(match.slug);
67
+ } catch {
68
+ }
69
+ })();
70
+ }, [contract, getClient]);
71
+ const handleCheck = async () => {
72
+ const slug = slugInput.toLowerCase().trim();
73
+ if (!slug) return;
74
+ setCheckState("checking");
75
+ setCheckReason(null);
76
+ try {
77
+ const result = await getClient().api.collections.slugAvailability(slug);
78
+ setCheckState(result.available ? "available" : "taken");
79
+ if (!result.available) setCheckReason(result.reason ?? "That slug is not available.");
80
+ } catch {
81
+ setCheckState("invalid");
82
+ setCheckReason("Unable to check availability. Try again.");
83
+ }
84
+ };
85
+ const handleSubmit = async () => {
86
+ const slug = slugInput.toLowerCase().trim();
87
+ if (!slug || checkState !== "available") return;
88
+ setSubmitState("submitting");
89
+ setSubmitError(null);
90
+ try {
91
+ await ensureSignedIn?.();
92
+ const { claim } = await getClient().api.collections.submitSlugClaim(contract, slug);
93
+ setPendingSlug(claim.slug);
94
+ setSubmitState("done");
95
+ } catch (err) {
96
+ const msg = (0, import_describe_error.describeError)(err).message;
97
+ setSubmitError(msg);
98
+ setSubmitState("error");
99
+ }
100
+ };
101
+ if (profile?.slug) {
102
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_settings_section.SectionCard, { className: "border-emerald-500/20 bg-emerald-500/[0.02]", children: [
103
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
104
+ import_settings_section.SectionHeader,
105
+ {
106
+ icon: import_lucide_react.Gem,
107
+ title: "Collection URL",
108
+ description: `medialane.io/collection/${profile.slug}`,
109
+ iconClass: "text-emerald-500",
110
+ bgClass: "bg-emerald-500/15",
111
+ badge: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_badge.Badge, { variant: "outline", className: "text-2xs h-4 px-1.5 text-emerald-600 border-emerald-500/30", children: "Active" })
112
+ }
113
+ ),
114
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pt-1 border-t border-border", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
115
+ "Your collection is accessible at",
116
+ " ",
117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "tabular-nums text-foreground", children: [
118
+ "medialane.io/collection/",
119
+ profile.slug
120
+ ] })
121
+ ] }) })
122
+ ] });
123
+ }
124
+ if (pendingSlug) {
125
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_settings_section.SectionCard, { className: "border-primary/20 bg-primary/[0.02]", children: [
126
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
127
+ import_settings_section.SectionHeader,
128
+ {
129
+ icon: import_lucide_react.Gem,
130
+ title: "Claim your collection URL",
131
+ description: `medialane.io/collection/${pendingSlug}`,
132
+ iconClass: "text-primary",
133
+ bgClass: "bg-primary/15",
134
+ badge: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_badge.Badge, { variant: "outline", className: "text-2xs h-4 px-1.5 text-amber-600 border-amber-500/30", children: "Pending review" })
135
+ }
136
+ ),
137
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pt-1 border-t border-border", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
138
+ "Your claim for",
139
+ " ",
140
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "tabular-nums text-foreground", children: [
141
+ "medialane.io/collection/",
142
+ pendingSlug
143
+ ] }),
144
+ " is under review. You'll be notified once it's approved."
145
+ ] }) })
146
+ ] });
147
+ }
148
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_settings_section.SectionCard, { className: "border-primary/20 bg-primary/[0.02]", children: [
149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
150
+ import_settings_section.SectionHeader,
151
+ {
152
+ icon: import_lucide_react.Gem,
153
+ title: "Claim your collection URL",
154
+ description: "Reserve a unique URL \u2014 medialane.io/collection/your-name",
155
+ iconClass: "text-primary",
156
+ bgClass: "bg-primary/15"
157
+ }
158
+ ),
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pt-3 border-t border-border space-y-3", children: [
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 flex items-center rounded-lg border border-border bg-muted/40 px-3 py-2", children: [
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: "medialane.io/collection/" }),
163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
164
+ "input",
165
+ {
166
+ type: "text",
167
+ className: "flex-1 bg-transparent text-xs text-foreground outline-none placeholder:text-muted-foreground/50 min-w-0",
168
+ placeholder: "your-name",
169
+ value: slugInput,
170
+ onChange: (e) => {
171
+ setSlugInput(e.target.value.toLowerCase().replace(/[^a-z0-9_-]/g, ""));
172
+ setCheckState("idle");
173
+ },
174
+ onKeyDown: (e) => {
175
+ if (e.key === "Enter") handleCheck();
176
+ },
177
+ maxLength: 20
178
+ }
179
+ )
180
+ ] }),
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
182
+ import_button.Button,
183
+ {
184
+ variant: "outline",
185
+ size: "sm",
186
+ onClick: handleCheck,
187
+ disabled: !slugInput.trim() || checkState === "checking",
188
+ className: "shrink-0",
189
+ children: checkState === "checking" ? "Checking\u2026" : "Check"
190
+ }
191
+ )
192
+ ] }),
193
+ checkState === "available" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-3", children: [
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-emerald-600 font-medium truncate", children: [
195
+ "\u2713 Available \u2014 ",
196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "tabular-nums", children: [
197
+ "medialane.io/collection/",
198
+ slugInput
199
+ ] })
200
+ ] }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { size: "sm", onClick: handleSubmit, disabled: submitState === "submitting", className: "shrink-0", children: submitState === "submitting" ? "Claiming\u2026" : "Claim this URL" })
202
+ ] }),
203
+ (checkState === "taken" || checkState === "invalid") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-destructive", children: checkReason ?? "That slug is not available." }),
204
+ submitState === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-destructive", children: [
205
+ submitError,
206
+ verifyEmailHref && submitError === "Verify your email to claim a collection." && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
207
+ " ",
208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: verifyEmailHref, className: "underline font-medium hover:text-foreground", children: "Verify email" })
209
+ ] })
210
+ ] }),
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-muted-foreground", children: "3\u201320 characters, lowercase letters, numbers, underscores or hyphens. Claims are reviewed before going live." })
212
+ ] })
213
+ ] });
214
+ }
215
+ // Annotate the CommonJS export names for ESM import in node:
216
+ 0 && (module.exports = {
217
+ CollectionSlugClaimSection
218
+ });
219
+ //# sourceMappingURL=collection-slug-claim.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/collection-slug-claim.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useRef } from \"react\";\nimport { normalizeAddress } from \"@medialane/sdk\";\nimport { Gem } from \"lucide-react\";\nimport { Button } from \"./button.js\";\nimport { Badge } from \"./badge.js\";\nimport Link from \"./link.js\";\nimport { useMedialaneApp } from \"./medialane-app.js\";\nimport { SectionCard, SectionHeader } from \"./settings-section.js\";\nimport { describeError } from \"../utils/describe-error.js\";\n\nexport function CollectionSlugClaimSection({\n contract,\n profile,\n}: {\n contract: string;\n profile: { slug?: string | null } | null;\n}) {\n const { getClient, ensureSignedIn, verifyEmailHref } = useMedialaneApp();\n const [slugInput, setSlugInput] = useState(\"\");\n const [checkState, setCheckState] = useState<\"idle\" | \"checking\" | \"available\" | \"taken\" | \"invalid\">(\"idle\");\n const [checkReason, setCheckReason] = useState<string | null>(null);\n const [submitState, setSubmitState] = useState<\"idle\" | \"submitting\" | \"done\" | \"error\">(\"idle\");\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [pendingSlug, setPendingSlug] = useState<string | null>(null);\n const loadedRef = useRef(false);\n\n useEffect(() => {\n if (loadedRef.current) return;\n loadedRef.current = true;\n (async () => {\n try {\n const { claims } = await getClient().api.collections.mySlugClaims();\n const match = claims.find(\n (c) => normalizeAddress(\"STARKNET\", c.contractAddress) === normalizeAddress(\"STARKNET\", contract) && c.status === \"PENDING\"\n );\n if (match) setPendingSlug(match.slug);\n } catch {}\n })();\n }, [contract, getClient]);\n\n const handleCheck = async () => {\n const slug = slugInput.toLowerCase().trim();\n if (!slug) return;\n setCheckState(\"checking\");\n setCheckReason(null);\n try {\n const result = await getClient().api.collections.slugAvailability(slug);\n setCheckState(result.available ? \"available\" : \"taken\");\n if (!result.available) setCheckReason(result.reason ?? \"That slug is not available.\");\n } catch {\n setCheckState(\"invalid\");\n setCheckReason(\"Unable to check availability. Try again.\");\n }\n };\n\n const handleSubmit = async () => {\n const slug = slugInput.toLowerCase().trim();\n if (!slug || checkState !== \"available\") return;\n setSubmitState(\"submitting\");\n setSubmitError(null);\n try {\n await ensureSignedIn?.();\n const { claim } = await getClient().api.collections.submitSlugClaim(contract, slug);\n setPendingSlug(claim.slug);\n setSubmitState(\"done\");\n } catch (err: unknown) {\n const msg = describeError(err).message;\n setSubmitError(msg);\n setSubmitState(\"error\");\n }\n };\n\n if (profile?.slug) {\n return (\n <SectionCard className=\"border-emerald-500/20 bg-emerald-500/[0.02]\">\n <SectionHeader\n icon={Gem}\n title=\"Collection URL\"\n description={`medialane.io/collection/${profile.slug}`}\n iconClass=\"text-emerald-500\"\n bgClass=\"bg-emerald-500/15\"\n badge={\n <Badge variant=\"outline\" className=\"text-2xs h-4 px-1.5 text-emerald-600 border-emerald-500/30\">\n Active\n </Badge>\n }\n />\n <div className=\"pt-1 border-t border-border\">\n <p className=\"text-xs text-muted-foreground\">\n Your collection is accessible at{\" \"}\n <span className=\"tabular-nums text-foreground\">medialane.io/collection/{profile.slug}</span>\n </p>\n </div>\n </SectionCard>\n );\n }\n\n if (pendingSlug) {\n return (\n <SectionCard className=\"border-primary/20 bg-primary/[0.02]\">\n <SectionHeader\n icon={Gem}\n title=\"Claim your collection URL\"\n description={`medialane.io/collection/${pendingSlug}`}\n iconClass=\"text-primary\"\n bgClass=\"bg-primary/15\"\n badge={\n <Badge variant=\"outline\" className=\"text-2xs h-4 px-1.5 text-amber-600 border-amber-500/30\">\n Pending review\n </Badge>\n }\n />\n <div className=\"pt-1 border-t border-border\">\n <p className=\"text-xs text-muted-foreground\">\n Your claim for{\" \"}\n <span className=\"tabular-nums text-foreground\">medialane.io/collection/{pendingSlug}</span> is under\n review. You&apos;ll be notified once it&apos;s approved.\n </p>\n </div>\n </SectionCard>\n );\n }\n\n return (\n <SectionCard className=\"border-primary/20 bg-primary/[0.02]\">\n <SectionHeader\n icon={Gem}\n title=\"Claim your collection URL\"\n description=\"Reserve a unique URL — medialane.io/collection/your-name\"\n iconClass=\"text-primary\"\n bgClass=\"bg-primary/15\"\n />\n <div className=\"pt-3 border-t border-border space-y-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"flex-1 flex items-center rounded-lg border border-border bg-muted/40 px-3 py-2\">\n <span className=\"text-xs text-muted-foreground whitespace-nowrap shrink-0\">\n medialane.io/collection/\n </span>\n <input\n type=\"text\"\n className=\"flex-1 bg-transparent text-xs text-foreground outline-none placeholder:text-muted-foreground/50 min-w-0\"\n placeholder=\"your-name\"\n value={slugInput}\n onChange={(e) => {\n setSlugInput(e.target.value.toLowerCase().replace(/[^a-z0-9_-]/g, \"\"));\n setCheckState(\"idle\");\n }}\n onKeyDown={(e) => { if (e.key === \"Enter\") handleCheck(); }}\n maxLength={20}\n />\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleCheck}\n disabled={!slugInput.trim() || checkState === \"checking\"}\n className=\"shrink-0\"\n >\n {checkState === \"checking\" ? \"Checking…\" : \"Check\"}\n </Button>\n </div>\n\n {checkState === \"available\" && (\n <div className=\"flex items-center justify-between gap-3\">\n <p className=\"text-xs text-emerald-600 font-medium truncate\">\n ✓ Available — <span className=\"tabular-nums\">medialane.io/collection/{slugInput}</span>\n </p>\n <Button size=\"sm\" onClick={handleSubmit} disabled={submitState === \"submitting\"} className=\"shrink-0\">\n {submitState === \"submitting\" ? \"Claiming…\" : \"Claim this URL\"}\n </Button>\n </div>\n )}\n\n {(checkState === \"taken\" || checkState === \"invalid\") && (\n <p className=\"text-xs text-destructive\">{checkReason ?? \"That slug is not available.\"}</p>\n )}\n\n {submitState === \"error\" && (\n <p className=\"text-xs text-destructive\">\n {submitError}\n {verifyEmailHref && submitError === \"Verify your email to claim a collection.\" && (\n <>\n {\" \"}\n <Link href={verifyEmailHref} className=\"underline font-medium hover:text-foreground\">\n Verify email\n </Link>\n </>\n )}\n </p>\n )}\n\n <p className=\"text-xs text-muted-foreground\">\n 3–20 characters, lowercase letters, numbers, underscores or hyphens. Claims are reviewed before\n going live.\n </p>\n </div>\n </SectionCard>\n );\n}\n"],"mappings":"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","names":["Link"]}