forked from coracle/caravel
Opus refactor
This commit is contained in:
@@ -2,54 +2,11 @@ import { A } from "@solidjs/router"
|
||||
import { Show, createEffect, createSignal, onCleanup } from "solid-js"
|
||||
import type { Relay, PlanId } from "@/lib/api"
|
||||
import menuDotsIcon from "@/assets/menu-dots-2.svg"
|
||||
import Field from "@/components/Field"
|
||||
import PricingTable from "@/components/PricingTable"
|
||||
|
||||
function Field(props: { label: string; children: any }) {
|
||||
return (
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-500 uppercase tracking-wide">{props.label}</dt>
|
||||
<dd class="mt-0.5 text-sm text-gray-900">{props.children}</dd>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ToggleField(props: { label: string; children: any }) {
|
||||
return (
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<dt class="text-xs font-medium text-gray-500 uppercase tracking-wide">{props.label}</dt>
|
||||
<dd class="text-sm text-gray-900">{props.children}</dd>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ToggleButton(props: {
|
||||
enabled: boolean
|
||||
disabled?: boolean
|
||||
onToggle?: () => void
|
||||
onLabel?: string
|
||||
offLabel?: string
|
||||
}) {
|
||||
const label = () => (props.enabled ? (props.onLabel ?? "Enabled") : (props.offLabel ?? "Disabled"))
|
||||
|
||||
return (
|
||||
<div class="inline-flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={props.enabled}
|
||||
aria-label={label()}
|
||||
class={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${props.enabled ? "bg-blue-600" : "bg-gray-300"} disabled:opacity-50`}
|
||||
onClick={props.onToggle}
|
||||
disabled={props.disabled}
|
||||
>
|
||||
<span
|
||||
class={`inline-block h-5 w-5 transform rounded-full bg-white shadow transition-transform ${props.enabled ? "translate-x-5" : "translate-x-0.5"}`}
|
||||
/>
|
||||
</button>
|
||||
<span class={`text-xs font-medium ${props.enabled ? "text-blue-700" : "text-gray-500"}`}>{label()}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
import ToggleButton from "@/components/ToggleButton"
|
||||
import ToggleField from "@/components/ToggleField"
|
||||
import { setToastMessage } from "@/components/Toast"
|
||||
|
||||
function DetailSection(props: { title: string; children: any }) {
|
||||
return (
|
||||
@@ -116,7 +73,12 @@ export default function RelayDetailCard(props: RelayDetailCardProps) {
|
||||
|
||||
async function changePlan(plan: PlanId) {
|
||||
setPlan(plan)
|
||||
props.onUpdatePlan?.(plan)
|
||||
try {
|
||||
await props.onUpdatePlan?.(plan)
|
||||
setToastMessage(`Plan updated to ${plan}`, "success")
|
||||
} catch {
|
||||
// error is handled by the caller
|
||||
}
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { A } from "@solidjs/router"
|
||||
import type { Relay } from "@/lib/api"
|
||||
|
||||
type RelayListItemProps = {
|
||||
relay: Relay
|
||||
href: string
|
||||
showTenant?: boolean
|
||||
}
|
||||
|
||||
export default function RelayListItem(props: RelayListItemProps) {
|
||||
return (
|
||||
<li>
|
||||
<A href={props.href} class="block rounded-lg border border-gray-200 bg-white p-4 hover:border-gray-300">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p class="font-medium text-gray-900">{props.relay.info_name || props.relay.subdomain}</p>
|
||||
<p class="text-xs text-gray-500">{props.relay.subdomain}.spaces.coracle.social</p>
|
||||
{props.showTenant && (
|
||||
<p class="text-xs text-gray-500 break-all mt-1">Tenant: {props.relay.tenant}</p>
|
||||
)}
|
||||
</div>
|
||||
<p class="text-xs uppercase tracking-wide text-gray-500">{props.relay.status}</p>
|
||||
</div>
|
||||
</A>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
type SearchInputProps = {
|
||||
value: string
|
||||
onInput: (value: string) => void
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
export default function SearchInput(props: SearchInputProps) {
|
||||
return (
|
||||
<div class="relative">
|
||||
<span class="pointer-events-none absolute inset-y-0 left-3 flex items-center text-gray-400">
|
||||
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="M21 21l-4.3-4.3" />
|
||||
</svg>
|
||||
</span>
|
||||
<input
|
||||
type="search"
|
||||
value={props.value}
|
||||
onInput={(e) => props.onInput(e.currentTarget.value)}
|
||||
placeholder={props.placeholder ?? "Search..."}
|
||||
class="w-full border border-gray-300 rounded-lg py-2 pl-10 pr-3 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
import { Show, createEffect, createSignal, onCleanup } from "solid-js"
|
||||
|
||||
export const [toastMessage, setToastMessage] = createSignal("")
|
||||
type ToastVariant = "error" | "success"
|
||||
|
||||
const [toastVariant, setToastVariant] = createSignal<ToastVariant>("error")
|
||||
export const [toastMessage, setRawToastMessage] = createSignal("")
|
||||
|
||||
export function setToastMessage(message: string, variant: ToastVariant = "error") {
|
||||
setToastVariant(variant)
|
||||
setRawToastMessage(message)
|
||||
}
|
||||
|
||||
export default function Toast() {
|
||||
const [visible, setVisible] = createSignal(false)
|
||||
@@ -57,10 +65,12 @@ export default function Toast() {
|
||||
<Show when={toastMessage()}>
|
||||
<div
|
||||
role="alert"
|
||||
class="fixed bottom-4 right-4 z-50 max-w-md rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-base text-red-700 shadow-lg transition-all duration-400 ease-out"
|
||||
class="fixed bottom-4 right-4 z-50 max-w-md rounded-xl border px-4 py-3 text-base shadow-lg transition-all duration-400 ease-out"
|
||||
classList={{
|
||||
"translate-y-0 opacity-100 scale-100": visible(),
|
||||
"translate-y-3 opacity-0 scale-95": !visible(),
|
||||
"border-red-200 bg-red-50 text-red-700": toastVariant() === "error",
|
||||
"border-green-200 bg-green-50 text-green-700": toastVariant() === "success",
|
||||
}}
|
||||
>
|
||||
{toastMessage()}
|
||||
|
||||
Reference in New Issue
Block a user