Rebuild frontend with Tailwind CSS + fix Python 3.14 compatibility

- Upgrade SQLAlchemy 2.0.27→2.0.45, google-genai SDK for Python 3.14
- Replace google-generativeai with google-genai in narrative_service.py
- Fix HTTPException handling in main.py (was wrapping as 500)
- Rebuild all frontend components with Tailwind CSS v3:
  - Dashboard, NarrativeSection, StatsGrid, VibeRadar, HeatMap, TopRotation
  - Custom color palette (background-dark, card-dark, accent-neon, etc.)
  - Add glass-panel, holographic-badge CSS effects
- Docker improvements:
  - Combined backend container (API + worker in entrypoint.sh)
  - DATABASE_URL configurable via env var
  - CI workflow builds both backend and frontend images
- Update README with clearer docker-compose instructions
This commit is contained in:
bnair123
2025-12-26 20:25:44 +04:00
parent 9b8f7355fb
commit 56b7e2a5ba
25 changed files with 2255 additions and 319 deletions

View File

@@ -0,0 +1,66 @@
import React from 'react';
import { motion } from 'framer-motion';
const NarrativeSection = ({ narrative, vibe }) => {
if (!narrative) return null;
const persona = narrative.persona || "THE UNKNOWN LISTENER";
const vibeCheck = narrative.vibe_check || "Analyzing auditory aura...";
// Generate tags based on vibe metrics if available
const getTags = () => {
if (!vibe) return [];
const tags = [];
if (vibe.valence > 0.6) tags.push({ text: "HIGH VALENCE", color: "primary" });
else if (vibe.valence < 0.4) tags.push({ text: "MELANCHOLIC", color: "accent-purple" });
if (vibe.energy > 0.6) tags.push({ text: "HIGH ENERGY", color: "accent-neon" });
else if (vibe.energy < 0.4) tags.push({ text: "CHILL VIBES", color: "accent-purple" });
if (vibe.danceability > 0.7) tags.push({ text: "DANCEABLE", color: "primary" });
return tags.slice(0, 3); // Max 3 tags
};
const tags = getTags();
// Default tags if none generated
if (tags.length === 0) {
tags.push({ text: "ECLECTIC", color: "primary" });
tags.push({ text: "MYSTERIOUS", color: "accent-purple" });
}
return (
<section className="relative rounded-2xl overflow-hidden min-h-[400px] flex items-center justify-center p-8 bg-card-dark border border-[#222f49]">
{/* Dynamic Background */}
<div className="absolute inset-0 mood-gradient"></div>
<div className="relative z-10 flex flex-col items-center text-center max-w-2xl gap-6">
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.5 }}
className="holographic-badge px-8 py-4 rounded-full border border-primary/30"
>
<h1 className="text-3xl md:text-5xl font-black tracking-tight text-white drop-shadow-[0_0_15px_rgba(37,106,244,0.5)] uppercase">
{persona}
</h1>
</motion.div>
<div className="font-mono text-primary/80 text-lg md:text-xl font-medium tracking-wide">
<span className="typing-cursor">{vibeCheck}</span>
</div>
<div className="mt-4 flex gap-3 flex-wrap justify-center">
{tags.map((tag, i) => (
<span key={i} className={`px-3 py-1 rounded-full text-xs font-bold bg-${tag.color}/20 text-${tag.color} border border-${tag.color}/20`}>
{tag.text}
</span>
))}
</div>
</div>
</section>
);
};
export default NarrativeSection;