Graphical PDS migrator for AT Protocol

update header and ticket booth info

Turtlepaw 9f6d3b69 9bda00df

Changed files
+103 -20
components
islands
static
icons
+65
components/Link.tsx
···
+
import { JSX } from "preact";
+
+
/**
+
* Props for the Link component
+
*/
+
type Props = Omit<JSX.HTMLAttributes<HTMLAnchorElement>, "href"> & {
+
/** URL for the link */
+
href: string;
+
/** Whether this is an external link that should show an outbound icon */
+
isExternal?: boolean;
+
/** Link text content */
+
children: JSX.Element | string;
+
};
+
+
/**
+
* A link component that handles external links with appropriate styling and accessibility.
+
* Automatically adds external link icon and proper attributes for external links.
+
*/
+
export function Link(props: Props) {
+
const {
+
isExternal = false,
+
class: className = "",
+
children,
+
href,
+
...rest
+
} = props;
+
+
// SVG for external link icon
+
const externalLinkIcon = (
+
<svg
+
xmlns="http://www.w3.org/2000/svg"
+
viewBox="0 0 20 20"
+
fill="currentColor"
+
className="w-4 h-4 inline-block ml-1"
+
aria-hidden="true"
+
>
+
<path
+
fillRule="evenodd"
+
d="M4.25 5.5a.75.75 0 00-.75.75v8.5c0 .414.336.75.75.75h8.5a.75.75 0 00.75-.75v-4a.75.75 0 011.5 0v4A2.25 2.25 0 0112.75 17h-8.5A2.25 2.25 0 012 14.75v-8.5A2.25 2.25 0 014.25 4h5a.75.75 0 010 1.5h-5z"
+
/>
+
<path
+
fillRule="evenodd"
+
d="M6.194 12.753a.75.75 0 001.06.053L16.5 4.44v2.81a.75.75 0 001.5 0v-4.5a.75.75 0 00-.75-.75h-4.5a.75.75 0 000 1.5h2.553l-9.056 8.194a.75.75 0 00-.053 1.06z"
+
/>
+
</svg>
+
);
+
+
return (
+
<a
+
href={href}
+
{...rest}
+
className={`inline-flex items-center hover:underline ${className}`}
+
{...(isExternal && {
+
target: "_blank",
+
rel: "noopener noreferrer",
+
"aria-label": `${
+
typeof children === "string" ? children : ""
+
} (opens in new tab)`,
+
})}
+
>
+
{children}
+
{isExternal && externalLinkIcon}
+
</a>
+
);
+
}
+22 -14
islands/DidPlcProgress.tsx
···
import { useState } from "preact/hooks";
+
import { Link } from "../components/Link.tsx";
interface PlcUpdateStep {
name: string;
···
{/* Main Description */}
<div class="prose dark:prose-invert max-w-none mb-6">
<p class="text-blue-800 dark:text-blue-200 mb-4">
-
This tool helps you add a new rotation key to your PLC (Public
-
Ledger of Credentials). Having control of a rotation key gives you
-
sovereignty over your DID (Decentralized Identifier).
+
This tool helps you add a new rotation key to your{" "}
+
<Link
+
href="https://web.plc.directory/"
+
isExternal
+
class="text-blue-600 dark:text-blue-400"
+
>
+
PLC (Public Ledger of Credentials)
+
</Link>
+
. Having control of a rotation key gives you sovereignty over your
+
DID (Decentralized Identifier).
</p>
<h4 class="text-blue-900 dark:text-blue-100 font-medium mt-4 mb-2">
···
Process Overview:
</h4>
<ol class="space-y-2 text-sm text-blue-700 dark:text-blue-300 list-decimal pl-5">
-
<li>Generate a secure rotation key</li>
-
<li>Download and safely store the key</li>
+
<li>Generate a new rotation key</li>
+
<li>Download the key</li>
<li>Verify your identity via email</li>
-
<li>Add the key to your PLC record</li>
+
<li>Add the key to your PLC document</li>
</ol>
</div>
···
</h4>
<p class="text-sm text-gray-600 dark:text-gray-400">
The rotation key is a did:key that will be added to your PLC
-
record's rotationKeys array. This process uses the ATP PLC
-
operations to update your DID document.
-
<a
-
href="https://atproto.com/specs/did-plc"
-
target="_blank"
-
class="text-blue-600 dark:text-blue-400 hover:underline ml-1"
+
document's rotationKeys array. This process uses the AT Protocol's
+
PLC operations to update your DID document.
+
<Link
+
href="https://web.plc.directory/"
+
class="text-blue-600 dark:text-blue-400 ml-1"
+
isExternal
>
-
Learn more about PLC DIDs →
-
</a>
+
Learn more about did:plc
+
</Link>
</p>
</div>
+15 -6
islands/Header.tsx
···
setUser(
userData
? {
-
did: userData.did,
-
handle: userData.handle,
-
}
-
: null,
+
did: userData.did,
+
handle: userData.handle,
+
}
+
: null
);
} catch (error) {
console.error("Failed to fetch user:", error);
···
/>
<div className="flex items-center gap-3">
+
{/* Ticket booth (did:plc update) */}
+
<Button
+
href="/ticket-booth"
+
color="amber"
+
icon="/icons/ticket_bold.svg"
+
iconAlt="Ticket"
+
label="TICKET BOOTH"
+
/>
+
{/* Departures (Migration) */}
<Button
href="/migrate"
···
<div className="relative">
<Button
color="amber"
-
icon="/icons/ticket_bold.svg"
+
icon="/icons/account.svg"
iconAlt="Check-in"
label="CHECKED IN"
onClick={() => setShowDropdown(!showDropdown)}
···
<Button
href="/login"
color="amber"
-
icon="/icons/ticket_bold.svg"
+
icon="/icons/account.svg"
iconAlt="Check-in"
label="CHECK-IN"
/>
+1
static/icons/account.svg
···
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M235.2-279.59q51-38.52 113.52-60.54 62.52-22.02 131.33-22.02t131.64 22.38q62.83 22.38 113.11 60.42 34.29-41.24 53.31-91.92 19.02-50.69 19.02-108.73 0-131.57-92.78-224.35T480-797.13q-131.57 0-224.35 92.78T162.87-480q0 57.8 18.9 108.49 18.9 50.68 53.43 91.92ZM480-437.85q-59.96 0-100.93-40.86-40.98-40.86-40.98-100.81 0-59.96 40.98-100.94 40.97-40.97 100.93-40.97 59.96 0 100.93 40.97 40.98 40.98 40.98 100.94 0 59.95-40.98 100.81-40.97 40.86-100.93 40.86Zm-.02 365.98q-84.65 0-159.09-32.1-74.43-32.1-129.63-87.29-55.19-55.2-87.29-129.65-32.1-74.46-32.1-159.11 0-84.65 32.1-159.09 32.1-74.43 87.29-129.63 55.2-55.19 129.65-87.29 74.46-32.1 159.11-32.1 84.65 0 159.09 32.1 74.43 32.1 129.63 87.29 55.19 55.2 87.29 129.65 32.1 74.46 32.1 159.11 0 84.65-32.1 159.09-32.1 74.43-87.29 129.63-55.2 55.19-129.65 87.29-74.46 32.1-159.11 32.1Zm.02-91q51.8 0 97.37-14.9 45.56-14.9 84.56-42.95-39.47-28.28-84.44-43.06-44.97-14.79-97.49-14.79-52.52 0-97.37 14.79-44.85 14.78-84.33 43.06 39 28.05 84.45 42.95 45.45 14.9 97.25 14.9Zm0-358.56q25.04 0 41.57-16.53 16.52-16.52 16.52-41.56 0-25.05-16.52-41.69-16.53-16.64-41.57-16.64t-41.57 16.64q-16.52 16.64-16.52 41.69 0 25.04 16.52 41.56 16.53 16.53 41.57 16.53Zm0-58.09Zm.24 358.8Z"/></svg>