| <!DOCTYPE html>
|
| <html lang="en">
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>Personalized Community Impact Predictor</title>
|
| <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
| <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
| <script src="https://cdn.tailwindcss.com"></script>
|
| <style>
|
| .glass {
|
| background: rgba(255, 255, 255, 0.85);
|
| backdrop-filter: blur(10px);
|
| border-radius: 20px;
|
| border: 1px solid rgba(255, 255, 255, 0.3);
|
| box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2);
|
| }
|
|
|
| .gradient-bg {
|
| background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 50%, #e8f5e8 100%);
|
| min-height: 100vh;
|
| }
|
|
|
| .action-card {
|
| transition: all 0.3s ease;
|
| cursor: pointer;
|
| background: rgba(255, 255, 255, 0.9);
|
| border: 2px solid rgba(102, 126, 234, 0.2);
|
| }
|
|
|
| .action-card:hover {
|
| transform: translateY(-5px);
|
| box-shadow: 0 20px 40px rgba(0,0,0,0.15);
|
| background: rgba(255, 255, 255, 0.95);
|
| }
|
|
|
| .action-card.selected {
|
| border: 3px solid #10b981;
|
| background: rgba(16, 185, 129, 0.1);
|
| }
|
|
|
| .custom-action-card {
|
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| color: white;
|
| border: 2px solid #667eea;
|
| }
|
|
|
| .custom-action-card:hover {
|
| background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%);
|
| }
|
|
|
| .custom-action-card.selected {
|
| border: 3px solid #10b981;
|
| background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
| }
|
|
|
| .personalization-form {
|
| background: rgba(255, 255, 255, 0.9);
|
| border-radius: 15px;
|
| padding: 1.5rem;
|
| margin: 1rem 0;
|
| border: 1px solid rgba(102, 126, 234, 0.2);
|
| }
|
|
|
| .ai-chat-bubble {
|
| background: linear-gradient(135deg, #667eea, #764ba2);
|
| color: white;
|
| padding: 1rem;
|
| border-radius: 15px;
|
| margin: 0.5rem 0;
|
| position: relative;
|
| }
|
|
|
| .ai-chat-bubble::before {
|
| content: "π€";
|
| position: absolute;
|
| left: -10px;
|
| top: 10px;
|
| background: white;
|
| border-radius: 50%;
|
| width: 30px;
|
| height: 30px;
|
| display: flex;
|
| align-items: center;
|
| justify-content: center;
|
| font-size: 16px;
|
| }
|
|
|
| .user-input-enhanced {
|
| background: rgba(255, 255, 255, 0.95);
|
| border: 2px solid rgba(102, 126, 234, 0.3);
|
| border-radius: 10px;
|
| padding: 0.75rem;
|
| transition: all 0.3s ease;
|
| color: #374151;
|
| }
|
|
|
| .user-input-enhanced:focus {
|
| border-color: #667eea;
|
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
| outline: none;
|
| }
|
|
|
| .progress-step {
|
| display: flex;
|
| align-items: center;
|
| margin: 0.5rem 0;
|
| }
|
|
|
| .progress-step.completed {
|
| color: #10b981;
|
| }
|
|
|
| .progress-step.active {
|
| color: #667eea;
|
| font-weight: bold;
|
| }
|
|
|
| .typing-indicator {
|
| display: inline-block;
|
| width: 8px;
|
| height: 8px;
|
| border-radius: 50%;
|
| background: currentColor;
|
| animation: typing 1.4s infinite ease-in-out;
|
| }
|
|
|
| .typing-indicator:nth-child(1) { animation-delay: -0.32s; }
|
| .typing-indicator:nth-child(2) { animation-delay: -0.16s; }
|
|
|
| @keyframes typing {
|
| 0%, 80%, 100% { transform: scale(0); opacity: 0.5; }
|
| 40% { transform: scale(1); opacity: 1; }
|
| }
|
|
|
| .country-dropdown {
|
| max-height: 200px;
|
| overflow-y: auto;
|
| background: white;
|
| border: 1px solid #e5e7eb;
|
| border-radius: 8px;
|
| position: absolute;
|
| top: 100%;
|
| left: 0;
|
| right: 0;
|
| z-index: 50;
|
| box-shadow: 0 10px 25px rgba(0,0,0,0.1);
|
| }
|
|
|
| .country-option {
|
| padding: 0.75rem;
|
| cursor: pointer;
|
| border-bottom: 1px solid #f3f4f6;
|
| transition: background-color 0.2s;
|
| }
|
|
|
| .country-option:hover {
|
| background-color: #f9fafb;
|
| }
|
|
|
| .country-option:last-child {
|
| border-bottom: none;
|
| }
|
|
|
| .text-dark {
|
| color: #374151 !important;
|
| }
|
|
|
| .text-darker {
|
| color: #1f2937 !important;
|
| }
|
| </style>
|
| </head>
|
| <body class="gradient-bg">
|
| <div id="root"></div>
|
|
|
| <script type="text/babel">
|
| const { useState, useEffect, useCallback, useRef } = React;
|
|
|
|
|
| const COUNTRIES = [
|
| "Afghanistan", "Albania", "Algeria", "Argentina", "Armenia", "Australia", "Austria", "Azerbaijan",
|
| "Bahrain", "Bangladesh", "Belarus", "Belgium", "Bolivia", "Bosnia and Herzegovina", "Brazil", "Bulgaria",
|
| "Cambodia", "Canada", "Chile", "China", "Colombia", "Costa Rica", "Croatia", "Czech Republic",
|
| "Denmark", "Dominican Republic", "Ecuador", "Egypt", "Estonia", "Ethiopia", "Finland", "France",
|
| "Georgia", "Germany", "Ghana", "Greece", "Guatemala", "Honduras", "Hungary", "Iceland", "India",
|
| "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Japan", "Jordan", "Kazakhstan", "Kenya",
|
| "Kuwait", "Latvia", "Lebanon", "Lithuania", "Luxembourg", "Malaysia", "Mexico", "Morocco", "Netherlands",
|
| "New Zealand", "Nigeria", "Norway", "Pakistan", "Peru", "Philippines", "Poland", "Portugal", "Qatar",
|
| "Romania", "Russia", "Saudi Arabia", "Singapore", "Slovakia", "Slovenia", "South Africa", "South Korea",
|
| "Spain", "Sri Lanka", "Sweden", "Switzerland", "Thailand", "Turkey", "Ukraine", "United Arab Emirates",
|
| "United Kingdom", "United States", "Uruguay", "Venezuela", "Vietnam"
|
| ];
|
|
|
|
|
| const COMMUNITY_ACTIONS = {
|
| park: {
|
| title: "Park Redesign",
|
| description: "Redesign local community park with fitness areas and gardens",
|
| icon: "π³",
|
| category: "Environment & Recreation",
|
| personalQuestions: [
|
| "What is the current condition of parks in your area?",
|
| "What specific improvements would benefit your community most?",
|
| "How often do you and your neighbors use local parks?",
|
| "What age groups in your community would benefit most from park improvements?",
|
| "Are there any cultural or accessibility considerations for your community?"
|
| ]
|
| },
|
| traffic: {
|
| title: "Traffic Policy",
|
| description: "Implement new traffic management and safety measures",
|
| icon: "π¦",
|
| category: "Transportation & Safety",
|
| personalQuestions: [
|
| "What are the main traffic issues in your neighborhood?",
|
| "Have you witnessed or experienced traffic-related problems?",
|
| "What times of day are traffic problems most severe in your area?",
|
| "How do current traffic conditions affect your daily life?",
|
| "What specific safety measures would you prioritize?"
|
| ]
|
| },
|
| recycling: {
|
| title: "Recycling Initiative",
|
| description: "Launch comprehensive community recycling program",
|
| icon: "β»οΈ",
|
| category: "Environment & Sustainability",
|
| personalQuestions: [
|
| "What recycling facilities currently exist in your community?",
|
| "How does your family currently handle waste and recycling?",
|
| "What are the biggest waste management challenges you observe?",
|
| "How environmentally conscious is your community?",
|
| "What would motivate more people in your area to recycle?"
|
| ]
|
| },
|
| housing: {
|
| title: "Affordable Housing",
|
| description: "Develop affordable housing solutions for community",
|
| icon: "π ",
|
| category: "Housing & Development",
|
| personalQuestions: [
|
| "What is the housing situation like in your community?",
|
| "Do you know people struggling with housing affordability?",
|
| "What types of housing are most needed in your area?",
|
| "How has housing availability changed in recent years?",
|
| "What would make housing more accessible for young people or families?"
|
| ]
|
| },
|
| education: {
|
| title: "Educational Program",
|
| description: "Create community education and skill development programs",
|
| icon: "π",
|
| category: "Education & Learning",
|
| personalQuestions: [
|
| "What educational resources are available in your community?",
|
| "What skills or knowledge gaps do you see in your area?",
|
| "How could education programs benefit people of different ages?",
|
| "What subjects or skills are you personally interested in learning?",
|
| "How do people in your community typically access learning opportunities?"
|
| ]
|
| },
|
| health: {
|
| title: "Health Initiative",
|
| description: "Implement community health and wellness programs",
|
| icon: "β€οΈ",
|
| category: "Health & Wellness",
|
| personalQuestions: [
|
| "What health challenges are common in your community?",
|
| "How accessible are healthcare services in your area?",
|
| "What wellness activities do people in your community enjoy?",
|
| "How has community health been affected by recent events?",
|
| "What would encourage more people to participate in health programs?"
|
| ]
|
| },
|
| business: {
|
| title: "Local Business Support",
|
| description: "Support and promote local business development",
|
| icon: "πͺ",
|
| category: "Economic Development",
|
| personalQuestions: [
|
| "What types of local businesses exist in your community?",
|
| "How has the local economy been affected by recent changes?",
|
| "What businesses or services are missing from your area?",
|
| "How do you and your family support local businesses?",
|
| "What would help local businesses thrive in your community?"
|
| ]
|
| },
|
| safety: {
|
| title: "Community Safety",
|
| description: "Enhance community safety and security measures",
|
| icon: "π‘οΈ",
|
| category: "Safety & Security",
|
| personalQuestions: [
|
| "How safe do you feel in your community during different times?",
|
| "What safety concerns are most important to address?",
|
| "How do community members currently look out for each other?",
|
| "What would make you feel safer in your neighborhood?",
|
| "How could technology or community programs improve safety?"
|
| ]
|
| }
|
| };
|
|
|
|
|
| const safeRender = (content) => {
|
| if (typeof content === 'string') {
|
| return content;
|
| }
|
| if (typeof content === 'object' && content !== null) {
|
| return JSON.stringify(content, null, 2);
|
| }
|
| return String(content);
|
| };
|
|
|
|
|
| const SafeAIContent = ({ content, title }) => {
|
| if (typeof content === 'string') {
|
| return React.createElement('div', { className: "text-white whitespace-pre-line" }, content);
|
| }
|
|
|
| if (Array.isArray(content)) {
|
| return React.createElement('div', { className: "space-y-2" },
|
| content.map((item, index) =>
|
| React.createElement('div', { key: index, className: "bg-white bg-opacity-20 rounded-lg p-3" },
|
| React.createElement(SafeAIContent, { content: item })
|
| )
|
| )
|
| );
|
| }
|
|
|
| if (typeof content === 'object' && content !== null) {
|
| return React.createElement('div', { className: "bg-white bg-opacity-20 rounded-lg p-3" },
|
| Object.entries(content).map(([key, value]) =>
|
| React.createElement('div', { key: key, className: "mb-2" },
|
| React.createElement('strong', { className: "text-white capitalize" }, key.replace(/_/g, ' ') + ':'),
|
| React.createElement('p', { className: "text-blue-100 mt-1" }, safeRender(value))
|
| )
|
| )
|
| );
|
| }
|
|
|
| return React.createElement('div', { className: "text-white" }, safeRender(content));
|
| };
|
|
|
|
|
| class ErrorBoundary extends React.Component {
|
| constructor(props) {
|
| super(props);
|
| this.state = { hasError: false, error: null, errorInfo: null };
|
| }
|
|
|
| static getDerivedStateFromError(error) {
|
| return { hasError: true };
|
| }
|
|
|
| componentDidCatch(error, errorInfo) {
|
| this.setState({ error, errorInfo });
|
| console.error("Uncaught error:", error, errorInfo);
|
| }
|
|
|
| render() {
|
| if (this.state.hasError) {
|
| return React.createElement('div', { className: "glass p-6 m-4 text-center" },
|
| React.createElement('h2', { className: "text-2xl font-bold text-red-600 mb-4" }, "π¨ Application Error"),
|
| React.createElement('p', { className: "text-darker mb-4" },
|
| "Something went wrong. Please try refreshing the page or starting a new prediction."
|
| ),
|
| React.createElement('button', {
|
| className: "bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600",
|
| onClick: () => window.location.reload()
|
| }, "Refresh Page")
|
| );
|
| }
|
| return this.props.children;
|
| }
|
| }
|
|
|
|
|
| const CountrySelector = ({ value, onChange, placeholder = "Select your country..." }) => {
|
| const [isOpen, setIsOpen] = useState(false);
|
| const [searchTerm, setSearchTerm] = useState('');
|
| const dropdownRef = useRef(null);
|
|
|
| const filteredCountries = COUNTRIES.filter(country =>
|
| country.toLowerCase().includes(searchTerm.toLowerCase())
|
| );
|
|
|
| useEffect(() => {
|
| const handleClickOutside = (event) => {
|
| if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
| setIsOpen(false);
|
| }
|
| };
|
|
|
| document.addEventListener('mousedown', handleClickOutside);
|
| return () => document.removeEventListener('mousedown', handleClickOutside);
|
| }, []);
|
|
|
| const handleSelect = (country) => {
|
| onChange(country);
|
| setIsOpen(false);
|
| setSearchTerm('');
|
| };
|
|
|
| return React.createElement('div', { className: "relative", ref: dropdownRef },
|
| React.createElement('input', {
|
| type: "text",
|
| className: "user-input-enhanced w-full",
|
| placeholder: value || placeholder,
|
| value: searchTerm,
|
| onChange: (e) => setSearchTerm(e.target.value),
|
| onFocus: () => setIsOpen(true),
|
| autoComplete: "off"
|
| }),
|
| isOpen && React.createElement('div', { className: "country-dropdown" },
|
| filteredCountries.length > 0 ?
|
| filteredCountries.map(country =>
|
| React.createElement('div', {
|
| key: country,
|
| className: "country-option text-darker",
|
| onClick: () => handleSelect(country)
|
| }, country)
|
| ) :
|
| React.createElement('div', { className: "country-option text-gray-500" }, "No countries found")
|
| )
|
| );
|
| };
|
|
|
|
|
| const CommunityImpactPredictor = () => {
|
| const [currentPhase, setCurrentPhase] = useState(0);
|
| const [selectedAction, setSelectedAction] = useState('');
|
| const [customAction, setCustomAction] = useState({
|
| title: '',
|
| description: '',
|
| category: ''
|
| });
|
| const [isCustomAction, setIsCustomAction] = useState(false);
|
| const [personalContext, setPersonalContext] = useState({
|
| ageRange: '',
|
| country: '',
|
| city: '',
|
| communityType: '',
|
| role: '',
|
| interests: '',
|
| experience: '',
|
| goals: ''
|
| });
|
| const [personalizationAnswers, setPersonalizationAnswers] = useState({});
|
| const [aiInsights, setAiInsights] = useState(null);
|
| const [isLoading, setIsLoading] = useState(false);
|
| const [apiKey, setApiKey] = useState('');
|
| const [error, setError] = useState('');
|
|
|
|
|
| const renderPersonalContextPhase = () => {
|
| return React.createElement('div', { className: "glass p-8 max-w-4xl mx-auto" },
|
| React.createElement('h2', { className: "text-3xl font-bold text-darker mb-6 text-center" },
|
| "π Personal Context & Background"
|
| ),
|
| React.createElement('p', { className: "text-dark mb-6 text-center" },
|
| "Help us understand your unique perspective and community context for personalized analysis."
|
| ),
|
|
|
| React.createElement('div', { className: "grid md:grid-cols-2 gap-6" },
|
|
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Age Range"),
|
| React.createElement('select', {
|
| className: "user-input-enhanced w-full",
|
| value: personalContext.ageRange,
|
| onChange: (e) => setPersonalContext({...personalContext, ageRange: e.target.value})
|
| },
|
| React.createElement('option', { value: "" }, "Select your age range..."),
|
| React.createElement('option', { value: "13-17" }, "13-17 years"),
|
| React.createElement('option', { value: "18-24" }, "18-24 years"),
|
| React.createElement('option', { value: "25-34" }, "25-34 years"),
|
| React.createElement('option', { value: "35-44" }, "35-44 years"),
|
| React.createElement('option', { value: "45-54" }, "45-54 years"),
|
| React.createElement('option', { value: "55+" }, "55+ years")
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Country"),
|
| React.createElement(CountrySelector, {
|
| value: personalContext.country,
|
| onChange: (country) => setPersonalContext({...personalContext, country})
|
| })
|
| ),
|
|
|
|
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "City/Town"),
|
| React.createElement('input', {
|
| type: "text",
|
| className: "user-input-enhanced w-full",
|
| placeholder: "Enter your city or town...",
|
| value: personalContext.city,
|
| onChange: (e) => setPersonalContext({...personalContext, city: e.target.value})
|
| })
|
| ),
|
|
|
|
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Community Type"),
|
| React.createElement('select', {
|
| className: "user-input-enhanced w-full",
|
| value: personalContext.communityType,
|
| onChange: (e) => setPersonalContext({...personalContext, communityType: e.target.value})
|
| },
|
| React.createElement('option', { value: "" }, "Select community type..."),
|
| React.createElement('option', { value: "Urban" }, "Urban (City center)"),
|
| React.createElement('option', { value: "Suburban" }, "Suburban (Residential area)"),
|
| React.createElement('option', { value: "Rural" }, "Rural (Countryside)"),
|
| React.createElement('option', { value: "Small Town" }, "Small Town"),
|
| React.createElement('option', { value: "Village" }, "Village")
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Your Role"),
|
| React.createElement('select', {
|
| className: "user-input-enhanced w-full",
|
| value: personalContext.role,
|
| onChange: (e) => setPersonalContext({...personalContext, role: e.target.value})
|
| },
|
| React.createElement('option', { value: "" }, "Select your role..."),
|
| React.createElement('option', { value: "Student" }, "Student"),
|
| React.createElement('option', { value: "Teacher/Educator" }, "Teacher/Educator"),
|
| React.createElement('option', { value: "Community Member" }, "Community Member"),
|
| React.createElement('option', { value: "Local Leader" }, "Local Leader"),
|
| React.createElement('option', { value: "Volunteer" }, "Volunteer"),
|
| React.createElement('option', { value: "Professional" }, "Professional"),
|
| React.createElement('option', { value: "Other" }, "Other")
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Interests & Passions"),
|
| React.createElement('textarea', {
|
| className: "user-input-enhanced w-full h-20",
|
| placeholder: "What causes, hobbies, or topics are you passionate about?",
|
| value: personalContext.interests,
|
| onChange: (e) => setPersonalContext({...personalContext, interests: e.target.value})
|
| })
|
| )
|
| ),
|
|
|
| React.createElement('div', { className: "mt-6" },
|
|
|
| React.createElement('div', { className: "mb-4" },
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Community Experience"),
|
| React.createElement('textarea', {
|
| className: "user-input-enhanced w-full h-20",
|
| placeholder: "Describe any previous community involvement, volunteering, or leadership experience...",
|
| value: personalContext.experience,
|
| onChange: (e) => setPersonalContext({...personalContext, experience: e.target.value})
|
| })
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "mb-6" },
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Goals & Aspirations"),
|
| React.createElement('textarea', {
|
| className: "user-input-enhanced w-full h-20",
|
| placeholder: "What do you hope to achieve through community engagement? What impact do you want to make?",
|
| value: personalContext.goals,
|
| onChange: (e) => setPersonalContext({...personalContext, goals: e.target.value})
|
| })
|
| )
|
| ),
|
|
|
| React.createElement('div', { className: "text-center" },
|
| React.createElement('button', {
|
| className: "bg-gradient-to-r from-blue-500 to-purple-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-blue-600 hover:to-purple-700 transition-all duration-300 disabled:opacity-50",
|
| onClick: () => setCurrentPhase(1),
|
| disabled: !personalContext.ageRange || !personalContext.country || !personalContext.city || !personalContext.communityType
|
| }, "Continue to Action Selection β")
|
| )
|
| );
|
| };
|
|
|
|
|
| const renderActionSelectionPhase = () => {
|
| return React.createElement('div', { className: "glass p-8 max-w-6xl mx-auto" },
|
| React.createElement('h2', { className: "text-3xl font-bold text-darker mb-6 text-center" },
|
| "π― Select Community Action"
|
| ),
|
| React.createElement('p', { className: "text-dark mb-6 text-center" },
|
| "Choose a community action to analyze, or create your own custom initiative."
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "grid md:grid-cols-3 lg:grid-cols-4 gap-4 mb-8" },
|
| Object.entries(COMMUNITY_ACTIONS).map(([key, action]) =>
|
| React.createElement('div', {
|
| key: key,
|
| className: `action-card p-4 rounded-lg text-center ${selectedAction === key && !isCustomAction ? 'selected' : ''}`,
|
| onClick: () => {
|
| setSelectedAction(key);
|
| setIsCustomAction(false);
|
| setCustomAction({ title: '', description: '', category: '' });
|
| }
|
| },
|
| React.createElement('div', { className: "text-4xl mb-2" }, action.icon),
|
| React.createElement('h3', { className: "font-bold text-darker mb-1" }, action.title),
|
| React.createElement('p', { className: "text-sm text-dark mb-2" }, action.description),
|
| React.createElement('span', { className: "text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded-full" }, action.category)
|
| )
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "mb-6" },
|
| React.createElement('div', {
|
| className: `custom-action-card action-card p-6 rounded-lg text-center cursor-pointer ${isCustomAction ? 'selected' : ''}`,
|
| onClick: () => {
|
| setIsCustomAction(true);
|
| setSelectedAction('');
|
| }
|
| },
|
| React.createElement('div', { className: "text-4xl mb-2" }, "β¨"),
|
| React.createElement('h3', { className: "font-bold mb-2" }, "Create Custom Action"),
|
| React.createElement('p', { className: "text-sm opacity-90" }, "Design your own community initiative")
|
| )
|
| ),
|
|
|
|
|
| isCustomAction && React.createElement('div', { className: "personalization-form mb-6" },
|
| React.createElement('h4', { className: "text-xl font-bold text-darker mb-4" }, "π οΈ Design Your Custom Action"),
|
| React.createElement('div', { className: "grid md:grid-cols-2 gap-4 mb-4" },
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Action Title"),
|
| React.createElement('input', {
|
| type: "text",
|
| className: "user-input-enhanced w-full",
|
| placeholder: "e.g., Community Garden Project",
|
| value: customAction.title,
|
| onChange: (e) => setCustomAction({...customAction, title: e.target.value})
|
| })
|
| ),
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Category"),
|
| React.createElement('input', {
|
| type: "text",
|
| className: "user-input-enhanced w-full",
|
| placeholder: "e.g., Environment & Sustainability",
|
| value: customAction.category,
|
| onChange: (e) => setCustomAction({...customAction, category: e.target.value})
|
| })
|
| )
|
| ),
|
| React.createElement('div', null,
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "Description"),
|
| React.createElement('textarea', {
|
| className: "user-input-enhanced w-full h-24",
|
| placeholder: "Describe your community action idea in detail...",
|
| value: customAction.description,
|
| onChange: (e) => setCustomAction({...customAction, description: e.target.value})
|
| })
|
| )
|
| ),
|
|
|
| React.createElement('div', { className: "flex justify-between" },
|
| React.createElement('button', {
|
| className: "bg-gray-500 text-white px-6 py-2 rounded-lg hover:bg-gray-600 transition-colors",
|
| onClick: () => setCurrentPhase(0)
|
| }, "β Back"),
|
| React.createElement('button', {
|
| className: "bg-gradient-to-r from-blue-500 to-purple-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-blue-600 hover:to-purple-700 transition-all duration-300 disabled:opacity-50",
|
| onClick: () => setCurrentPhase(2),
|
| disabled: !selectedAction && (!isCustomAction || !customAction.title || !customAction.description)
|
| }, "Continue to Personalization β")
|
| )
|
| );
|
| };
|
|
|
|
|
| const renderPersonalizationPhase = () => {
|
| const currentAction = isCustomAction ? customAction : COMMUNITY_ACTIONS[selectedAction];
|
| const questions = isCustomAction ? [
|
| "Why is this action important to your community?",
|
| "What specific challenges does this action address in your area?",
|
| "How would this action benefit different groups in your community?",
|
| "What resources or support would be needed to implement this?",
|
| "How does this action align with your personal interests and goals?"
|
| ] : currentAction.personalQuestions;
|
|
|
| return React.createElement('div', { className: "glass p-8 max-w-4xl mx-auto" },
|
| React.createElement('h2', { className: "text-3xl font-bold text-darker mb-6 text-center" },
|
| "π€ Personalization Questions"
|
| ),
|
| React.createElement('div', { className: "bg-blue-50 p-4 rounded-lg mb-6" },
|
| React.createElement('h3', { className: "text-xl font-bold text-darker mb-2" },
|
| `${currentAction.icon || 'β¨'} ${currentAction.title}`
|
| ),
|
| React.createElement('p', { className: "text-dark" }, currentAction.description),
|
| currentAction.category && React.createElement('span', {
|
| className: "inline-block mt-2 text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded-full"
|
| }, currentAction.category)
|
| ),
|
|
|
| React.createElement('div', { className: "space-y-6" },
|
| questions.map((question, index) =>
|
| React.createElement('div', { key: index, className: "personalization-form" },
|
| React.createElement('label', { className: "block text-darker font-semibold mb-3" },
|
| `${index + 1}. ${question}`
|
| ),
|
| React.createElement('textarea', {
|
| className: "user-input-enhanced w-full h-24",
|
| placeholder: "Share your thoughts and experiences...",
|
| value: personalizationAnswers[index] || '',
|
| onChange: (e) => setPersonalizationAnswers({
|
| ...personalizationAnswers,
|
| [index]: e.target.value
|
| })
|
| })
|
| )
|
| )
|
| ),
|
|
|
| React.createElement('div', { className: "flex justify-between mt-8" },
|
| React.createElement('button', {
|
| className: "bg-gray-500 text-white px-6 py-2 rounded-lg hover:bg-gray-600 transition-colors",
|
| onClick: () => setCurrentPhase(1)
|
| }, "β Back"),
|
| React.createElement('button', {
|
| className: "bg-gradient-to-r from-blue-500 to-purple-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-blue-600 hover:to-purple-700 transition-all duration-300 disabled:opacity-50",
|
| onClick: () => setCurrentPhase(3),
|
| disabled: Object.keys(personalizationAnswers).length < questions.length ||
|
| Object.values(personalizationAnswers).some(answer => !answer.trim())
|
| }, "Continue to AI Analysis β")
|
| )
|
| );
|
| };
|
|
|
|
|
| const renderAIAnalysisSetup = () => {
|
| return React.createElement('div', { className: "glass p-8 max-w-4xl mx-auto" },
|
| React.createElement('h2', { className: "text-3xl font-bold text-darker mb-6 text-center" },
|
| "π€ AI Impact Analysis"
|
| ),
|
| React.createElement('p', { className: "text-dark mb-6 text-center" },
|
| "Provide your OpenAI API key to generate personalized community impact predictions."
|
| ),
|
|
|
| React.createElement('div', { className: "personalization-form mb-6" },
|
| React.createElement('label', { className: "block text-darker font-semibold mb-2" }, "OpenAI API Key"),
|
| React.createElement('input', {
|
| type: "password",
|
| className: "user-input-enhanced w-full",
|
| placeholder: "Enter your OpenAI API key...",
|
| value: apiKey,
|
| onChange: (e) => setApiKey(e.target.value)
|
| }),
|
| React.createElement('p', { className: "text-sm text-dark mt-2" },
|
| "Your API key is stored locally and never transmitted to external servers."
|
| )
|
| ),
|
|
|
| error && React.createElement('div', { className: "bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4" },
|
| error
|
| ),
|
|
|
| React.createElement('div', { className: "flex justify-between" },
|
| React.createElement('button', {
|
| className: "bg-gray-500 text-white px-6 py-2 rounded-lg hover:bg-gray-600 transition-colors",
|
| onClick: () => setCurrentPhase(2)
|
| }, "β Back"),
|
| React.createElement('button', {
|
| className: "bg-gradient-to-r from-green-500 to-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-green-600 hover:to-blue-700 transition-all duration-300 disabled:opacity-50",
|
| onClick: generateAIInsights,
|
| disabled: !apiKey || isLoading
|
| }, isLoading ? "Generating Analysis..." : "Generate AI Analysis π")
|
| )
|
| );
|
| };
|
|
|
|
|
| const generateAIInsights = async () => {
|
| setIsLoading(true);
|
| setError('');
|
|
|
| try {
|
| const currentAction = isCustomAction ? customAction : COMMUNITY_ACTIONS[selectedAction];
|
|
|
| const prompt = `As an expert community development analyst, provide a comprehensive impact analysis for the following community action:
|
|
|
| Action: ${currentAction.title}
|
| Description: ${currentAction.description}
|
| Category: ${currentAction.category || 'Community Development'}
|
|
|
| Personal Context:
|
| - Age Range: ${personalContext.ageRange}
|
| - Location: ${personalContext.city}, ${personalContext.country}
|
| - Community Type: ${personalContext.communityType}
|
| - Role: ${personalContext.role}
|
| - Interests: ${personalContext.interests}
|
| - Experience: ${personalContext.experience}
|
| - Goals: ${personalContext.goals}
|
|
|
| Personalization Responses:
|
| ${Object.entries(personalizationAnswers).map(([index, answer]) => `${parseInt(index) + 1}. ${answer}`).join('\n')}
|
|
|
| Please provide a detailed analysis in the following JSON format:
|
| {
|
| "overview": "Comprehensive overview of the action's potential impact",
|
| "shortTermImpacts": ["impact1", "impact2", "impact3"],
|
| "mediumTermImpacts": ["impact1", "impact2", "impact3"],
|
| "longTermImpacts": ["impact1", "impact2", "impact3"],
|
| "challenges": ["challenge1", "challenge2", "challenge3"],
|
| "successFactors": ["factor1", "factor2", "factor3"],
|
| "stakeholders": ["stakeholder1", "stakeholder2", "stakeholder3"],
|
| "resources": ["resource1", "resource2", "resource3"],
|
| "personalizedRecommendations": "Specific recommendations based on their context and responses",
|
| "globalExamples": [
|
| {
|
| "location": "City, Country",
|
| "description": "Brief description of similar successful initiative",
|
| "outcome": "Key outcomes achieved"
|
| }
|
| ]
|
| }
|
|
|
| Ensure the analysis is specific to their location (${personalContext.city}, ${personalContext.country}) and considers their personal context and responses.`;
|
|
|
| const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
| method: 'POST',
|
| headers: {
|
| 'Content-Type': 'application/json',
|
| 'Authorization': `Bearer ${apiKey}`
|
| },
|
| body: JSON.stringify({
|
| model: 'gpt-4o-mini',
|
| messages: [
|
| {
|
| role: 'system',
|
| content: 'You are an expert community development analyst providing detailed, actionable insights for community initiatives. Always respond with valid JSON format.'
|
| },
|
| {
|
| role: 'user',
|
| content: prompt
|
| }
|
| ],
|
| max_tokens: 2000,
|
| temperature: 0.7
|
| })
|
| });
|
|
|
| if (!response.ok) {
|
| throw new Error(`API Error: ${response.status} ${response.statusText}`);
|
| }
|
|
|
| const data = await response.json();
|
| const aiResponse = data.choices[0].message.content;
|
|
|
|
|
| let insights;
|
| try {
|
| const jsonMatch = aiResponse.match(/\{[\s\S]*\}/);
|
| if (jsonMatch) {
|
| insights = JSON.parse(jsonMatch[0]);
|
| } else {
|
| throw new Error("No JSON found in response");
|
| }
|
| } catch (parseError) {
|
| console.error("JSON parsing error:", parseError);
|
|
|
| insights = {
|
| overview: aiResponse,
|
| shortTermImpacts: ["Analysis provided in overview"],
|
| mediumTermImpacts: ["Analysis provided in overview"],
|
| longTermImpacts: ["Analysis provided in overview"],
|
| challenges: ["See overview for details"],
|
| successFactors: ["See overview for details"],
|
| stakeholders: ["Community members", "Local government", "Organizations"],
|
| resources: ["Community support", "Funding", "Volunteers"],
|
| personalizedRecommendations: "See overview for personalized insights",
|
| globalExamples: []
|
| };
|
| }
|
|
|
| setAiInsights(insights);
|
| setCurrentPhase(4);
|
|
|
| } catch (error) {
|
| console.error('Error generating AI insights:', error);
|
| setError(`Failed to generate analysis: ${error.message}`);
|
| } finally {
|
| setIsLoading(false);
|
| }
|
| };
|
|
|
|
|
| const renderAIResults = () => {
|
| if (!aiInsights) return null;
|
|
|
| const currentAction = isCustomAction ? customAction : COMMUNITY_ACTIONS[selectedAction];
|
|
|
| return React.createElement('div', { className: "glass p-8 max-w-6xl mx-auto" },
|
| React.createElement('h2', { className: "text-3xl font-bold text-darker mb-6 text-center" },
|
| "π AI Impact Analysis Results"
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "bg-blue-50 p-4 rounded-lg mb-6" },
|
| React.createElement('h3', { className: "text-xl font-bold text-darker mb-2" },
|
| `${currentAction.icon || 'β¨'} ${currentAction.title}`
|
| ),
|
| React.createElement('p', { className: "text-dark mb-2" }, currentAction.description),
|
| React.createElement('p', { className: "text-sm text-dark" },
|
| `Analysis for: ${personalContext.city}, ${personalContext.country} (${personalContext.communityType})`
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "ai-chat-bubble mb-6" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "π― Overview"),
|
| React.createElement(SafeAIContent, { content: aiInsights.overview })
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "grid md:grid-cols-3 gap-4 mb-6" },
|
| React.createElement('div', { className: "ai-chat-bubble" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "π
Short-term (1-6 months)"),
|
| React.createElement(SafeAIContent, { content: aiInsights.shortTermImpacts })
|
| ),
|
| React.createElement('div', { className: "ai-chat-bubble" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "π Medium-term (6-18 months)"),
|
| React.createElement(SafeAIContent, { content: aiInsights.mediumTermImpacts })
|
| ),
|
| React.createElement('div', { className: "ai-chat-bubble" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "π Long-term (18+ months)"),
|
| React.createElement(SafeAIContent, { content: aiInsights.longTermImpacts })
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "grid md:grid-cols-2 gap-4 mb-6" },
|
| React.createElement('div', { className: "ai-chat-bubble" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "β οΈ Challenges"),
|
| React.createElement(SafeAIContent, { content: aiInsights.challenges })
|
| ),
|
| React.createElement('div', { className: "ai-chat-bubble" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "β
Success Factors"),
|
| React.createElement(SafeAIContent, { content: aiInsights.successFactors })
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "grid md:grid-cols-2 gap-4 mb-6" },
|
| React.createElement('div', { className: "ai-chat-bubble" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "π₯ Key Stakeholders"),
|
| React.createElement(SafeAIContent, { content: aiInsights.stakeholders })
|
| ),
|
| React.createElement('div', { className: "ai-chat-bubble" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "π οΈ Required Resources"),
|
| React.createElement(SafeAIContent, { content: aiInsights.resources })
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "ai-chat-bubble mb-6" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "π‘ Personalized Recommendations"),
|
| React.createElement(SafeAIContent, { content: aiInsights.personalizedRecommendations })
|
| ),
|
|
|
|
|
| aiInsights.globalExamples && aiInsights.globalExamples.length > 0 &&
|
| React.createElement('div', { className: "ai-chat-bubble mb-6" },
|
| React.createElement('h4', { className: "text-lg font-bold mb-3" }, "π Global Examples"),
|
| React.createElement(SafeAIContent, { content: aiInsights.globalExamples })
|
| ),
|
|
|
| React.createElement('div', { className: "text-center" },
|
| React.createElement('button', {
|
| className: "bg-gradient-to-r from-green-500 to-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-green-600 hover:to-blue-700 transition-all duration-300 mr-4",
|
| onClick: () => {
|
| setCurrentPhase(0);
|
| setSelectedAction('');
|
| setIsCustomAction(false);
|
| setCustomAction({ title: '', description: '', category: '' });
|
| setPersonalizationAnswers({});
|
| setAiInsights(null);
|
| setPersonalContext({
|
| ageRange: '',
|
| country: '',
|
| city: '',
|
| communityType: '',
|
| role: '',
|
| interests: '',
|
| experience: '',
|
| goals: ''
|
| });
|
| }
|
| }, "π Start New Analysis"),
|
| React.createElement('button', {
|
| className: "bg-gray-500 text-white px-6 py-2 rounded-lg hover:bg-gray-600 transition-colors",
|
| onClick: () => setCurrentPhase(3)
|
| }, "β Back to Setup")
|
| )
|
| );
|
| };
|
|
|
|
|
| return React.createElement('div', { className: "min-h-screen p-4" },
|
| React.createElement('div', { className: "max-w-7xl mx-auto" },
|
| React.createElement('header', { className: "text-center mb-8" },
|
| React.createElement('h1', { className: "text-4xl font-bold text-darker mb-2" },
|
| "ποΈ Community Impact Predictor"
|
| ),
|
| React.createElement('p', { className: "text-dark text-lg" },
|
| "Analyze and predict the impact of community actions with AI-powered insights"
|
| )
|
| ),
|
|
|
|
|
| React.createElement('div', { className: "glass p-4 mb-6 max-w-4xl mx-auto" },
|
| React.createElement('div', { className: "flex justify-between items-center" },
|
| ['Personal Context', 'Action Selection', 'Personalization', 'AI Analysis', 'Results'].map((step, index) =>
|
| React.createElement('div', {
|
| key: index,
|
| className: `progress-step ${index < currentPhase ? 'completed' : index === currentPhase ? 'active' : ''}`
|
| },
|
| React.createElement('span', { className: "mr-2" },
|
| index < currentPhase ? 'β
' : index === currentPhase ? 'π' : 'β'
|
| ),
|
| React.createElement('span', { className: "text-sm font-medium text-darker" }, step)
|
| )
|
| )
|
| )
|
| ),
|
|
|
|
|
| currentPhase === 0 && renderPersonalContextPhase(),
|
| currentPhase === 1 && renderActionSelectionPhase(),
|
| currentPhase === 2 && renderPersonalizationPhase(),
|
| currentPhase === 3 && renderAIAnalysisSetup(),
|
| currentPhase === 4 && renderAIResults()
|
| )
|
| );
|
| };
|
|
|
|
|
| ReactDOM.render(
|
| React.createElement(ErrorBoundary, null,
|
| React.createElement(CommunityImpactPredictor)
|
| ),
|
| document.getElementById('root')
|
| );
|
| </script>
|
| </body>
|
| </html>
|
|
|
|
|