import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import Layout from "@/components/Layout"; import SEO from "@/components/SEO"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { useAuth } from "@/contexts/AuthContext"; import TrackUploadModal from "@/components/ethos/TrackUploadModal"; import TrackMetadataForm from "@/components/ethos/TrackMetadataForm"; import { ethosStorage, getAudioDuration } from "@/lib/ethos-storage"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { useAethexToast } from "@/hooks/use-aethex-toast"; import { Upload, Music, Settings, CheckCircle, Clock } from "lucide-react"; import EcosystemLicenseModal from "@/components/ethos/EcosystemLicenseModal"; const SKILLS = [ "Synthwave", "Orchestral", "SFX Design", "Game Audio", "Ambient", "Electronic", "Cinematic", "Jazz", "Hip-Hop", "Folk", ]; interface ArtistProfile { skills: string[]; for_hire: boolean; bio?: string; portfolio_url?: string; sample_price_track?: number; sample_price_sfx?: number; sample_price_score?: number; turnaround_days?: number; verified?: boolean; ecosystem_license_accepted?: boolean; price_list?: { track_custom?: number; sfx_pack?: number; full_score?: number; day_rate?: number; contact_for_quote?: boolean; }; } interface VerificationStatus { status: "pending" | "approved" | "rejected" | "none"; submitted_at?: string; rejection_reason?: string; } export default function ArtistSettings() { const { user } = useAuth(); const navigate = useNavigate(); const toast = useAethexToast(); const [profile, setProfile] = useState({ skills: [], for_hire: true, }); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [uploadModalOpen, setUploadModalOpen] = useState(false); const [currentFile, setCurrentFile] = useState(null); const [showMetadataForm, setShowMetadataForm] = useState(false); const [verificationStatus, setVerificationStatus] = useState({ status: "none", }); const [isSubmittingVerification, setIsSubmittingVerification] = useState(false); const [submissionNotes, setSubmissionNotes] = useState(""); const [portfolioLinks, setPortfolioLinks] = useState(""); const [showLicenseModal, setShowLicenseModal] = useState(false); const [isAcceptingLicense, setIsAcceptingLicense] = useState(false); useEffect(() => { if (!user) { navigate("/login"); return; } const fetchProfile = async () => { try { const res = await fetch(`/api/ethos/artists?id=${user.id}`, { headers: { "x-user-id": user.id }, }); if (res.ok) { const data = await res.json(); setProfile({ skills: data.skills || [], for_hire: data.for_hire ?? true, bio: data.bio, portfolio_url: data.portfolio_url, sample_price_track: data.sample_price_track, sample_price_sfx: data.sample_price_sfx, sample_price_score: data.sample_price_score, turnaround_days: data.turnaround_days, verified: data.verified, }); } // Fetch verification status const verRes = await fetch(`/api/ethos/verification?status=pending`, { headers: { "x-user-id": user.id }, }); if (verRes.ok) { const { data: requests } = await verRes.json(); const userRequest = requests?.find((r: any) => r.user_id === user.id); if (userRequest) { setVerificationStatus({ status: userRequest.status, submitted_at: userRequest.submitted_at, rejection_reason: userRequest.rejection_reason, }); } } } catch (error) { console.error("Failed to fetch profile:", error); } finally { setLoading(false); } }; fetchProfile(); }, [user, navigate]); const handleSubmitVerification = async () => { if (!user) return; setIsSubmittingVerification(true); try { const response = await fetch("/api/ethos/verification", { method: "POST", headers: { "Content-Type": "application/json", "x-user-id": user.id, }, body: JSON.stringify({ action: "submit", submission_notes: submissionNotes, portfolio_links: portfolioLinks .split("\n") .filter((link) => link.trim()), }), }); if (response.ok) { const { data } = await response.json(); setVerificationStatus({ status: "pending", submitted_at: data.submitted_at, }); setSubmissionNotes(""); setPortfolioLinks(""); toast.success({ title: "Verification request submitted", description: "Your application has been sent to the Ethos Guild team for review. You'll be notified via email of any updates.", }); } else { throw new Error("Failed to submit verification request"); } } catch (error) { toast.error({ title: "Error", description: String(error), }); } finally { setIsSubmittingVerification(false); } }; const toggleSkill = (skill: string) => { setProfile((prev) => ({ ...prev, skills: prev.skills.includes(skill) ? prev.skills.filter((s) => s !== skill) : [...prev.skills, skill], })); }; const handleSave = async () => { if (!user) return; setSaving(true); try { const res = await fetch(`/api/ethos/artists`, { method: "PUT", headers: { "x-user-id": user.id, "Content-Type": "application/json", }, body: JSON.stringify(profile), }); if (res.ok) { toast.success({ title: "Profile updated", description: "Your Ethos artist profile has been saved", }); } else { throw new Error("Failed to save profile"); } } catch (error) { toast.error({ title: "Error", description: String(error), }); } finally { setSaving(false); } }; const handleFileSelected = (file: File) => { // Show ecosystem license modal on first upload if not already accepted if (!profile.ecosystem_license_accepted) { setCurrentFile(file); setShowLicenseModal(true); } else { setCurrentFile(file); setShowMetadataForm(true); } }; const handleAcceptEcosystemLicense = async () => { if (!user) return; setIsAcceptingLicense(true); try { // Update profile to accept ecosystem license const res = await fetch(`/api/ethos/artists`, { method: "PUT", headers: { "x-user-id": user.id, "Content-Type": "application/json", }, body: JSON.stringify({ ...profile, ecosystem_license_accepted: true, }), }); if (res.ok) { setProfile((prev) => ({ ...prev, ecosystem_license_accepted: true, })); toast.success({ title: "License accepted", description: "You can now upload tracks to the Ethos Library", }); // Continue with metadata form setShowLicenseModal(false); setShowMetadataForm(true); } else { throw new Error("Failed to accept license"); } } catch (error) { console.error("License acceptance error:", error); toast.error({ title: "Error", description: "Failed to accept ecosystem license", }); } finally { setIsAcceptingLicense(false); } }; const handleRejectLicense = () => { setShowLicenseModal(false); setCurrentFile(null); toast.info({ title: "Upload cancelled", description: "You must accept the Ecosystem License to upload tracks. You can still license commercially outside of AeThex.", }); }; const handleMetadataSubmit = async (metadata: any) => { if (!user || !currentFile) return; try { toast.loading({ title: "Uploading track...", description: "Please wait while we upload your file to secure storage", }); // Get audio duration let durationSeconds = 0; try { durationSeconds = Math.round(await getAudioDuration(currentFile)); } catch (error) { console.warn("Could not determine audio duration:", error); durationSeconds = Math.floor(currentFile.size / 16000); // Fallback estimate } // Upload file to Supabase Storage const fileUrl = await ethosStorage.uploadTrackFile(currentFile, user.id); // Create track record in database const res = await fetch(`/api/ethos/tracks`, { method: "POST", headers: { "x-user-id": user.id, "Content-Type": "application/json", }, body: JSON.stringify({ ...metadata, file_url: fileUrl, duration_seconds: durationSeconds, }), }); if (res.ok) { toast.success({ title: "Track uploaded successfully! 🎵", description: "Your track has been added to your portfolio and is ready to share", }); setShowMetadataForm(false); setCurrentFile(null); } else { throw new Error("Failed to create track record"); } } catch (error) { console.error("Upload error:", error); toast.error({ title: "Upload failed", description: String(error), }); } }; if (loading) { return (
Loading settings...
); } return ( <>
{/* Header */}

Artist Settings

Manage your Ethos Guild profile, portfolio, and services

{/* Profile Section */} Profile Information