feat: PlanetScale managed-MySQL preset with setup guide and host detection (Task 3)

This commit is contained in:
2026-09-09 18:05:13 +08:00
parent 28ce32ce91
commit 45e3327db4
10 changed files with 67 additions and 17 deletions
@@ -8,7 +8,7 @@ import type { ConnectionFormData } from "./connectionFormData";
export interface DetailedConnectionFormProps {
form: ConnectionFormData;
onChange: (updates: Partial<ConnectionFormData>) => void;
managedPreset?: "supabase" | "neon" | null;
managedPreset?: "supabase" | "neon" | "planetscale" | null;
}
export function DetailedConnectionForm({ form, onChange, managedPreset }: DetailedConnectionFormProps) {
@@ -7,7 +7,7 @@ import type { ConnectionFormData } from "./connectionFormData";
export interface GeneralTabProps {
form: ConnectionFormData;
onChange: (updates: Partial<ConnectionFormData>) => void;
managedPreset?: "supabase" | "neon" | null;
managedPreset?: "supabase" | "neon" | "planetscale" | null;
}
const AUTH_OPTIONS = ["User & Password"];
@@ -32,7 +32,7 @@ export function GeneralTab({ form, onChange, managedPreset }: GeneralTabProps) {
)}
{managedPreset && (
<p className="text-xs text-accent-muted mt-1.5">
{managedPreset === "supabase" ? "Supabase" : "NeonDB"} requires SSL — enable it under SSH / SSL.
{managedPreset === "supabase" ? "Supabase" : managedPreset === "planetscale" ? "PlanetScale" : "NeonDB"} requires SSL — enable it under SSH / SSL.
</p>
)}
</div>
@@ -74,7 +74,7 @@ export function NewConnectionScreen({
}: NewConnectionScreenProps) {
const [stage, setStage] = useState<Stage>("entry");
const [managedPreset, setManagedPreset] = useState<
"supabase" | "neon" | null
"supabase" | "neon" | "planetscale" | null
>(null);
const [form, setForm] = useState<ConnectionFormData>(() =>
createEmptyForm(
@@ -99,7 +99,7 @@ export function NewConnectionScreen({
const parsed = parseConnectionString(value);
if (parsed) {
const provider =
parsed.db_type === "postgresql"
parsed.db_type === "postgresql" || parsed.db_type === "mysql"
? detectProviderFromHost(parsed.host)
: null;
setManagedPreset(provider);
@@ -139,7 +139,9 @@ export function NewConnectionScreen({
(id: ProviderId) => {
const provider = getProviderById(id)!;
setManagedPreset(
provider.isManagedPreset ? (id as "supabase" | "neon") : null,
provider.isManagedPreset
? (id as "supabase" | "neon" | "planetscale")
: null,
);
revealConfigured({
db_type: provider.dbType,
@@ -148,6 +150,7 @@ export function NewConnectionScreen({
? null
: getDefaultPort(provider.dbType),
...(provider.dbType === "sqlite" ? { host: "" } : {}),
...(id === "planetscale" ? { ssl_mode: "verify-full" } : {}),
});
},
[revealConfigured],
@@ -164,7 +167,7 @@ export function NewConnectionScreen({
const parsed = parseConnectionString(value);
if (parsed) {
const provider =
parsed.db_type === "postgresql"
parsed.db_type === "postgresql" || parsed.db_type === "mysql"
? detectProviderFromHost(parsed.host)
: null;
setManagedPreset(provider);
@@ -3,12 +3,13 @@ import { ChevronDown } from "lucide-react";
import { SETUP_GUIDES } from "../../lib/providers";
interface ProviderSetupGuideProps {
provider: "supabase" | "neon";
provider: "supabase" | "neon" | "planetscale";
}
const SSL_NOTE: Record<"supabase" | "neon", string> = {
const SSL_NOTE: Record<"supabase" | "neon" | "planetscale", string> = {
supabase: "SSL is required by Supabase.",
neon: "Neon requires SSL.",
planetscale: "PlanetScale requires SSL with identity verification.",
};
export function ProviderSetupGuide({ provider }: ProviderSetupGuideProps) {
@@ -24,7 +24,7 @@ export function ProviderTabsGrid({ selectedId, onSelect }: ProviderTabsGridProps
}`}
>
<span className="w-8 h-8 flex items-center justify-center">
{p.isManagedPreset ? <ProviderIcon id={p.id as "supabase" | "neon"} size={24} /> : <DbIcon type={p.dbType} size={24} />}
{p.isManagedPreset ? <ProviderIcon id={p.id as "supabase" | "neon" | "planetscale"} size={24} /> : <DbIcon type={p.dbType} size={24} />}
</span>
<span className="text-sm font-medium">{p.label}</span>
{p.isManagedPreset && (