metatroncubeswdev 8e3a9f2882
Some checks failed
CI / ci (push) Has been cancelled
CI / docker-build (push) Has been cancelled
Publish Docker image / docker (push) Has been cancelled
Upload sourcemaps / upload (push) Has been cancelled
Replace the actual logo asset — crawlerx-mark.png was MetatronCube's cube
The previous commit pointed every <img> at crawlerx-mark.png instead of
transparent-logo.png, but both files are byte-for-byte the same
MetatronCube glass-cube artwork — the Oct-era rebrand renamed the file,
it never replaced the image. So the logo never actually changed anywhere
in the real app, despite every component referencing the "right" filename.

Added the real CrawlerX mark (the teal/amber check-and-arrow symbol
already used on the marketing site) as public/crawlerx-logo.svg and
repointed every logo reference at it.

Also: the favicons and apple-touch-icon were a literal pine tree (unrelated
leftover asset, not MetatronCube or CrawlerX), and the webmanifest's
theme_color was still MetatronCube blue. Regenerated all icon sizes from
the real mark and fixed the manifest color.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-18 21:05:08 -04:00

129 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useMutation, useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { Loader2 } from "lucide-react";
import { DEFAULT_LOCATION_CODE } from "@/shared/keyword-locations";
import { LocationSelect } from "@/client/components/LocationSelect";
import { useSession } from "@/lib/auth-client";
import { saveOnboardingSite } from "@/serverFunctions/onboardingChat";
import { OnboardingAccountMenu } from "./OnboardingAccountMenu";
import { OnboardingChatConversation } from "./OnboardingChatConversation";
import {
invalidateOnboardingChatState,
onboardingChatStateQueryOptions,
} from "./onboardingChatQueries";
// Full-viewport chat surface. Breaks out of the centered, padded AuthPageShell
// with `fixed inset-0` so the chat fills the screen. There's no header bar —
// the strategy's first message carries the context — and inner content is
// constrained to a narrow column for comfortable reading width.
function StrategyShell({ children }: { children: React.ReactNode }) {
return (
<div className="fixed inset-0 flex flex-col bg-base-100">{children}</div>
);
}
export function OnboardingChat() {
const stateQuery = useQuery(onboardingChatStateQueryOptions());
const { data: session } = useSession();
const accountMenu = <OnboardingAccountMenu email={session?.user?.email} />;
if (stateQuery.isError) {
return (
<StrategyShell>
<div className="flex flex-1 items-center justify-center p-6 text-sm text-error">
Couldn’t load your strategy. Please refresh to try again.
</div>
</StrategyShell>
);
}
if (!stateQuery.data) {
return (
<StrategyShell>
<div className="flex flex-1 items-center justify-center gap-2 p-6 text-sm text-base-content/60">
<Loader2 className="size-4 animate-spin" />
Loading…
</div>
</StrategyShell>
);
}
const { projectId, domain } = stateQuery.data;
return (
<StrategyShell>
{accountMenu}
{!domain ? (
<SiteForm projectId={projectId} />
) : (
<OnboardingChatConversation projectId={projectId} domain={domain} />
)}
</StrategyShell>
);
}
function SiteForm({ projectId }: { projectId: string }) {
const [domain, setDomain] = useState("");
const [locationCode, setLocationCode] = useState(DEFAULT_LOCATION_CODE);
const save = useMutation({
mutationFn: () =>
saveOnboardingSite({ data: { projectId, domain, locationCode } }),
onSuccess: invalidateOnboardingChatState,
});
return (
<div className="flex flex-1 items-center justify-center overflow-y-auto p-6">
<form
className="w-full max-w-md space-y-6"
onSubmit={(event) => {
event.preventDefault();
if (domain.trim()) {
save.mutate();
}
}}
>
<div className="space-y-3 text-center">
<img
src="/crawlerx-logo.svg"
alt="CrawlerX"
className="mx-auto size-10 rounded-lg"
/>
<h1 className="text-xl font-semibold">Tell us about your website.</h1>
<p className="text-sm text-base-content/60">
If you have multiple websites, you can set that up later.
</p>
</div>
<div className="space-y-4 rounded-lg border border-base-300 bg-base-100 p-5 shadow-sm">
<label className="block space-y-1">
<span className="text-sm font-medium">Your website</span>
<input
type="text"
className="input input-bordered w-full"
placeholder="example.com"
value={domain}
onChange={(event) => setDomain(event.target.value)}
/>
</label>
<label className="block space-y-1">
<span className="text-sm font-medium">
This is the country we will use when getting SEO data.
</span>
<LocationSelect value={locationCode} onChange={setLocationCode} />
</label>
<button
type="submit"
className="btn btn-primary w-full"
disabled={!domain.trim() || save.isPending}
>
{save.isPending ? "Saving…" : "Continue"}
</button>
</div>
</form>
</div>
);
}