Prettier format pending files
This commit is contained in:
parent
47b9b4a0bd
commit
2d25128ee1
12 changed files with 190 additions and 42 deletions
|
|
@ -1,6 +1,12 @@
|
|||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
|
|
@ -36,11 +42,15 @@ export default function LeadForm() {
|
|||
}));
|
||||
}, [inferred.full, inferred.email]);
|
||||
|
||||
const update = (k: keyof typeof form) => (e: any) => setForm({ ...form, [k]: e.target.value });
|
||||
const update = (k: keyof typeof form) => (e: any) =>
|
||||
setForm({ ...form, [k]: e.target.value });
|
||||
|
||||
const submit = async () => {
|
||||
if (!form.email) {
|
||||
toast({ title: "Email required", description: "Please provide a valid email." });
|
||||
toast({
|
||||
title: "Email required",
|
||||
description: "Please provide a valid email.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
|
|
@ -53,10 +63,17 @@ export default function LeadForm() {
|
|||
const ok = r.ok;
|
||||
const data = await r.json().catch(() => ({}));
|
||||
if (!ok) throw new Error(data?.error || `Request failed (${r.status})`);
|
||||
toast({ title: "Thanks!", description: "We’ll follow up shortly with next steps." });
|
||||
toast({
|
||||
title: "Thanks!",
|
||||
description: "We’ll follow up shortly with next steps.",
|
||||
});
|
||||
setForm({ ...form, message: "" });
|
||||
} catch (e: any) {
|
||||
toast({ title: "Submission failed", description: e?.message || "Try again later.", variant: "destructive" as any });
|
||||
toast({
|
||||
title: "Submission failed",
|
||||
description: e?.message || "Try again later.",
|
||||
variant: "destructive" as any,
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
|
@ -67,39 +84,81 @@ export default function LeadForm() {
|
|||
<Card className="border-border/40 bg-card/60 backdrop-blur">
|
||||
<CardHeader>
|
||||
<CardTitle>Start a Wix project</CardTitle>
|
||||
<CardDescription>Tell us a bit about your goals. We’ll get back within 1 business day.</CardDescription>
|
||||
<CardDescription>
|
||||
Tell us a bit about your goals. We’ll get back within 1 business
|
||||
day.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="name">Name</Label>
|
||||
<Input id="name" value={form.name} onChange={update("name")} placeholder="Your name" />
|
||||
<Input
|
||||
id="name"
|
||||
value={form.name}
|
||||
onChange={update("name")}
|
||||
placeholder="Your name"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input id="email" value={form.email} onChange={update("email")} placeholder="you@company.com" />
|
||||
<Input
|
||||
id="email"
|
||||
value={form.email}
|
||||
onChange={update("email")}
|
||||
placeholder="you@company.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="company">Company</Label>
|
||||
<Input id="company" value={form.company} onChange={update("company")} placeholder="Company Inc." />
|
||||
<Input
|
||||
id="company"
|
||||
value={form.company}
|
||||
onChange={update("company")}
|
||||
placeholder="Company Inc."
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="website">Website</Label>
|
||||
<Input id="website" value={form.website} onChange={update("website")} placeholder="https://example.com" />
|
||||
<Input
|
||||
id="website"
|
||||
value={form.website}
|
||||
onChange={update("website")}
|
||||
placeholder="https://example.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="budget">Budget</Label>
|
||||
<Input id="budget" value={form.budget} onChange={update("budget")} placeholder="e.g. $5k–$10k" />
|
||||
<Input
|
||||
id="budget"
|
||||
value={form.budget}
|
||||
onChange={update("budget")}
|
||||
placeholder="e.g. $5k–$10k"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="timeline">Timeline</Label>
|
||||
<Input id="timeline" value={form.timeline} onChange={update("timeline")} placeholder="e.g. 4–6 weeks" />
|
||||
<Input
|
||||
id="timeline"
|
||||
value={form.timeline}
|
||||
onChange={update("timeline")}
|
||||
placeholder="e.g. 4–6 weeks"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<Label htmlFor="message">Project overview</Label>
|
||||
<Textarea id="message" value={form.message} onChange={update("message")} placeholder="What are you building? Who is it for? What does success look like?" />
|
||||
<Textarea
|
||||
id="message"
|
||||
value={form.message}
|
||||
onChange={update("message")}
|
||||
placeholder="What are you building? Who is it for? What does success look like?"
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Button onClick={submit} disabled={loading} className="w-full md:w-auto">
|
||||
<Button
|
||||
onClick={submit}
|
||||
disabled={loading}
|
||||
className="w-full md:w-auto"
|
||||
>
|
||||
{loading ? "Submitting…" : "Submit"}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { PricingTier } from "@/data/wix/pricing";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
} from "@/components/ui/card";
|
||||
|
||||
export default function ServiceCard({
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ export default function WixHero() {
|
|||
<CardContent className="p-6 md:p-10">
|
||||
<div className="flex flex-col gap-4 md:gap-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className="border-aethex-400/50 text-aethex-300">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-aethex-400/50 text-aethex-300"
|
||||
>
|
||||
Official Wix Agency Partner
|
||||
</Badge>
|
||||
</div>
|
||||
|
|
@ -18,8 +21,9 @@ export default function WixHero() {
|
|||
Wix & Wix Studio Sites — designed, built, and scaled by AeThex
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-base md:text-lg max-w-3xl">
|
||||
We ship fast, accessible, and SEO-ready sites using Wix Studio. From launch
|
||||
microsites to full eCommerce, your team gets a site you can actually edit.
|
||||
We ship fast, accessible, and SEO-ready sites using Wix Studio.
|
||||
From launch microsites to full eCommerce, your team gets a site
|
||||
you can actually edit.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Button asChild>
|
||||
|
|
@ -32,7 +36,13 @@ export default function WixHero() {
|
|||
<Link to="/wix/faq">FAQ</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline">
|
||||
<a href="https://www.wix.com/studio/community/partners/aethex" target="_blank" rel="noreferrer">Wix Partner Profile</a>
|
||||
<a
|
||||
href="https://www.wix.com/studio/community/partners/aethex"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Wix Partner Profile
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -271,11 +271,17 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||
// Auto-seed owner roles if logging in as site owner
|
||||
const normalizedEmail = userProfile?.email?.toLowerCase();
|
||||
if (normalizedEmail === "mrpiglr@gmail.com" && !r.includes("owner")) {
|
||||
const seeded = Array.from(new Set(["owner", "admin", "founder", ...r]));
|
||||
const seeded = Array.from(
|
||||
new Set(["owner", "admin", "founder", ...r]),
|
||||
);
|
||||
await aethexRoleService.setUserRoles(userId, seeded);
|
||||
r = seeded;
|
||||
}
|
||||
if (normalizedEmail && /@aethex\.dev$/i.test(normalizedEmail) && !r.includes("staff")) {
|
||||
if (
|
||||
normalizedEmail &&
|
||||
/@aethex\.dev$/i.test(normalizedEmail) &&
|
||||
!r.includes("staff")
|
||||
) {
|
||||
const seeded = Array.from(new Set(["staff", ...r]));
|
||||
await aethexRoleService.setUserRoles(userId, seeded);
|
||||
r = seeded;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
export type Partner = { name: string; tier?: string | null; url?: string | null };
|
||||
export type Partner = {
|
||||
name: string;
|
||||
tier?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
|
||||
export const partners: Partner[] = [
|
||||
{ name: "Wix", tier: "Official Agency Partner", url: "https://www.wix.com/studio/community/partners/aethex" },
|
||||
{
|
||||
name: "Wix",
|
||||
tier: "Official Agency Partner",
|
||||
url: "https://www.wix.com/studio/community/partners/aethex",
|
||||
},
|
||||
];
|
||||
|
||||
export default partners;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ export const pricing: PricingTier[] = [
|
|||
id: "starter",
|
||||
name: "Starter",
|
||||
price: "From $2,500",
|
||||
description: "Single-page or small brochure site with strong SEO and fast turnaround.",
|
||||
description:
|
||||
"Single-page or small brochure site with strong SEO and fast turnaround.",
|
||||
features: [
|
||||
"1–3 pages",
|
||||
"Wix Studio setup and theme",
|
||||
|
|
|
|||
|
|
@ -49,16 +49,21 @@ function OrgLogin() {
|
|||
<div className="space-y-3 p-3 rounded border border-border/40 bg-background/50">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-sm font-medium">Aethex Login (org)</div>
|
||||
<Badge variant="outline" className="uppercase">@aethex.dev</Badge>
|
||||
<Badge variant="outline" className="uppercase">
|
||||
@aethex.dev
|
||||
</Badge>
|
||||
</div>
|
||||
{sent ? (
|
||||
<Alert className="border-aethex-400/30 bg-aethex-500/10 text-foreground">
|
||||
<AlertTitle>Check your inbox</AlertTitle>
|
||||
<AlertDescription>
|
||||
We sent a magic link to {email}. If email isn’t configured, a manual link is shown below.
|
||||
We sent a magic link to {email}. If email isn’t configured, a manual
|
||||
link is shown below.
|
||||
</AlertDescription>
|
||||
{sent.startsWith("http") && (
|
||||
<p className="mt-2 break-all rounded bg-background/60 px-3 py-2 font-mono text-xs text-foreground/90">{sent}</p>
|
||||
<p className="mt-2 break-all rounded bg-background/60 px-3 py-2 font-mono text-xs text-foreground/90">
|
||||
{sent}
|
||||
</p>
|
||||
)}
|
||||
</Alert>
|
||||
) : null}
|
||||
|
|
@ -88,7 +93,10 @@ function OrgLogin() {
|
|||
const r = await fetch("/api/auth/send-org-link", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ email, redirectTo: window.location.origin + "/dashboard" }),
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
redirectTo: window.location.origin + "/dashboard",
|
||||
}),
|
||||
});
|
||||
if (!r.ok) {
|
||||
const msg = await r.text().catch(() => "");
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ export default function ProjectsAdmin() {
|
|||
const isOwner = Boolean(
|
||||
user?.email?.toLowerCase() === "mrpiglr@gmail.com" ||
|
||||
(roles || []).some((r) =>
|
||||
["owner", "admin", "founder", "staff"].includes(String(r).toLowerCase()),
|
||||
["owner", "admin", "founder", "staff"].includes(
|
||||
String(r).toLowerCase(),
|
||||
),
|
||||
),
|
||||
);
|
||||
const [list, setList] = useState<any[]>([]);
|
||||
|
|
|
|||
|
|
@ -13,10 +13,17 @@ export default function Wix() {
|
|||
<WixHero />
|
||||
|
||||
<section className="container mx-auto px-4 py-10">
|
||||
<h2 className="text-2xl md:text-3xl font-semibold mb-6">What we build</h2>
|
||||
<h2 className="text-2xl md:text-3xl font-semibold mb-6">
|
||||
What we build
|
||||
</h2>
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
{services.map((s) => (
|
||||
<ServiceCard key={s.id} name={s.name} summary={s.summary} highlights={s.highlights} />
|
||||
<ServiceCard
|
||||
key={s.id}
|
||||
name={s.name}
|
||||
summary={s.summary}
|
||||
highlights={s.highlights}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import Layout from "@/components/Layout";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import studies from "@/data/wix/caseStudies";
|
||||
|
||||
export default function WixCaseStudies() {
|
||||
|
|
@ -7,10 +13,15 @@ export default function WixCaseStudies() {
|
|||
<Layout>
|
||||
<div className="min-h-screen bg-aethex-gradient py-10">
|
||||
<section className="container mx-auto px-4">
|
||||
<h1 className="text-3xl md:text-4xl font-bold mb-6">Wix Case Studies</h1>
|
||||
<h1 className="text-3xl md:text-4xl font-bold mb-6">
|
||||
Wix Case Studies
|
||||
</h1>
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
{studies.map((c) => (
|
||||
<Card key={c.id} className="border-border/40 bg-card/60 backdrop-blur">
|
||||
<Card
|
||||
key={c.id}
|
||||
className="border-border/40 bg-card/60 backdrop-blur"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle>{c.title}</CardTitle>
|
||||
<CardDescription>{c.summary}</CardDescription>
|
||||
|
|
@ -18,7 +29,10 @@ export default function WixCaseStudies() {
|
|||
<CardContent>
|
||||
<div className="grid grid-cols-3 gap-3 text-sm">
|
||||
{c.metrics.map((m, i) => (
|
||||
<div key={i} className="rounded border border-border/40 p-3">
|
||||
<div
|
||||
key={i}
|
||||
className="rounded border border-border/40 p-3"
|
||||
>
|
||||
<div className="text-muted-foreground">{m.label}</div>
|
||||
<div className="font-semibold">{m.value}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -147,11 +147,17 @@ export function createServer() {
|
|||
// Org domain magic-link sender (Aethex)
|
||||
app.post("/api/auth/send-org-link", async (req, res) => {
|
||||
try {
|
||||
const { email, redirectTo } = (req.body || {}) as { email?: string; redirectTo?: string };
|
||||
const target = String(email || "").trim().toLowerCase();
|
||||
const { email, redirectTo } = (req.body || {}) as {
|
||||
email?: string;
|
||||
redirectTo?: string;
|
||||
};
|
||||
const target = String(email || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (!target) return res.status(400).json({ error: "email is required" });
|
||||
const allowed = /@aethex\.dev$/i.test(target);
|
||||
if (!allowed) return res.status(403).json({ error: "domain not allowed" });
|
||||
if (!allowed)
|
||||
return res.status(403).json({ error: "domain not allowed" });
|
||||
|
||||
if (!adminSupabase?.auth?.admin) {
|
||||
return res.status(500).json({ error: "Supabase admin unavailable" });
|
||||
|
|
@ -163,7 +169,10 @@ export function createServer() {
|
|||
process.env.SITE_URL ??
|
||||
"https://aethex.dev";
|
||||
|
||||
const toUrl = typeof redirectTo === "string" && redirectTo.startsWith("http") ? redirectTo : fallbackRedirect;
|
||||
const toUrl =
|
||||
typeof redirectTo === "string" && redirectTo.startsWith("http")
|
||||
? redirectTo
|
||||
: fallbackRedirect;
|
||||
|
||||
const { data, error } = await adminSupabase.auth.admin.generateLink({
|
||||
type: "magiclink" as any,
|
||||
|
|
@ -1310,7 +1319,16 @@ export function createServer() {
|
|||
|
||||
// Leads: capture website leads (Wix microsite and others)
|
||||
app.post("/api/leads", async (req, res) => {
|
||||
const { name, email, company, website, budget, timeline, message, source } = (req.body || {}) as {
|
||||
const {
|
||||
name,
|
||||
email,
|
||||
company,
|
||||
website,
|
||||
budget,
|
||||
timeline,
|
||||
message,
|
||||
source,
|
||||
} = (req.body || {}) as {
|
||||
name?: string;
|
||||
email?: string;
|
||||
company?: string;
|
||||
|
|
@ -1335,7 +1353,10 @@ export function createServer() {
|
|||
|
||||
try {
|
||||
if (emailService.isConfigured) {
|
||||
const base = process.env.PUBLIC_BASE_URL || process.env.SITE_URL || "https://aethex.dev";
|
||||
const base =
|
||||
process.env.PUBLIC_BASE_URL ||
|
||||
process.env.SITE_URL ||
|
||||
"https://aethex.dev";
|
||||
await (emailService as any).sendInviteEmail({
|
||||
to: process.env.VERIFY_SUPPORT_EMAIL || "support@aethex.biz",
|
||||
inviteUrl: `${base}/wix`,
|
||||
|
|
|
|||
Loading…
Reference in a new issue