Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions apps/web/src/components/HireCJModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React from "react";
import {
X,
Mail,
Briefcase,
MapPin,
Calendar,
Mic,
Radio,
Volume2,
Settings,
Cable,
} from "lucide-react";

interface HireCJModalProps {
isOpen: boolean;
onClose: () => void;
}

const HireCJModal: React.FC<HireCJModalProps> = ({ isOpen, onClose }) => {
if (!isOpen) return null;

const skills = [
{ icon: Mic, text: "FOH & Monitors A1 - Front of House & Monitor Mix Engineering" },
{ icon: Volume2, text: "A2 & Stage Support - Audio Assistant, Patch, RF Coordination" },
{
icon: Settings,
text: "System Design & Deployment - PA Architecture, Setup, Tuning & Optimization",
},
{ icon: Radio, text: "RF & Intercom Systems - Wireless Coordination, Clear-Com, RTS" },
{ icon: Cable, text: "Festival & Tour Production - Signal Flow, System Integration" },
{
icon: Briefcase,
text: "Production Management - Technical Director, Project Manager, Show Coordination",
},
];

return (
<>
<div
className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
onClick={onClose}
>
<div
className="bg-gray-950/95 backdrop-blur-lg border border-gray-700 rounded-lg shadow-2xl shadow-indigo-900/20 max-w-2xl w-full max-h-[90vh] overflow-y-auto"
onClick={(e) => e.stopPropagation()}
>
<div className="p-6">
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold text-white">
Hire CJ - Professional Audio Engineer
</h2>
<button
onClick={onClose}
className="p-2 rounded-full hover:bg-gray-800 transition-colors"
>
<X className="h-5 w-5 text-gray-400" />
</button>
</div>

<div className="space-y-6">
<div className="bg-indigo-900/30 border border-indigo-700/50 rounded-lg p-5">
<h3 className="text-lg font-semibold text-indigo-300 mb-3 flex items-center">
<Mail className="h-5 w-5 mr-2" />
Get In Touch
</h3>
<p className="text-white mb-2">
Ready to elevate your event's audio experience? Contact me at:
</p>
<a
href="mailto:cj@sounddocs.org"
className="text-indigo-400 hover:text-indigo-300 font-semibold text-lg transition-colors"
>
cj@sounddocs.org
</a>
<p className="text-gray-300 mt-3">Please include:</p>
<ul className="list-disc list-inside text-gray-300 mt-2 space-y-1">
<li>Event dates and location</li>
<li>Type of event (concert, corporate, festival, theater, etc.)</li>
<li>Specific role needed (FOH, Monitors, System Tech, RF, etc.)</li>
<li>Venue size and expected attendance</li>
<li>Equipment details if available</li>
</ul>
</div>

<div>
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
<Briefcase className="h-5 w-5 mr-2 text-indigo-400" />
Skills & Services
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{skills.map((skill, index) => {
const Icon = skill.icon;
return (
<div
key={index}
className="flex items-start p-3 bg-gray-800/50 rounded-lg hover:bg-gray-800/70 transition-colors"
>
<Icon className="h-5 w-5 text-indigo-400 mr-3 mt-0.5 flex-shrink-0" />
<span className="text-gray-200 text-sm">{skill.text}</span>
</div>
);
})}
</div>
</div>

<div className="bg-gray-800/50 rounded-lg p-5">
<h3 className="text-lg font-semibold text-white mb-3 flex items-center">
<MapPin className="h-5 w-5 mr-2 text-indigo-400" />
Availability
</h3>
<p className="text-gray-300">
Available for events worldwide. Based in the United States with extensive touring
experience. Passport ready for international events.
</p>
</div>

<div className="bg-gray-800/50 rounded-lg p-5">
<h3 className="text-lg font-semibold text-white mb-3 flex items-center">
<Calendar className="h-5 w-5 mr-2 text-indigo-400" />
Booking Information
</h3>
<p className="text-gray-300">
For the best chance of securing your dates, please reach out as early as possible.
I work with a production company full time, and my schedule fills up quickly
</p>
</div>
</div>

<div className="mt-6 flex justify-end">
<button
onClick={onClose}
className="px-6 py-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors"
>
Close
</button>
</div>
</div>
</div>
</div>
</>
);
};

export default HireCJModal;
92 changes: 70 additions & 22 deletions apps/web/src/components/WheresCJ.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import { MapPin, Loader2, WifiOff } from "lucide-react";
import { MapPin, Loader2, WifiOff, Briefcase } from "lucide-react";
import { fetchCurrentCJLocation } from "../lib/supabase";
import HireCJModal from "./HireCJModal";

interface LocationData {
location_name: string;
Expand All @@ -11,6 +12,8 @@ const WheresCJ: React.FC = () => {
const [location, setLocation] = useState<LocationData | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isHovered, setIsHovered] = useState(false);
const [showHireModal, setShowHireModal] = useState(false);
const [hoverTimeout, setHoverTimeout] = useState<NodeJS.Timeout | null>(null);

useEffect(() => {
const getLocation = async () => {
Expand Down Expand Up @@ -40,10 +43,24 @@ const WheresCJ: React.FC = () => {

if (!location) {
return (
<div className="p-4 text-center">
<WifiOff className="h-8 w-8 mx-auto mb-2 text-gray-500" />
<h3 className="font-bold text-white mb-1">Off The Grid</h3>
<p className="text-sm text-gray-400">CJ is currently unavailable. Check back later!</p>
<div className="p-4">
<div className="text-center">
<WifiOff className="h-8 w-8 mx-auto mb-2 text-gray-500" />
<h3 className="font-bold text-white mb-1">Off The Grid</h3>
<p className="text-sm text-gray-400">CJ is currently unavailable. Check back later!</p>
</div>
<div className="mt-4 pt-3 border-t border-gray-700">
<button
onClick={(e) => {
e.stopPropagation();
setShowHireModal(true);
}}
className="w-full flex items-center justify-center bg-indigo-600 hover:bg-indigo-500 text-white font-semibold py-2 px-4 rounded-lg transition-colors"
>
<Briefcase className="h-4 w-4 mr-2" />
Hire CJ for Your Event
</button>
</div>
</div>
);
}
Expand All @@ -62,7 +79,17 @@ const WheresCJ: React.FC = () => {
)}
</div>
</div>
<div className="mt-4 pt-3 border-t border-gray-700">
<div className="mt-4 pt-3 border-t border-gray-700 space-y-3">
<button
onClick={(e) => {
e.stopPropagation();
setShowHireModal(true);
}}
className="w-full flex items-center justify-center bg-indigo-600 hover:bg-indigo-500 text-white font-semibold py-2 px-4 rounded-lg transition-colors"
>
<Briefcase className="h-4 w-4 mr-2" />
Hire CJ for Your Event
</button>
<p className="text-xs text-center text-indigo-300 bg-indigo-900/50 rounded-md p-2">
Find me and say hi to get some exclusive SoundDocs stickers!
</p>
Expand All @@ -71,25 +98,46 @@ const WheresCJ: React.FC = () => {
);
};

const handleMouseEnter = () => {
if (hoverTimeout) {
clearTimeout(hoverTimeout);
setHoverTimeout(null);
}
setIsHovered(true);
};

const handleMouseLeave = () => {
const timeout = setTimeout(() => {
setIsHovered(false);
}, 200); // 200ms delay before closing
setHoverTimeout(timeout);
};

return (
<div
className="relative flex items-center"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div className="p-2 rounded-full bg-gray-800/50 hover:bg-gray-700/80 text-indigo-400 hover:text-indigo-300 transition-all duration-200 cursor-pointer">
<MapPin className="h-5 w-5" />
<>
<div
className="relative flex items-center"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div className="p-2 rounded-full bg-gray-800/50 hover:bg-gray-700/80 text-indigo-400 hover:text-indigo-300 transition-all duration-200 cursor-pointer">
<MapPin className="h-5 w-5" />
</div>

{isHovered && (
<div
className="absolute bottom-full right-0 mb-3 w-72 bg-gray-950/90 backdrop-blur-lg border border-gray-700 rounded-lg shadow-2xl shadow-indigo-900/20 z-50"
style={{ animation: "fadeInUp 0.3s ease-out forwards" }}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<PopoverContent />
</div>
)}
</div>

{isHovered && (
<div
className="absolute bottom-full right-0 mb-3 w-72 bg-gray-950/90 backdrop-blur-lg border border-gray-700 rounded-lg shadow-2xl shadow-indigo-900/20 z-50"
style={{ animation: "fadeInUp 0.3s ease-out forwards" }}
>
<PopoverContent />
</div>
)}
</div>
<HireCJModal isOpen={showHireModal} onClose={() => setShowHireModal(false)} />
</>
);
};

Expand Down