assets, move memories to proper location

This commit is contained in:
2026-05-14 07:36:23 -04:00
parent 0bec3c574a
commit 1b917321cf
52 changed files with 3352 additions and 297 deletions

View File

@@ -1,4 +1,4 @@
import { Component, createSignal } from 'solid-js';
import { Component, createSignal, onMount } from 'solid-js';
import { trackWaitlistSignup } from '../hooks/useAnalytics';
interface WaitlistFormProps {
@@ -7,14 +7,29 @@ interface WaitlistFormProps {
buttonText?: string;
}
function getUtmParams() {
if (typeof window === 'undefined') return {};
const params = new URLSearchParams(window.location.search);
return {
utmSource: params.get('utm_source') || undefined,
utmMedium: params.get('utm_medium') || undefined,
utmCampaign: params.get('utm_campaign') || undefined,
};
}
const WaitlistForm: Component<WaitlistFormProps> = (props) => {
const [email, setEmail] = createSignal('');
const [name, setName] = createSignal('');
const [tier, setTier] = createSignal('basic');
const [utm, setUtm] = createSignal<Record<string, string | undefined>>({});
const [submitted, setSubmitted] = createSignal(false);
const [loading, setLoading] = createSignal(false);
const [error, setError] = createSignal('');
onMount(() => {
setUtm(getUtmParams());
});
const variant = props.variant || 'hero';
const handleSubmit = async (e: Event) => {
@@ -36,6 +51,7 @@ const WaitlistForm: Component<WaitlistFormProps> = (props) => {
email: email(),
name: name() || undefined,
tier: tier() !== 'basic' ? tier() : undefined,
...utm(),
}),
});

View File

@@ -2,12 +2,17 @@ type EventParams = Record<string, string | number | boolean | undefined>;
const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID as string | undefined;
const MIXPANEL_TOKEN = import.meta.env.VITE_MIXPANEL_TOKEN as string | undefined;
const META_PIXEL_ID = import.meta.env.VITE_META_PIXEL_ID as string | undefined;
const LINKEDIN_PARTNER_ID = import.meta.env.VITE_LINKEDIN_PARTNER_ID as string | undefined;
declare global {
interface Window {
gtag?: (command: string, target: string, params?: EventParams) => void;
mixpanel?: { track: (event: string, params?: EventParams) => void };
mixpanel?: { track: (event: string, params?: EventParams) => void; init?: (token: string) => void };
dataLayer?: unknown[];
fbq?: (...args: unknown[]) => void;
_fbq?: unknown;
lintrk?: (...args: unknown[]) => void;
}
}
@@ -43,9 +48,47 @@ function initMixpanel() {
};
}
function initMetaPixel() {
if (!META_PIXEL_ID || typeof window === 'undefined') return;
if (window.fbq) return;
window.fbq = function fbq() { window._fbq = window._fbq || []; window._fbq.push(arguments); };
const script = document.createElement('script');
script.async = true;
script.src = `https://connect.facebook.net/en_US/fbevents.js`;
document.head.appendChild(script);
window.fbq('init', META_PIXEL_ID);
window.fbq('track', 'PageView');
}
function initLinkedInInsight() {
if (!LINKEDIN_PARTNER_ID || typeof window === 'undefined') return;
if (window.lintrk) return;
window.lintrk = function lintrk() { window.lintrk.q.push(arguments); };
window.lintrk.q = [];
const script = document.createElement('script');
script.async = true;
script.src = `https://snap.licdn.com/li.lms-analytics/insight.min.js`;
document.head.appendChild(script);
}
export function initAnalytics() {
initGA();
initMixpanel();
initMetaPixel();
initLinkedInInsight();
}
export function trackMetaEvent(eventName: string, params?: EventParams) {
if (typeof window === 'undefined' || !window.fbq) return;
window.fbq('track', eventName, params);
}
export function trackLinkedInEvent() {
if (typeof window === 'undefined' || !window.lintrk) return;
window.lintrk('track', { conversion_id: null });
}
export function trackEvent(name: string, params?: EventParams) {
@@ -60,12 +103,23 @@ export function trackEvent(name: string, params?: EventParams) {
}
}
function hashEmail(email: string): string {
let hash = 0;
for (let i = 0; i < email.length; i++) {
const char = email.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash).toString(16);
}
export function trackWaitlistSignup(email: string, source?: string, tier?: string) {
trackEvent('waitlist_signup', {
email,
source: source || 'landing_page',
tier: tier || 'unknown',
});
trackMetaEvent('Lead', { value: 5.00, currency: 'USD', eventID: hashEmail(email) });
}
export function trackPageView(path: string, title?: string) {

View File

@@ -855,6 +855,31 @@ img {
color: var(--text-secondary);
}
/* Blog Waitlist CTA */
.blog-waitlist-cta {
background: var(--bg-card);
border-top: 1px solid var(--border-color);
padding: 80px 0;
text-align: center;
}
.blog-waitlist-cta h2 {
font-size: 2rem;
font-weight: 700;
margin-bottom: 12px;
}
.blog-waitlist-cta p {
color: var(--text-secondary);
margin-bottom: 32px;
font-size: 1.125rem;
}
.blog-waitlist-cta .hero-form {
max-width: 500px;
margin: 0 auto;
}
/* Responsive */
@media (max-width: 1024px) {
.features-grid {

View File

@@ -2,6 +2,7 @@ import { render } from 'solid-js/web';
import { Router, Route } from '@solidjs/router';
import App from './App';
import LandingPage from './pages/LandingPage';
import AdsLandingPage from './pages/AdsLandingPage';
import BlogPage from './pages/BlogPage';
import BlogPostPage from './pages/BlogPostPage';
import './index.css';
@@ -12,6 +13,7 @@ if (!root) throw new Error('Root element not found');
render(() => (
<Router root={App}>
<Route path="/" component={LandingPage} />
<Route path="/ads" component={AdsLandingPage} />
<Route path="/blog" component={BlogPage} />
<Route path="/blog/:slug" component={BlogPostPage} />
</Router>

View File

@@ -0,0 +1,24 @@
import { Component, onMount } from 'solid-js';
import { initAnalytics, trackPageView } from '../hooks/useAnalytics';
import HeroSection from '../components/HeroSection';
import FeaturesSection from '../components/FeaturesSection';
import TierComparison from '../components/TierComparison';
import Footer from '../components/Footer';
const AdsLandingPage: Component = () => {
onMount(() => {
initAnalytics();
trackPageView('/ads', 'ShieldAI — Ads | AI-Powered Identity Protection');
});
return (
<main>
<HeroSection />
<FeaturesSection />
<TierComparison />
<Footer />
</main>
);
};
export default AdsLandingPage;

View File

@@ -1,5 +1,6 @@
import { Component, createSignal, onMount, For } from 'solid-js';
import { initAnalytics, trackPageView } from '../hooks/useAnalytics';
import WaitlistForm from '../components/WaitlistForm';
import Footer from '../components/Footer';
interface BlogPost {
@@ -104,6 +105,14 @@ const BlogPage: Component = () => {
</div>
</section>
<section class="blog-waitlist-cta">
<div class="container">
<h2>Stay Protected</h2>
<p>Get notified when we publish new guides and early access to ShieldAI.</p>
<WaitlistForm variant="hero" buttonText="Get Notified" placeholder="your@email.com" />
</div>
</section>
<Footer />
</main>
);