feat: Add contributing guidelines, license, and security policy documents
- Created CONTRIBUTING.md to outline contribution process and code of conduct. - Added LICENSE file with MIT License and third-party licenses. - Introduced SECURITY.md detailing vulnerability reporting and security measures. - Established README.md in assets directory for asset management and guidelines. - Implemented index.html as the main entry point for the website. - Configured package.json for project dependencies and scripts. - Developed styles.css for custom styles and responsive design. - Set up vite.config.ts for Vite configuration and build settings.
This commit is contained in:
277
mim_web.jsx
277
mim_web.jsx
@@ -1,35 +1,11 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion";
|
||||
import {
|
||||
ArrowRight,
|
||||
Backpack,
|
||||
CheckCircle2,
|
||||
Facebook,
|
||||
Globe,
|
||||
HandHeart,
|
||||
Heart,
|
||||
Instagram,
|
||||
Mail,
|
||||
MapPin,
|
||||
Moon,
|
||||
Phone,
|
||||
Shirt,
|
||||
Sparkles,
|
||||
Star,
|
||||
SunMedium,
|
||||
Users,
|
||||
Building2,
|
||||
BookOpenText,
|
||||
Quote,
|
||||
FileText,
|
||||
ShieldCheck,
|
||||
} from "lucide-react";
|
||||
const { React, ReactDOM } = window;
|
||||
const { useState, useEffect, useRef, useMemo } = React;
|
||||
const { motion, AnimatePresence } = window.FramerMotion || {};
|
||||
|
||||
/**
|
||||
* Miracles in Motion — Tailwind + React single-file site
|
||||
* Now includes a tiny hash router, dedicated pages (Donate, Volunteers, Sponsors,
|
||||
* Stories, Testimonies, Legal), cookie banner w/ consent, analytics loader,
|
||||
* and accessibility-minded forms + policies. Keep it serverless-friendly.
|
||||
* Miracles in Motion — Complete Non-Profit Website
|
||||
* A comprehensive 501(c)3 organization website with modern design,
|
||||
* donation processing, volunteer management, and impact tracking.
|
||||
*/
|
||||
|
||||
/* ===================== Router ===================== */
|
||||
@@ -44,9 +20,41 @@ function useHashRoute() {
|
||||
return route;
|
||||
}
|
||||
|
||||
/* ===================== Icons ===================== */
|
||||
const Icons = {
|
||||
Heart: () => React.createElement('i', { className: 'fas fa-heart' }),
|
||||
HandHeart: () => React.createElement('i', { className: 'fas fa-hand-holding-heart' }),
|
||||
Users: () => React.createElement('i', { className: 'fas fa-users' }),
|
||||
Globe: () => React.createElement('i', { className: 'fas fa-globe' }),
|
||||
Star: () => React.createElement('i', { className: 'fas fa-star' }),
|
||||
CheckCircle: () => React.createElement('i', { className: 'fas fa-check-circle' }),
|
||||
Mail: () => React.createElement('i', { className: 'fas fa-envelope' }),
|
||||
Phone: () => React.createElement('i', { className: 'fas fa-phone' }),
|
||||
MapPin: () => React.createElement('i', { className: 'fas fa-map-marker-alt' }),
|
||||
Facebook: () => React.createElement('i', { className: 'fab fa-facebook-f' }),
|
||||
Instagram: () => React.createElement('i', { className: 'fab fa-instagram' }),
|
||||
Twitter: () => React.createElement('i', { className: 'fab fa-twitter' }),
|
||||
LinkedIn: () => React.createElement('i', { className: 'fab fa-linkedin-in' }),
|
||||
ArrowRight: () => React.createElement('i', { className: 'fas fa-arrow-right' }),
|
||||
Menu: () => React.createElement('i', { className: 'fas fa-bars' }),
|
||||
Close: () => React.createElement('i', { className: 'fas fa-times' }),
|
||||
Donate: () => React.createElement('i', { className: 'fas fa-donate' }),
|
||||
Volunteer: () => React.createElement('i', { className: 'fas fa-hands-helping' }),
|
||||
Calendar: () => React.createElement('i', { className: 'fas fa-calendar-alt' }),
|
||||
Award: () => React.createElement('i', { className: 'fas fa-award' }),
|
||||
Shield: () => React.createElement('i', { className: 'fas fa-shield-alt' }),
|
||||
FileText: () => React.createElement('i', { className: 'fas fa-file-text' }),
|
||||
Quote: () => React.createElement('i', { className: 'fas fa-quote-left' }),
|
||||
ChevronDown: () => React.createElement('i', { className: 'fas fa-chevron-down' }),
|
||||
ExternalLink: () => React.createElement('i', { className: 'fas fa-external-link-alt' }),
|
||||
};
|
||||
|
||||
/* ===================== Main App ===================== */
|
||||
export default function MiraclesInMotionSite() {
|
||||
const [dark, setDark] = useState(true);
|
||||
const [darkMode, setDarkMode] = useState(false);
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const route = useHashRoute();
|
||||
|
||||
useEffect(() => {
|
||||
document.title =
|
||||
route === "/"
|
||||
@@ -73,7 +81,7 @@ export default function MiraclesInMotionSite() {
|
||||
);
|
||||
}
|
||||
|
||||
function Router({ route }: { route: string }) {
|
||||
function Router({ route }) {
|
||||
switch (route) {
|
||||
case "/":
|
||||
return <HomePage />;
|
||||
@@ -106,7 +114,7 @@ function SkipToContent() {
|
||||
);
|
||||
}
|
||||
|
||||
function Nav({ dark, onToggleDark }: { dark: boolean; onToggleDark: () => void }) {
|
||||
function Nav({ darkMode, setDarkMode, mobileMenuOpen, setMobileMenuOpen }) {
|
||||
return (
|
||||
<nav className="mx-auto flex w-full max-w-7xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -239,7 +247,7 @@ function HeroShowcase() {
|
||||
);
|
||||
}
|
||||
|
||||
function TiltCard({ icon: Icon, title, desc }: { icon: any; title: string; desc: string }) {
|
||||
function TiltCard({ icon: Icon, title, desc }) {
|
||||
const x = useMotionValue(0);
|
||||
const y = useMotionValue(0);
|
||||
const rx = useTransform(y, [-50, 50], [8, -8]);
|
||||
@@ -250,7 +258,7 @@ function TiltCard({ icon: Icon, title, desc }: { icon: any; title: string; desc:
|
||||
return (
|
||||
<motion.div
|
||||
onMouseMove={(e) => {
|
||||
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
x.set(e.clientX - rect.left - rect.width / 2);
|
||||
y.set(e.clientY - rect.top - rect.height / 2);
|
||||
}}
|
||||
@@ -322,7 +330,7 @@ function Programs() {
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureCard({ icon: Icon, title, body }: { icon: any; title: string; body: string }) {
|
||||
function FeatureCard({ icon: Icon, title, body }) {
|
||||
return (
|
||||
<div className="group relative overflow-hidden rounded-2xl border border-white/30 bg-white/70 p-6 shadow-xl backdrop-blur transition hover:-translate-y-0.5 hover:shadow-2xl dark:border-white/10 dark:bg-white/5">
|
||||
<div className="absolute left-1/2 top-0 -z-10 h-40 w-40 -translate-x-1/2 rounded-full bg-gradient-to-br from-fuchsia-500/20 to-indigo-500/20 blur-2xl" />
|
||||
@@ -375,7 +383,7 @@ function Impact() {
|
||||
);
|
||||
}
|
||||
|
||||
function Stat({ label, value }: { label: string; value: number }) {
|
||||
function Stat({ label, value }) {
|
||||
return (
|
||||
<div className="relative overflow-hidden rounded-2xl border border-white/30 bg-white/70 p-6 text-center shadow-xl backdrop-blur dark:border-white/10 dark:bg-white/5">
|
||||
<div className="absolute -right-10 -top-10 h-28 w-28 rounded-full bg-gradient-to-br from-fuchsia-500/20 to-indigo-500/20 blur-2xl" />
|
||||
@@ -387,7 +395,7 @@ function Stat({ label, value }: { label: string; value: number }) {
|
||||
);
|
||||
}
|
||||
|
||||
function AnimatedNumber({ value }: { value: number }) {
|
||||
function AnimatedNumber({ value }) {
|
||||
const mv = useMotionValue(0);
|
||||
const spring = useSpring(mv, { stiffness: 90, damping: 15 });
|
||||
const rounded = useTransform(spring, (latest) => Math.floor(latest).toLocaleString());
|
||||
@@ -483,7 +491,7 @@ function GetInvolved() {
|
||||
);
|
||||
}
|
||||
|
||||
function Callout({ title, body, href, accent, icon: Icon }: { title: string; body: string; href: string; accent: string; icon: any }) {
|
||||
function Callout({ title, body, href, accent, icon: Icon }) {
|
||||
return (
|
||||
<div className="group relative overflow-hidden rounded-2xl border border-white/30 bg-white/70 p-6 shadow-xl backdrop-blur transition hover:-translate-y-0.5 hover:shadow-2xl dark:border-white/10 dark:bg-white/5">
|
||||
<div className={`absolute -right-10 -top-10 h-36 w-36 rounded-full bg-gradient-to-br ${accent} opacity-30 blur-2xl`} />
|
||||
@@ -531,7 +539,7 @@ function CTA() {
|
||||
}
|
||||
|
||||
/* ===================== New Pages ===================== */
|
||||
function PageShell({ title, icon: Icon, eyebrow, children, cta }: { title: string; icon: any; eyebrow?: string; children: React.ReactNode; cta?: React.ReactNode }) {
|
||||
function PageShell({ title, icon: Icon, eyebrow, children, cta }) {
|
||||
return (
|
||||
<section className="relative py-16 sm:py-24">
|
||||
<div className="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
|
||||
@@ -807,6 +815,191 @@ function LegalPage() {
|
||||
);
|
||||
}
|
||||
|
||||
function PolicySection({ id, title, children }: { id: string; title: string; children: React.ReactNode }) {
|
||||
/* ===================== Helper Components ===================== */
|
||||
function PolicySection({ id, title, children }) {
|
||||
return (
|
||||
<section id={id} className="rounded-2xl border bord
|
||||
<section id={id} className="rounded-2xl border border-white/30 bg-white/70 p-6 dark:border-white/10 dark:bg-white/5">
|
||||
<h2 className="font-semibold tracking-tight">{title}</h2>
|
||||
<div className="mt-3 space-y-3 text-sm text-neutral-700 dark:text-neutral-300">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionHeader({ eyebrow, title, subtitle }) {
|
||||
return (
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
{eyebrow && <div className="text-sm uppercase tracking-wider text-fuchsia-600 dark:text-fuchsia-400">{eyebrow}</div>}
|
||||
<h2 className="mt-2 text-3xl font-bold tracking-tight sm:text-4xl">{title}</h2>
|
||||
{subtitle && <p className="mt-4 text-lg text-neutral-600 dark:text-neutral-300">{subtitle}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Card({ title, icon: Icon, children }) {
|
||||
return (
|
||||
<div className="rounded-2xl border border-white/30 bg-white/70 p-6 dark:border-white/10 dark:bg-white/5">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="grid h-10 w-10 place-items-center rounded-xl bg-gradient-to-br from-fuchsia-500 to-indigo-500 text-white shadow">
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="font-semibold tracking-tight">{title}</div>
|
||||
</div>
|
||||
<div className="mt-3">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NotFoundPage() {
|
||||
return (
|
||||
<section className="relative py-24">
|
||||
<div className="mx-auto max-w-md px-4 text-center sm:px-6 lg:px-8">
|
||||
<h1 className="text-6xl font-bold text-neutral-300 dark:text-neutral-700">404</h1>
|
||||
<h2 className="mt-4 text-2xl font-semibold">Page not found</h2>
|
||||
<p className="mt-2 text-neutral-600 dark:text-neutral-400">
|
||||
The page you're looking for doesn't exist.
|
||||
</p>
|
||||
<a href="#/" className="btn-primary mt-6">
|
||||
Go home
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function BackgroundDecor() {
|
||||
return (
|
||||
<div className="pointer-events-none fixed inset-0 overflow-hidden">
|
||||
<div className="absolute -top-40 -right-40 h-80 w-80 rounded-full bg-gradient-to-br from-fuchsia-400/20 to-violet-600/20 blur-3xl" />
|
||||
<div className="absolute -bottom-40 -left-40 h-80 w-80 rounded-full bg-gradient-to-tr from-indigo-400/20 to-sky-600/20 blur-3xl" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Footer() {
|
||||
return (
|
||||
<footer className="relative mt-24 border-t border-white/30 bg-white/50 backdrop-blur dark:border-white/10 dark:bg-white/5">
|
||||
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
|
||||
<div className="grid gap-8 lg:grid-cols-4">
|
||||
<div className="lg:col-span-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<LogoMark />
|
||||
<div>
|
||||
<div className="font-semibold">Miracles in Motion</div>
|
||||
<div className="text-sm text-neutral-600 dark:text-neutral-400">Essentials for every student</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-4 max-w-md text-sm text-neutral-600 dark:text-neutral-400">
|
||||
A 501(c)(3) nonprofit providing students with school supplies, clothing, and emergency support to help them succeed.
|
||||
</p>
|
||||
<div className="mt-4 flex gap-4">
|
||||
<a href="#" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">
|
||||
<Icons.Facebook />
|
||||
</a>
|
||||
<a href="#" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">
|
||||
<Icons.Instagram />
|
||||
</a>
|
||||
<a href="#" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">
|
||||
<Icons.Twitter />
|
||||
</a>
|
||||
<a href="#" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">
|
||||
<Icons.LinkedIn />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">Get Involved</h3>
|
||||
<ul className="mt-4 space-y-2 text-sm">
|
||||
<li><a href="#/donate" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">Donate</a></li>
|
||||
<li><a href="#/volunteers" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">Volunteer</a></li>
|
||||
<li><a href="#/sponsors" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">Corporate Partnerships</a></li>
|
||||
<li><a href="#/stories" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">Success Stories</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">Organization</h3>
|
||||
<ul className="mt-4 space-y-2 text-sm">
|
||||
<li><a href="#/testimonies" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">Testimonials</a></li>
|
||||
<li><a href="#/legal" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">Legal & Policies</a></li>
|
||||
<li><a href="mailto:contact@miraclesinmotion.org" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">Contact Us</a></li>
|
||||
<li><a href="tel:+15551234567" className="text-neutral-600 hover:text-fuchsia-600 dark:text-neutral-400">(555) 123-4567</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 border-t border-white/30 pt-8 text-center text-xs text-neutral-500 dark:border-white/10 dark:text-neutral-400">
|
||||
<p>© 2024 Miracles in Motion. All rights reserved. EIN: 12-3456789</p>
|
||||
<p className="mt-1">501(c)(3) nonprofit organization. Donations are tax-deductible to the extent allowed by law.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
function StickyDonate() {
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 z-50 md:hidden">
|
||||
<a
|
||||
href="#/donate"
|
||||
className="flex h-14 w-14 items-center justify-center rounded-full bg-gradient-to-br from-fuchsia-500 to-indigo-500 text-white shadow-lg shadow-fuchsia-500/25 transition hover:scale-105"
|
||||
>
|
||||
<Icons.Heart />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CookieBanner() {
|
||||
const [show, setShow] = useState(true);
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 left-4 right-4 z-50 rounded-2xl border border-white/30 bg-white/90 p-4 shadow-xl backdrop-blur dark:border-white/10 dark:bg-black/90 md:max-w-md md:right-auto">
|
||||
<p className="text-sm">
|
||||
We use cookies to improve your experience. By continuing, you agree to our cookie policy.
|
||||
</p>
|
||||
<div className="mt-3 flex gap-2">
|
||||
<button
|
||||
onClick={() => setShow(false)}
|
||||
className="btn-primary text-xs"
|
||||
>
|
||||
Accept
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShow(false)}
|
||||
className="btn-secondary text-xs"
|
||||
>
|
||||
Decline
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AnalyticsLoader() {
|
||||
useEffect(() => {
|
||||
// Load Google Analytics or other analytics here
|
||||
// This is a placeholder for actual analytics implementation
|
||||
}, []);
|
||||
return null;
|
||||
}
|
||||
|
||||
function Magnetic({ children }) {
|
||||
return (
|
||||
<div className="relative">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Add missing icon components
|
||||
const {
|
||||
HandHeart, Users, Globe, Star, CheckCircle2, ArrowRight,
|
||||
Heart, MapPin, Mail, Phone, Sparkles, FileText, Quote,
|
||||
Moon, SunMedium, Backpack, Shirt, Building2, BookOpenText,
|
||||
ShieldCheck
|
||||
} = Icons;
|
||||
|
||||
// React DOM render
|
||||
if (typeof ReactDOM !== 'undefined' && document.getElementById('root')) {
|
||||
ReactDOM.render(React.createElement(MiraclesInMotionSite), document.getElementById('root'));
|
||||
}
|
||||
Reference in New Issue
Block a user