|
|
<!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>
|
|
|
|
|
|
|