eny.space Landingpage
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(dashboard): improve atproto test client UX

- Split handle input into prefix + domain suffix display
- Add try/catch error handling to invite/account/session actions
- Make PDS endpoint a clickable link
- Use solid button styles for enabled states; bump mock PDS state to 3

+64 -39
+1 -1
app/api/pds/service/route.ts
··· 60 60 "helm repo update\n" + 61 61 "helm install bluesky-pds nerkho/bluesky-pds --namespace {namespace} -f {values}\n" + 62 62 'export KUBECONFIG=""', 63 - state: 0, 63 + state: 3, 64 64 kubeconfig_id: 1, 65 65 created_at: "2026-03-17T15:05:40.000000Z", 66 66 updated_at: "2026-03-17T15:05:40.000000Z",
+63 -38
app/dashboard/atproto-test-client.tsx
··· 29 29 const [inviteCode, setInviteCode] = useState<string>(""); 30 30 31 31 const [email, setEmail] = useState<string>(""); 32 - const [handle, setHandle] = useState<string>(""); 32 + const [handlePrefix, setHandlePrefix] = useState<string>(""); 33 33 const [newPassword, setNewPassword] = useState<string>(""); 34 34 35 35 const [sessionIdentifier, setSessionIdentifier] = useState<string>(""); ··· 40 40 const [error, setError] = useState<string | null>(null); 41 41 42 42 const pdsBareHost = useMemo(() => stripScheme(pdsHost), [pdsHost]); 43 + const fullHandle = useMemo( 44 + () => (handlePrefix && pdsBareHost ? `${handlePrefix}.${pdsBareHost}` : ""), 45 + [handlePrefix, pdsBareHost], 46 + ); 43 47 const pdsStateNum = useMemo(() => { 44 48 if (pdsState === null) return null; 45 49 const n = typeof pdsState === "number" ? pdsState : Number(pdsState); ··· 47 51 }, [pdsState]); 48 52 const isPdsReady = pdsStateNum !== null && pdsStateNum >= 3; 49 53 const createAccountDisabled = 50 - loading || !inviteCode || !handle || !newPassword || !isPdsReady; 54 + loading || !inviteCode || !handlePrefix || !newPassword || !isPdsReady; 51 55 const createSessionDisabled = 52 - loading || !handle || !newPassword || !isPdsReady; 56 + loading || !handlePrefix || !newPassword || !isPdsReady; 53 57 54 58 useEffect(() => { 55 59 const load = async () => { ··· 61 65 setPdsHost(host); 62 66 setPdsState(data?.state ?? null); 63 67 64 - if (!handle && host) { 68 + if (!handlePrefix && host) { 65 69 const bare = stripScheme(host); 66 - setHandle(`user1.${bare}`); 70 + setHandlePrefix("user1"); 67 71 setSessionIdentifier(`user1.${bare}`); 68 72 } 69 73 } catch (e) { ··· 94 98 throw new Error(payload); 95 99 } 96 100 97 - // Prefer the nested upstream error if present. 98 101 const upstreamMessage = 99 102 payload?.payload?.message || 100 103 payload?.payload?.error || ··· 117 120 }; 118 121 119 122 const createInvite = async () => { 120 - const payload = await call("/api/pds/atproto/invite", { useCount: 1 }); 121 - const code = payload?.code || payload?.inviteCode || ""; 122 - setInviteCode(code); 123 - setOutput(payload); 123 + try { 124 + const payload = await call("/api/pds/atproto/invite", { useCount: 1 }); 125 + const code = payload?.code || payload?.inviteCode || ""; 126 + setInviteCode(code); 127 + setOutput(payload); 128 + } catch (e) { 129 + setError(e instanceof Error ? e.message : String(e)); 130 + } 124 131 }; 125 132 126 133 const createAccount = async () => { 127 - const payload = await call("/api/pds/atproto/create-account", { 128 - email, 129 - handle, 130 - password: newPassword, 131 - inviteCode, 132 - }); 133 - setOutput(payload); 134 - setSessionIdentifier(handle); 135 - setSessionPassword(newPassword); 134 + try { 135 + const payload = await call("/api/pds/atproto/create-account", { 136 + email, 137 + handle: fullHandle, 138 + password: newPassword, 139 + inviteCode, 140 + }); 141 + setOutput(payload); 142 + setSessionIdentifier(fullHandle); 143 + setSessionPassword(newPassword); 144 + } catch (e) { 145 + setError(e instanceof Error ? e.message : String(e)); 146 + } 136 147 }; 137 148 138 149 const createSession = async () => { 139 - const payload = await call("/api/pds/atproto/create-session", { 140 - identifier: sessionIdentifier || handle, 141 - password: sessionPassword || newPassword, 142 - }); 143 - setOutput(payload); 150 + try { 151 + const payload = await call("/api/pds/atproto/create-session", { 152 + identifier: sessionIdentifier || fullHandle, 153 + password: sessionPassword || newPassword, 154 + }); 155 + setOutput(payload); 156 + } catch (e) { 157 + setError(e instanceof Error ? e.message : String(e)); 158 + } 144 159 }; 145 160 146 161 return ( ··· 155 170 {pdsHost ? ( 156 171 <Paragraph className="text-sm text-white/70"> 157 172 PDS endpoint:{" "} 158 - <span className="font-mono text-white">{pdsHost}</span> 173 + <a 174 + href={pdsHost} 175 + target="_blank" 176 + rel="noreferrer" 177 + className="font-mono text-white underline underline-offset-2 hover:text-white/80" 178 + > 179 + {pdsHost} 180 + </a> 159 181 </Paragraph> 160 182 ) : ( 161 183 <Paragraph className="text-sm text-white/70">Loading PDS endpoint…</Paragraph> ··· 163 185 164 186 {pdsStateNum !== null && !isPdsReady && ( 165 187 <Paragraph className="text-sm text-amber-100/90"> 166 - PDS not ready yet (state={pdsStateNum}). Waiting for provisioning to 167 - finish. 188 + PDS not ready yet (state={pdsStateNum}). Waiting for provisioning to finish. 168 189 </Paragraph> 169 190 )} 170 191 ··· 206 227 207 228 <label className="space-y-1"> 208 229 <Paragraph className="text-xs font-medium text-white/60">Handle</Paragraph> 209 - <input 210 - value={handle} 211 - onChange={(e) => setHandle(e.target.value)} 212 - className="w-full rounded-md border border-white/20 bg-transparent px-3 py-2 text-sm text-white placeholder:text-white/50" 213 - placeholder={`user1.${pdsBareHost || "eny.k8s.frx.pub"}`} 214 - /> 230 + <div className="flex items-center rounded-md border border-white/20 bg-transparent text-sm text-white focus-within:border-white/50"> 231 + <input 232 + value={handlePrefix} 233 + onChange={(e) => setHandlePrefix(e.target.value)} 234 + className="min-w-0 flex-1 bg-transparent px-3 py-2 placeholder:text-white/50 focus:outline-none" 235 + placeholder="user1" 236 + /> 237 + {pdsBareHost && ( 238 + <span className="shrink-0 pr-3 text-white/40">.{pdsBareHost}</span> 239 + )} 240 + </div> 215 241 </label> 216 242 217 243 <label className="space-y-1 md:col-span-2"> ··· 234 260 disabled={createAccountDisabled} 235 261 className={ 236 262 createAccountDisabled 237 - ? "rounded-full bg-emerald-400/10 border border-emerald-200/10 opacity-60 cursor-not-allowed" 238 - : "rounded-full bg-emerald-400/20 hover:bg-emerald-400/35 border border-emerald-200/25" 263 + ? "rounded-full bg-emerald-400/10 border border-emerald-200/10 opacity-40 cursor-not-allowed" 264 + : "rounded-full bg-emerald-500 hover:bg-emerald-400 text-neutral-950 font-medium" 239 265 } 240 266 > 241 267 Create user ··· 245 271 disabled={createSessionDisabled} 246 272 className={ 247 273 createSessionDisabled 248 - ? "rounded-full bg-sky-400/10 border border-sky-200/10 opacity-60 cursor-not-allowed" 249 - : "rounded-full bg-sky-400/20 hover:bg-sky-400/35 border border-sky-200/25" 274 + ? "rounded-full bg-sky-400/10 border border-sky-200/10 opacity-40 cursor-not-allowed" 275 + : "rounded-full bg-sky-500 hover:bg-sky-400 text-neutral-950 font-medium" 250 276 } 251 277 > 252 278 Login (create session) ··· 286 312 </section> 287 313 ); 288 314 } 289 -