| <!DOCTYPE html>
|
| <html lang="en">
|
| <head>
|
| <meta charset="UTF-8" />
|
| <title>Cultural Intelligence Explorer - Global Edition with AI Analysis</title>
|
| <meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
|
| <script src="https://cdn.tailwindcss.com"></script>
|
|
|
| <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
| <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
|
|
| <script crossorigin src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
|
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| <style>
|
| html, body {
|
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| min-height: 100%;
|
| margin: 0;
|
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
| }
|
|
|
| .glass {
|
| background: rgba(255, 255, 255, 0.9);
|
| backdrop-filter: blur(15px);
|
| border-radius: 1.5rem;
|
| box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
|
| border: 1px solid rgba(255, 255, 255, 0.18);
|
| }
|
|
|
| .country-dropdown {
|
| background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
| border-radius: 1rem;
|
| padding: 1.5rem;
|
| color: white;
|
| margin-bottom: 2rem;
|
| }
|
|
|
| .dropdown-container {
|
| position: relative;
|
| width: 100%;
|
| }
|
|
|
| .dropdown-input {
|
| width: 100%;
|
| padding: 1rem;
|
| border: 2px solid rgba(255,255,255,0.3);
|
| border-radius: 0.75rem;
|
| background: rgba(255,255,255,0.9);
|
| color: #333;
|
| font-size: 1rem;
|
| cursor: pointer;
|
| }
|
|
|
| .dropdown-input:focus {
|
| outline: none;
|
| border-color: #fbbf24;
|
| box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.3);
|
| }
|
|
|
| .dropdown-list {
|
| position: absolute;
|
| top: 100%;
|
| left: 0;
|
| right: 0;
|
| background: white;
|
| border: 1px solid #e5e7eb;
|
| border-radius: 0.75rem;
|
| max-height: 300px;
|
| overflow-y: auto;
|
| z-index: 1000;
|
| box-shadow: 0 10px 25px rgba(0,0,0,0.15);
|
| }
|
|
|
| .dropdown-item {
|
| padding: 0.75rem 1rem;
|
| cursor: pointer;
|
| border-bottom: 1px solid #f3f4f6;
|
| display: flex;
|
| align-items: center;
|
| gap: 0.75rem;
|
| transition: background-color 0.2s;
|
| }
|
|
|
| .dropdown-item:hover {
|
| background-color: #f8fafc;
|
| }
|
|
|
| .dropdown-item:last-child {
|
| border-bottom: none;
|
| }
|
|
|
| .dropdown-item.highlighted {
|
| background-color: #eff6ff;
|
| }
|
|
|
| .theme-card {
|
| background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
| border-radius: 0.75rem;
|
| padding: 1rem;
|
| color: white;
|
| cursor: pointer;
|
| transition: all 0.3s ease;
|
| border: 2px solid transparent;
|
| text-align: center;
|
| }
|
|
|
| .theme-card:hover {
|
| transform: scale(1.05);
|
| border-color: rgba(255,255,255,0.5);
|
| }
|
|
|
| .theme-card.selected {
|
| border-color: #fbbf24;
|
| box-shadow: 0 0 15px rgba(251, 191, 36, 0.4);
|
| }
|
|
|
| .insight-section {
|
| background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
|
| border-radius: 1rem;
|
| padding: 1.5rem;
|
| margin: 1rem 0;
|
| border-left: 4px solid #667eea;
|
| }
|
|
|
| .reflection-card {
|
| background: rgba(255, 255, 255, 0.95);
|
| border-radius: 1rem;
|
| padding: 1.5rem;
|
| margin: 1rem 0;
|
| box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
| border: 1px solid rgba(102, 126, 234, 0.2);
|
| }
|
|
|
| .analysis-table {
|
| background: rgba(255, 255, 255, 0.98);
|
| border-radius: 1rem;
|
| overflow: hidden;
|
| box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
| border: 1px solid rgba(102, 126, 234, 0.2);
|
| }
|
|
|
| .analysis-table table {
|
| width: 100%;
|
| border-collapse: collapse;
|
| }
|
|
|
| .analysis-table th {
|
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| color: white;
|
| padding: 1rem;
|
| text-align: left;
|
| font-weight: 600;
|
| border-bottom: 2px solid rgba(255,255,255,0.2);
|
| }
|
|
|
| .analysis-table td {
|
| padding: 1rem;
|
| border-bottom: 1px solid #e5e7eb;
|
| vertical-align: top;
|
| }
|
|
|
| .analysis-table tr:last-child td {
|
| border-bottom: none;
|
| }
|
|
|
| .analysis-table tr:nth-child(even) {
|
| background-color: #f8fafc;
|
| }
|
|
|
| .analysis-table tr:hover {
|
| background-color: #eff6ff;
|
| }
|
|
|
| .insight-badge {
|
| display: inline-block;
|
| background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
| color: white;
|
| padding: 0.25rem 0.75rem;
|
| border-radius: 1rem;
|
| font-size: 0.75rem;
|
| font-weight: 600;
|
| margin: 0.25rem 0.25rem 0.25rem 0;
|
| }
|
|
|
| .comparison-badge {
|
| display: inline-block;
|
| background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
| color: white;
|
| padding: 0.25rem 0.75rem;
|
| border-radius: 1rem;
|
| font-size: 0.75rem;
|
| font-weight: 600;
|
| margin: 0.25rem 0.25rem 0.25rem 0;
|
| }
|
|
|
| .progress-bar {
|
| background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
| height: 6px;
|
| border-radius: 3px;
|
| transition: width 0.5s ease;
|
| }
|
|
|
| .typing-indicator {
|
| display: inline-flex;
|
| align-items: center;
|
| gap: 4px;
|
| }
|
|
|
| .typing-dot {
|
| width: 8px;
|
| height: 8px;
|
| border-radius: 50%;
|
| background: #667eea;
|
| animation: typing 1.4s infinite ease-in-out;
|
| }
|
|
|
| .typing-dot:nth-child(1) { animation-delay: -0.32s; }
|
| .typing-dot:nth-child(2) { animation-delay: -0.16s; }
|
|
|
| @keyframes typing {
|
| 0%, 80%, 100% { transform: scale(0); opacity: 0.5; }
|
| 40% { transform: scale(1); opacity: 1; }
|
| }
|
|
|
| .fade-in {
|
| animation: fadeIn 0.6s ease-in;
|
| }
|
|
|
| @keyframes fadeIn {
|
| from { opacity: 0; transform: translateY(20px); }
|
| to { opacity: 1; transform: translateY(0); }
|
| }
|
|
|
| .slide-in {
|
| animation: slideIn 0.5s ease-out;
|
| }
|
|
|
| @keyframes slideIn {
|
| from { opacity: 0; transform: translateX(-30px); }
|
| to { opacity: 1; transform: translateX(0); }
|
| }
|
|
|
|
|
| .sr-only {
|
| position: absolute;
|
| width: 1px;
|
| height: 1px;
|
| padding: 0;
|
| margin: -1px;
|
| overflow: hidden;
|
| clip: rect(0, 0, 0, 0);
|
| white-space: nowrap;
|
| border: 0;
|
| }
|
|
|
| button:focus, input:focus, textarea:focus, select:focus {
|
| outline: 3px solid #fbbf24;
|
| outline-offset: 2px;
|
| }
|
|
|
| .theme-card:focus {
|
| outline: 3px solid #fbbf24;
|
| outline-offset: 2px;
|
| }
|
|
|
|
|
| @media (max-width: 768px) {
|
| .theme-grid {
|
| grid-template-columns: repeat(2, 1fr);
|
| }
|
|
|
| .analysis-table {
|
| overflow-x: auto;
|
| }
|
|
|
| .analysis-table table {
|
| min-width: 600px;
|
| }
|
| }
|
|
|
| @media (max-width: 640px) {
|
| .theme-grid {
|
| grid-template-columns: 1fr;
|
| }
|
| }
|
| </style>
|
| </head>
|
| <body>
|
| <div id="root"></div>
|
| <script type="text/babel">
|
| const { useState, useEffect, useCallback, useRef } = React;
|
|
|
|
|
| const WORLD_COUNTRIES = [
|
|
|
| { id: 'algeria', name: 'Algeria', flag: '🇩🇿', region: 'North Africa' },
|
| { id: 'angola', name: 'Angola', flag: '🇦🇴', region: 'Central Africa' },
|
| { id: 'benin', name: 'Benin', flag: '🇧🇯', region: 'West Africa' },
|
| { id: 'botswana', name: 'Botswana', flag: '🇧🇼', region: 'Southern Africa' },
|
| { id: 'burkina-faso', name: 'Burkina Faso', flag: '🇧🇫', region: 'West Africa' },
|
| { id: 'burundi', name: 'Burundi', flag: '🇧🇮', region: 'East Africa' },
|
| { id: 'cameroon', name: 'Cameroon', flag: '🇨🇲', region: 'Central Africa' },
|
| { id: 'cape-verde', name: 'Cape Verde', flag: '🇨🇻', region: 'West Africa' },
|
| { id: 'chad', name: 'Chad', flag: '🇹🇩', region: 'Central Africa' },
|
| { id: 'comoros', name: 'Comoros', flag: '🇰🇲', region: 'East Africa' },
|
| { id: 'congo', name: 'Congo', flag: '🇨🇬', region: 'Central Africa' },
|
| { id: 'drc', name: 'Democratic Republic of Congo', flag: '🇨🇩', region: 'Central Africa' },
|
| { id: 'djibouti', name: 'Djibouti', flag: '🇩🇯', region: 'East Africa' },
|
| { id: 'egypt', name: 'Egypt', flag: '🇪🇬', region: 'North Africa' },
|
| { id: 'equatorial-guinea', name: 'Equatorial Guinea', flag: '🇬🇶', region: 'Central Africa' },
|
| { id: 'eritrea', name: 'Eritrea', flag: '🇪🇷', region: 'East Africa' },
|
| { id: 'eswatini', name: 'Eswatini', flag: '🇸🇿', region: 'Southern Africa' },
|
| { id: 'ethiopia', name: 'Ethiopia', flag: '🇪🇹', region: 'East Africa' },
|
| { id: 'gabon', name: 'Gabon', flag: '🇬🇦', region: 'Central Africa' },
|
| { id: 'gambia', name: 'Gambia', flag: '🇬🇲', region: 'West Africa' },
|
| { id: 'ghana', name: 'Ghana', flag: '🇬🇭', region: 'West Africa' },
|
| { id: 'guinea', name: 'Guinea', flag: '🇬🇳', region: 'West Africa' },
|
| { id: 'guinea-bissau', name: 'Guinea-Bissau', flag: '🇬🇼', region: 'West Africa' },
|
| { id: 'ivory-coast', name: 'Ivory Coast', flag: '🇨🇮', region: 'West Africa' },
|
| { id: 'kenya', name: 'Kenya', flag: '🇰🇪', region: 'East Africa' },
|
| { id: 'lesotho', name: 'Lesotho', flag: '🇱🇸', region: 'Southern Africa' },
|
| { id: 'liberia', name: 'Liberia', flag: '🇱🇷', region: 'West Africa' },
|
| { id: 'libya', name: 'Libya', flag: '🇱🇾', region: 'North Africa' },
|
| { id: 'madagascar', name: 'Madagascar', flag: '🇲🇬', region: 'East Africa' },
|
| { id: 'malawi', name: 'Malawi', flag: '🇲🇼', region: 'Southern Africa' },
|
| { id: 'mali', name: 'Mali', flag: '🇲🇱', region: 'West Africa' },
|
| { id: 'mauritania', name: 'Mauritania', flag: '🇲🇷', region: 'West Africa' },
|
| { id: 'mauritius', name: 'Mauritius', flag: '🇲🇺', region: 'East Africa' },
|
| { id: 'morocco', name: 'Morocco', flag: '🇲🇦', region: 'North Africa' },
|
| { id: 'mozambique', name: 'Mozambique', flag: '🇲🇿', region: 'Southern Africa' },
|
| { id: 'namibia', name: 'Namibia', flag: '🇳🇦', region: 'Southern Africa' },
|
| { id: 'niger', name: 'Niger', flag: '🇳🇪', region: 'West Africa' },
|
| { id: 'nigeria', name: 'Nigeria', flag: '🇳🇬', region: 'West Africa' },
|
| { id: 'rwanda', name: 'Rwanda', flag: '🇷🇼', region: 'East Africa' },
|
| { id: 'sao-tome', name: 'São Tomé and Príncipe', flag: '🇸🇹', region: 'Central Africa' },
|
| { id: 'senegal', name: 'Senegal', flag: '🇸🇳', region: 'West Africa' },
|
| { id: 'seychelles', name: 'Seychelles', flag: '🇸🇨', region: 'East Africa' },
|
| { id: 'sierra-leone', name: 'Sierra Leone', flag: '🇸🇱', region: 'West Africa' },
|
| { id: 'somalia', name: 'Somalia', flag: '🇸🇴', region: 'East Africa' },
|
| { id: 'south-africa', name: 'South Africa', flag: '🇿🇦', region: 'Southern Africa' },
|
| { id: 'south-sudan', name: 'South Sudan', flag: '🇸🇸', region: 'East Africa' },
|
| { id: 'sudan', name: 'Sudan', flag: '🇸🇩', region: 'North Africa' },
|
| { id: 'tanzania', name: 'Tanzania', flag: '🇹🇿', region: 'East Africa' },
|
| { id: 'togo', name: 'Togo', flag: '🇹🇬', region: 'West Africa' },
|
| { id: 'tunisia', name: 'Tunisia', flag: '🇹🇳', region: 'North Africa' },
|
| { id: 'uganda', name: 'Uganda', flag: '🇺🇬', region: 'East Africa' },
|
| { id: 'zambia', name: 'Zambia', flag: '🇿🇲', region: 'Southern Africa' },
|
| { id: 'zimbabwe', name: 'Zimbabwe', flag: '🇿🇼', region: 'Southern Africa' },
|
|
|
|
|
| { id: 'afghanistan', name: 'Afghanistan', flag: '🇦🇫', region: 'Central Asia' },
|
| { id: 'armenia', name: 'Armenia', flag: '🇦🇲', region: 'Western Asia' },
|
| { id: 'azerbaijan', name: 'Azerbaijan', flag: '🇦🇿', region: 'Western Asia' },
|
| { id: 'bahrain', name: 'Bahrain', flag: '🇧🇭', region: 'Western Asia' },
|
| { id: 'bangladesh', name: 'Bangladesh', flag: '🇧🇩', region: 'South Asia' },
|
| { id: 'bhutan', name: 'Bhutan', flag: '🇧🇹', region: 'South Asia' },
|
| { id: 'brunei', name: 'Brunei', flag: '🇧🇳', region: 'Southeast Asia' },
|
| { id: 'cambodia', name: 'Cambodia', flag: '🇰🇭', region: 'Southeast Asia' },
|
| { id: 'china', name: 'China', flag: '🇨🇳', region: 'East Asia' },
|
| { id: 'cyprus', name: 'Cyprus', flag: '🇨🇾', region: 'Western Asia' },
|
| { id: 'georgia', name: 'Georgia', flag: '🇬🇪', region: 'Western Asia' },
|
| { id: 'india', name: 'India', flag: '🇮🇳', region: 'South Asia' },
|
| { id: 'indonesia', name: 'Indonesia', flag: '🇮🇩', region: 'Southeast Asia' },
|
| { id: 'iran', name: 'Iran', flag: '🇮🇷', region: 'Western Asia' },
|
| { id: 'iraq', name: 'Iraq', flag: '🇮🇶', region: 'Western Asia' },
|
| { id: 'israel', name: 'Israel', flag: '🇮🇱', region: 'Western Asia' },
|
| { id: 'japan', name: 'Japan', flag: '🇯🇵', region: 'East Asia' },
|
| { id: 'jordan', name: 'Jordan', flag: '🇯🇴', region: 'Western Asia' },
|
| { id: 'kazakhstan', name: 'Kazakhstan', flag: '🇰🇿', region: 'Central Asia' },
|
| { id: 'kuwait', name: 'Kuwait', flag: '🇰🇼', region: 'Western Asia' },
|
| { id: 'kyrgyzstan', name: 'Kyrgyzstan', flag: '🇰🇬', region: 'Central Asia' },
|
| { id: 'laos', name: 'Laos', flag: '🇱🇦', region: 'Southeast Asia' },
|
| { id: 'lebanon', name: 'Lebanon', flag: '🇱🇧', region: 'Western Asia' },
|
| { id: 'malaysia', name: 'Malaysia', flag: '🇲🇾', region: 'Southeast Asia' },
|
| { id: 'maldives', name: 'Maldives', flag: '🇲🇻', region: 'South Asia' },
|
| { id: 'mongolia', name: 'Mongolia', flag: '🇲🇳', region: 'East Asia' },
|
| { id: 'myanmar', name: 'Myanmar', flag: '🇲🇲', region: 'Southeast Asia' },
|
| { id: 'nepal', name: 'Nepal', flag: '🇳🇵', region: 'South Asia' },
|
| { id: 'north-korea', name: 'North Korea', flag: '🇰🇵', region: 'East Asia' },
|
| { id: 'oman', name: 'Oman', flag: '🇴🇲', region: 'Western Asia' },
|
| { id: 'pakistan', name: 'Pakistan', flag: '🇵🇰', region: 'South Asia' },
|
| { id: 'palestine', name: 'Palestine', flag: '🇵🇸', region: 'Western Asia' },
|
| { id: 'philippines', name: 'Philippines', flag: '🇵🇭', region: 'Southeast Asia' },
|
| { id: 'qatar', name: 'Qatar', flag: '🇶🇦', region: 'Western Asia' },
|
| { id: 'saudi-arabia', name: 'Saudi Arabia', flag: '🇸🇦', region: 'Western Asia' },
|
| { id: 'singapore', name: 'Singapore', flag: '🇸🇬', region: 'Southeast Asia' },
|
| { id: 'south-korea', name: 'South Korea', flag: '🇰🇷', region: 'East Asia' },
|
| { id: 'sri-lanka', name: 'Sri Lanka', flag: '🇱🇰', region: 'South Asia' },
|
| { id: 'syria', name: 'Syria', flag: '🇸🇾', region: 'Western Asia' },
|
| { id: 'taiwan', name: 'Taiwan', flag: '🇹🇼', region: 'East Asia' },
|
| { id: 'tajikistan', name: 'Tajikistan', flag: '🇹🇯', region: 'Central Asia' },
|
| { id: 'thailand', name: 'Thailand', flag: '🇹🇭', region: 'Southeast Asia' },
|
| { id: 'timor-leste', name: 'Timor-Leste', flag: '🇹🇱', region: 'Southeast Asia' },
|
| { id: 'turkey', name: 'Turkey', flag: '🇹🇷', region: 'Western Asia' },
|
| { id: 'turkmenistan', name: 'Turkmenistan', flag: '🇹🇲', region: 'Central Asia' },
|
| { id: 'uae', name: 'United Arab Emirates', flag: '🇦🇪', region: 'Western Asia' },
|
| { id: 'uzbekistan', name: 'Uzbekistan', flag: '🇺🇿', region: 'Central Asia' },
|
| { id: 'vietnam', name: 'Vietnam', flag: '🇻🇳', region: 'Southeast Asia' },
|
| { id: 'yemen', name: 'Yemen', flag: '🇾🇪', region: 'Western Asia' },
|
|
|
|
|
| { id: 'albania', name: 'Albania', flag: '🇦🇱', region: 'Southern Europe' },
|
| { id: 'andorra', name: 'Andorra', flag: '🇦🇩', region: 'Southern Europe' },
|
| { id: 'austria', name: 'Austria', flag: '🇦🇹', region: 'Central Europe' },
|
| { id: 'belarus', name: 'Belarus', flag: '🇧🇾', region: 'Eastern Europe' },
|
| { id: 'belgium', name: 'Belgium', flag: '🇧🇪', region: 'Western Europe' },
|
| { id: 'bosnia', name: 'Bosnia and Herzegovina', flag: '🇧🇦', region: 'Southern Europe' },
|
| { id: 'bulgaria', name: 'Bulgaria', flag: '🇧🇬', region: 'Eastern Europe' },
|
| { id: 'croatia', name: 'Croatia', flag: '🇭🇷', region: 'Southern Europe' },
|
| { id: 'czech-republic', name: 'Czech Republic', flag: '🇨🇿', region: 'Central Europe' },
|
| { id: 'denmark', name: 'Denmark', flag: '🇩🇰', region: 'Northern Europe' },
|
| { id: 'estonia', name: 'Estonia', flag: '🇪🇪', region: 'Northern Europe' },
|
| { id: 'finland', name: 'Finland', flag: '🇫🇮', region: 'Northern Europe' },
|
| { id: 'france', name: 'France', flag: '🇫🇷', region: 'Western Europe' },
|
| { id: 'germany', name: 'Germany', flag: '🇩🇪', region: 'Central Europe' },
|
| { id: 'greece', name: 'Greece', flag: '🇬🇷', region: 'Southern Europe' },
|
| { id: 'hungary', name: 'Hungary', flag: '🇭🇺', region: 'Central Europe' },
|
| { id: 'iceland', name: 'Iceland', flag: '🇮🇸', region: 'Northern Europe' },
|
| { id: 'ireland', name: 'Ireland', flag: '🇮🇪', region: 'Northern Europe' },
|
| { id: 'italy', name: 'Italy', flag: '🇮🇹', region: 'Southern Europe' },
|
| { id: 'kosovo', name: 'Kosovo', flag: '🇽🇰', region: 'Southern Europe' },
|
| { id: 'latvia', name: 'Latvia', flag: '🇱🇻', region: 'Northern Europe' },
|
| { id: 'liechtenstein', name: 'Liechtenstein', flag: '🇱🇮', region: 'Central Europe' },
|
| { id: 'lithuania', name: 'Lithuania', flag: '🇱🇹', region: 'Northern Europe' },
|
| { id: 'luxembourg', name: 'Luxembourg', flag: '🇱🇺', region: 'Western Europe' },
|
| { id: 'malta', name: 'Malta', flag: '🇲🇹', region: 'Southern Europe' },
|
| { id: 'moldova', name: 'Moldova', flag: '🇲🇩', region: 'Eastern Europe' },
|
| { id: 'monaco', name: 'Monaco', flag: '🇲🇨', region: 'Western Europe' },
|
| { id: 'montenegro', name: 'Montenegro', flag: '🇲🇪', region: 'Southern Europe' },
|
| { id: 'netherlands', name: 'Netherlands', flag: '🇳🇱', region: 'Western Europe' },
|
| { id: 'north-macedonia', name: 'North Macedonia', flag: '🇲🇰', region: 'Southern Europe' },
|
| { id: 'norway', name: 'Norway', flag: '🇳🇴', region: 'Northern Europe' },
|
| { id: 'poland', name: 'Poland', flag: '🇵🇱', region: 'Central Europe' },
|
| { id: 'portugal', name: 'Portugal', flag: '🇵🇹', region: 'Southern Europe' },
|
| { id: 'romania', name: 'Romania', flag: '🇷🇴', region: 'Eastern Europe' },
|
| { id: 'russia', name: 'Russia', flag: '🇷🇺', region: 'Eastern Europe' },
|
| { id: 'san-marino', name: 'San Marino', flag: '🇸🇲', region: 'Southern Europe' },
|
| { id: 'serbia', name: 'Serbia', flag: '🇷🇸', region: 'Southern Europe' },
|
| { id: 'slovakia', name: 'Slovakia', flag: '🇸🇰', region: 'Central Europe' },
|
| { id: 'slovenia', name: 'Slovenia', flag: '🇸🇮', region: 'Central Europe' },
|
| { id: 'spain', name: 'Spain', flag: '🇪🇸', region: 'Southern Europe' },
|
| { id: 'sweden', name: 'Sweden', flag: '🇸🇪', region: 'Northern Europe' },
|
| { id: 'switzerland', name: 'Switzerland', flag: '🇨🇭', region: 'Central Europe' },
|
| { id: 'ukraine', name: 'Ukraine', flag: '🇺🇦', region: 'Eastern Europe' },
|
| { id: 'united-kingdom', name: 'United Kingdom', flag: '🇬🇧', region: 'Northern Europe' },
|
| { id: 'vatican', name: 'Vatican City', flag: '🇻🇦', region: 'Southern Europe' },
|
|
|
|
|
| { id: 'antigua', name: 'Antigua and Barbuda', flag: '🇦🇬', region: 'Caribbean' },
|
| { id: 'bahamas', name: 'Bahamas', flag: '🇧🇸', region: 'Caribbean' },
|
| { id: 'barbados', name: 'Barbados', flag: '🇧🇧', region: 'Caribbean' },
|
| { id: 'belize', name: 'Belize', flag: '🇧🇿', region: 'Central America' },
|
| { id: 'canada', name: 'Canada', flag: '🇨🇦', region: 'North America' },
|
| { id: 'costa-rica', name: 'Costa Rica', flag: '🇨🇷', region: 'Central America' },
|
| { id: 'cuba', name: 'Cuba', flag: '🇨🇺', region: 'Caribbean' },
|
| { id: 'dominica', name: 'Dominica', flag: '🇩🇲', region: 'Caribbean' },
|
| { id: 'dominican-republic', name: 'Dominican Republic', flag: '🇩🇴', region: 'Caribbean' },
|
| { id: 'el-salvador', name: 'El Salvador', flag: '🇸🇻', region: 'Central America' },
|
| { id: 'grenada', name: 'Grenada', flag: '🇬🇩', region: 'Caribbean' },
|
| { id: 'guatemala', name: 'Guatemala', flag: '🇬🇹', region: 'Central America' },
|
| { id: 'haiti', name: 'Haiti', flag: '🇭🇹', region: 'Caribbean' },
|
| { id: 'honduras', name: 'Honduras', flag: '🇭🇳', region: 'Central America' },
|
| { id: 'jamaica', name: 'Jamaica', flag: '🇯🇲', region: 'Caribbean' },
|
| { id: 'mexico', name: 'Mexico', flag: '🇲🇽', region: 'North America' },
|
| { id: 'nicaragua', name: 'Nicaragua', flag: '🇳🇮', region: 'Central America' },
|
| { id: 'panama', name: 'Panama', flag: '🇵🇦', region: 'Central America' },
|
| { id: 'saint-kitts', name: 'Saint Kitts and Nevis', flag: '🇰🇳', region: 'Caribbean' },
|
| { id: 'saint-lucia', name: 'Saint Lucia', flag: '🇱🇨', region: 'Caribbean' },
|
| { id: 'saint-vincent', name: 'Saint Vincent and the Grenadines', flag: '🇻🇨', region: 'Caribbean' },
|
| { id: 'trinidad', name: 'Trinidad and Tobago', flag: '🇹🇹', region: 'Caribbean' },
|
| { id: 'usa', name: 'United States', flag: '🇺🇸', region: 'North America' },
|
|
|
|
|
| { id: 'argentina', name: 'Argentina', flag: '🇦🇷', region: 'South America' },
|
| { id: 'bolivia', name: 'Bolivia', flag: '🇧🇴', region: 'South America' },
|
| { id: 'brazil', name: 'Brazil', flag: '🇧🇷', region: 'South America' },
|
| { id: 'chile', name: 'Chile', flag: '🇨🇱', region: 'South America' },
|
| { id: 'colombia', name: 'Colombia', flag: '🇨🇴', region: 'South America' },
|
| { id: 'ecuador', name: 'Ecuador', flag: '🇪🇨', region: 'South America' },
|
| { id: 'guyana', name: 'Guyana', flag: '🇬🇾', region: 'South America' },
|
| { id: 'paraguay', name: 'Paraguay', flag: '🇵🇾', region: 'South America' },
|
| { id: 'peru', name: 'Peru', flag: '🇵🇪', region: 'South America' },
|
| { id: 'suriname', name: 'Suriname', flag: '🇸🇷', region: 'South America' },
|
| { id: 'uruguay', name: 'Uruguay', flag: '🇺🇾', region: 'South America' },
|
| { id: 'venezuela', name: 'Venezuela', flag: '🇻🇪', region: 'South America' },
|
|
|
|
|
| { id: 'australia', name: 'Australia', flag: '🇦🇺', region: 'Oceania' },
|
| { id: 'fiji', name: 'Fiji', flag: '🇫🇯', region: 'Oceania' },
|
| { id: 'kiribati', name: 'Kiribati', flag: '🇰🇮', region: 'Oceania' },
|
| { id: 'marshall-islands', name: 'Marshall Islands', flag: '🇲🇭', region: 'Oceania' },
|
| { id: 'micronesia', name: 'Micronesia', flag: '🇫🇲', region: 'Oceania' },
|
| { id: 'nauru', name: 'Nauru', flag: '🇳🇷', region: 'Oceania' },
|
| { id: 'new-zealand', name: 'New Zealand', flag: '🇳🇿', region: 'Oceania' },
|
| { id: 'palau', name: 'Palau', flag: '🇵🇼', region: 'Oceania' },
|
| { id: 'papua-new-guinea', name: 'Papua New Guinea', flag: '🇵🇬', region: 'Oceania' },
|
| { id: 'samoa', name: 'Samoa', flag: '🇼🇸', region: 'Oceania' },
|
| { id: 'solomon-islands', name: 'Solomon Islands', flag: '🇸🇧', region: 'Oceania' },
|
| { id: 'tonga', name: 'Tonga', flag: '🇹🇴', region: 'Oceania' },
|
| { id: 'tuvalu', name: 'Tuvalu', flag: '🇹🇻', region: 'Oceania' },
|
| { id: 'vanuatu', name: 'Vanuatu', flag: '🇻🇺', region: 'Oceania' }
|
| ];
|
|
|
| const THEMES = [
|
| {
|
| id: 'family',
|
| name: 'Family Life',
|
| icon: '👨👩👧👦',
|
| description: 'Family structures, relationships, and traditions'
|
| },
|
| {
|
| id: 'education',
|
| name: 'Education',
|
| icon: '📚',
|
| description: 'Learning systems, values, and approaches to knowledge'
|
| },
|
| {
|
| id: 'traditions',
|
| name: 'Traditions & Festivals',
|
| icon: '🎭',
|
| description: 'Cultural celebrations, rituals, and customs'
|
| },
|
| {
|
| id: 'communication',
|
| name: 'Communication',
|
| icon: '💬',
|
| description: 'Language, non-verbal cues, and social interactions'
|
| },
|
| {
|
| id: 'work',
|
| name: 'Work & Business',
|
| icon: '💼',
|
| description: 'Professional culture, work ethics, and business practices'
|
| },
|
| {
|
| id: 'food',
|
| name: 'Food & Dining',
|
| icon: '🍽️',
|
| description: 'Culinary traditions, dining etiquette, and food culture'
|
| },
|
| {
|
| id: 'values',
|
| name: 'Core Values',
|
| icon: '⭐',
|
| description: 'Fundamental beliefs, principles, and worldviews'
|
| },
|
| {
|
| id: 'challenges',
|
| name: 'Modern Challenges',
|
| icon: '🌍',
|
| description: 'Contemporary issues and cultural adaptations'
|
| }
|
| ];
|
|
|
| const REFLECTION_PROMPTS = [
|
| {
|
| id: 'similarities',
|
| question: 'What similarities did you discover between this culture and your own?',
|
| icon: '🤝'
|
| },
|
| {
|
| id: 'differences',
|
| question: 'What differences surprised you the most?',
|
| icon: '🔍'
|
| },
|
| {
|
| id: 'assumptions',
|
| question: 'What assumptions about this culture were challenged?',
|
| icon: '💭'
|
| },
|
| {
|
| id: 'learning',
|
| question: 'What valuable lesson or practice could you adopt from this culture?',
|
| icon: '💡'
|
| },
|
| {
|
| id: 'empathy',
|
| question: 'How has this exploration changed your perspective on cultural diversity?',
|
| icon: '❤️'
|
| }
|
| ];
|
|
|
|
|
| function CountryDropdown({ selectedCountry, onCountrySelect }) {
|
| const [isOpen, setIsOpen] = useState(false);
|
| const [searchTerm, setSearchTerm] = useState('');
|
| const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
| const dropdownRef = useRef(null);
|
| const inputRef = useRef(null);
|
|
|
| const filteredCountries = WORLD_COUNTRIES.filter(country =>
|
| country.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
| country.region.toLowerCase().includes(searchTerm.toLowerCase())
|
| );
|
|
|
| useEffect(() => {
|
| function handleClickOutside(event) {
|
| if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
| setIsOpen(false);
|
| setHighlightedIndex(-1);
|
| }
|
| }
|
|
|
| document.addEventListener('mousedown', handleClickOutside);
|
| return () => document.removeEventListener('mousedown', handleClickOutside);
|
| }, []);
|
|
|
| const handleInputChange = (e) => {
|
| setSearchTerm(e.target.value);
|
| setIsOpen(true);
|
| setHighlightedIndex(-1);
|
| };
|
|
|
| const handleKeyDown = (e) => {
|
| if (!isOpen) {
|
| if (e.key === 'Enter' || e.key === 'ArrowDown') {
|
| setIsOpen(true);
|
| setHighlightedIndex(0);
|
| }
|
| return;
|
| }
|
|
|
| switch (e.key) {
|
| case 'ArrowDown':
|
| e.preventDefault();
|
| setHighlightedIndex(prev =>
|
| prev < filteredCountries.length - 1 ? prev + 1 : 0
|
| );
|
| break;
|
| case 'ArrowUp':
|
| e.preventDefault();
|
| setHighlightedIndex(prev =>
|
| prev > 0 ? prev - 1 : filteredCountries.length - 1
|
| );
|
| break;
|
| case 'Enter':
|
| e.preventDefault();
|
| if (highlightedIndex >= 0 && filteredCountries[highlightedIndex]) {
|
| handleCountrySelect(filteredCountries[highlightedIndex]);
|
| }
|
| break;
|
| case 'Escape':
|
| setIsOpen(false);
|
| setHighlightedIndex(-1);
|
| inputRef.current?.blur();
|
| break;
|
| }
|
| };
|
|
|
| const handleCountrySelect = (country) => {
|
| onCountrySelect(country);
|
| setSearchTerm('');
|
| setIsOpen(false);
|
| setHighlightedIndex(-1);
|
| };
|
|
|
| const displayValue = selectedCountry
|
| ? `${selectedCountry.flag} ${selectedCountry.name}`
|
| : searchTerm;
|
|
|
| return (
|
| <div className="dropdown-container" ref={dropdownRef}>
|
| <input
|
| ref={inputRef}
|
| type="text"
|
| className="dropdown-input"
|
| placeholder={selectedCountry ? `${selectedCountry.flag} ${selectedCountry.name}` : "Search for a country..."}
|
| value={isOpen ? searchTerm : displayValue}
|
| onChange={handleInputChange}
|
| onKeyDown={handleKeyDown}
|
| onFocus={() => setIsOpen(true)}
|
| autoComplete="off"
|
| aria-expanded={isOpen}
|
| aria-haspopup="listbox"
|
| role="combobox"
|
| />
|
|
|
| {isOpen && (
|
| <div className="dropdown-list" role="listbox">
|
| {filteredCountries.length > 0 ? (
|
| filteredCountries.map((country, index) => (
|
| <div
|
| key={country.id}
|
| className={`dropdown-item ${index === highlightedIndex ? 'highlighted' : ''}`}
|
| onClick={() => handleCountrySelect(country)}
|
| role="option"
|
| aria-selected={selectedCountry?.id === country.id}
|
| >
|
| <span className="text-xl">{country.flag}</span>
|
| <div>
|
| <div className="font-semibold text-gray-800">{country.name}</div>
|
| <div className="text-sm text-gray-500">{country.region}</div>
|
| </div>
|
| </div>
|
| ))
|
| ) : (
|
| <div className="dropdown-item text-gray-500">
|
| No countries found matching "{searchTerm}"
|
| </div>
|
| )}
|
| </div>
|
| )}
|
| </div>
|
| );
|
| }
|
|
|
| function CulturalIntelligenceExplorer() {
|
|
|
| const [phase, setPhase] = useState(1);
|
| const [selectedCountry, setSelectedCountry] = useState(null);
|
| const [selectedTheme, setSelectedTheme] = useState(null);
|
| const [apiKey, setApiKey] = useState('');
|
| const [showApiKeyModal, setShowApiKeyModal] = useState(false);
|
|
|
|
|
| const [insights, setInsights] = useState(null);
|
| const [isGeneratingInsights, setIsGeneratingInsights] = useState(false);
|
| const [aiError, setAiError] = useState('');
|
|
|
|
|
| const [reflections, setReflections] = useState({});
|
| const [explorationHistory, setExplorationHistory] = useState([]);
|
|
|
|
|
| const [analysis, setAnalysis] = useState(null);
|
| const [isGeneratingAnalysis, setIsGeneratingAnalysis] = useState(false);
|
|
|
|
|
| useEffect(() => {
|
| try {
|
| const savedApiKey = localStorage.getItem('cultural-explorer-api-key');
|
| const savedHistory = localStorage.getItem('cultural-explorer-history');
|
|
|
| if (savedApiKey) {
|
| setApiKey(savedApiKey);
|
| }
|
| if (savedHistory) {
|
| setExplorationHistory(JSON.parse(savedHistory));
|
| }
|
| } catch (error) {
|
| console.error('Error loading saved data:', error);
|
| }
|
| }, []);
|
|
|
|
|
| useEffect(() => {
|
| try {
|
| if (apiKey) {
|
| localStorage.setItem('cultural-explorer-api-key', apiKey);
|
| }
|
| } catch (error) {
|
| console.error('Error saving API key:', error);
|
| }
|
| }, [apiKey]);
|
|
|
| useEffect(() => {
|
| try {
|
| localStorage.setItem('cultural-explorer-history', JSON.stringify(explorationHistory));
|
| } catch (error) {
|
| console.error('Error saving history:', error);
|
| }
|
| }, [explorationHistory]);
|
|
|
| const handleCountrySelect = (country) => {
|
| setSelectedCountry(country);
|
| };
|
|
|
| const handleThemeSelect = (theme) => {
|
| setSelectedTheme(theme);
|
| };
|
|
|
| const proceedToInsights = () => {
|
| if (!selectedCountry) {
|
| alert('Please select a country to explore');
|
| return;
|
| }
|
|
|
| if (!apiKey) {
|
| setShowApiKeyModal(true);
|
| return;
|
| }
|
|
|
| setPhase(2);
|
| generateInsights();
|
| };
|
|
|
| const generateInsights = async () => {
|
| if (!apiKey || !selectedCountry) return;
|
|
|
| setIsGeneratingInsights(true);
|
| setAiError('');
|
|
|
| try {
|
| const prompt = createInsightPrompt();
|
| const response = await callOpenAI(prompt);
|
| setInsights(response);
|
| } catch (error) {
|
| console.error('Error generating insights:', error);
|
| setAiError(`Failed to generate insights: ${error.message}`);
|
| } finally {
|
| setIsGeneratingInsights(false);
|
| }
|
| };
|
|
|
| const createInsightPrompt = () => {
|
| const country = selectedCountry;
|
| const theme = selectedTheme;
|
|
|
| return `You are a cultural intelligence educator helping students develop empathy and global citizenship. Provide comprehensive, respectful insights about ${country.name} culture.
|
|
|
| ${theme ? `Focus specifically on: ${theme.name} - ${theme.description}` : 'Provide a general cultural overview.'}
|
|
|
| Structure your response with the following sections:
|
|
|
| **CULTURAL OVERVIEW:**
|
| Provide a respectful, nuanced introduction to ${country.name} culture, avoiding stereotypes.
|
|
|
| **KEY CULTURAL VALUES:**
|
| List and explain 4-5 core values that guide this culture.
|
|
|
| **DAILY LIFE INSIGHTS:**
|
| Describe how these values manifest in everyday life.
|
|
|
| ${theme ? `**${theme.name.toUpperCase()} FOCUS:**
|
| Provide detailed insights about ${theme.description} in ${country.name} culture.` : ''}
|
|
|
| **HISTORICAL CONTEXT:**
|
| Brief background on how history shaped current cultural practices.
|
|
|
| **MODERN ADAPTATIONS:**
|
| How this culture navigates contemporary global challenges while preserving traditions.
|
|
|
| **COMMON MISCONCEPTIONS:**
|
| Address 2-3 common stereotypes or misconceptions about this culture.
|
|
|
| **CULTURAL WISDOM:**
|
| Share a meaningful lesson or perspective this culture offers to the world.
|
|
|
| Keep the tone educational, respectful, and engaging. Use specific examples while avoiding overgeneralization. Aim for 800-1000 words total.`;
|
| };
|
|
|
| const callOpenAI = async (prompt) => {
|
| if (!apiKey) {
|
| throw new Error('API key not provided');
|
| }
|
|
|
| const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
| method: 'POST',
|
| headers: {
|
| 'Content-Type': 'application/json',
|
| 'Authorization': `Bearer ${apiKey.trim()}`
|
| },
|
| body: JSON.stringify({
|
| model: 'gpt-4o-mini',
|
| messages: [
|
| { role: 'system', content: 'You are a cultural intelligence educator focused on promoting empathy, understanding, and global citizenship.' },
|
| { role: 'user', content: prompt }
|
| ],
|
| max_tokens: 1200,
|
| temperature: 0.7
|
| })
|
| });
|
|
|
| if (!response.ok) {
|
| const errorData = await response.json().catch(() => ({}));
|
| throw new Error(`OpenAI API error: ${response.status} ${response.statusText}${errorData.error ? ` - ${errorData.error.message}` : ''}`);
|
| }
|
|
|
| const data = await response.json();
|
|
|
| if (!data.choices || !data.choices[0] || !data.choices[0].message) {
|
| throw new Error('Invalid response format from OpenAI API');
|
| }
|
|
|
| return data.choices[0].message.content;
|
| };
|
|
|
| const proceedToReflection = () => {
|
| setPhase(3);
|
| };
|
|
|
| const handleReflectionChange = (promptId, value) => {
|
| setReflections(prev => ({
|
| ...prev,
|
| [promptId]: value
|
| }));
|
| };
|
|
|
| const proceedToAnalysis = () => {
|
| setPhase(4);
|
| generateAnalysis();
|
| };
|
|
|
| const generateAnalysis = async () => {
|
| if (!apiKey || !selectedCountry || !insights) return;
|
|
|
| setIsGeneratingAnalysis(true);
|
| setAiError('');
|
|
|
| try {
|
| const prompt = createAnalysisPrompt();
|
| const response = await callOpenAI(prompt);
|
| setAnalysis(response);
|
| } catch (error) {
|
| console.error('Error generating analysis:', error);
|
| setAiError(`Failed to generate analysis: ${error.message}`);
|
| } finally {
|
| setIsGeneratingAnalysis(false);
|
| }
|
| };
|
|
|
| const createAnalysisPrompt = () => {
|
| const country = selectedCountry;
|
| const theme = selectedTheme;
|
| const reflectionText = Object.entries(reflections)
|
| .map(([key, value]) => {
|
| const prompt = REFLECTION_PROMPTS.find(p => p.id === key);
|
| return `${prompt?.question}: ${value}`;
|
| })
|
| .join('\n\n');
|
|
|
| return `You are a cultural intelligence educator analyzing a student's cultural exploration journey. Based on the cultural insights and student reflections below, provide a comprehensive analysis in a structured tabular format.
|
|
|
| **CULTURAL CONTEXT:**
|
| Country: ${country.name} (${country.region})
|
| ${theme ? `Focus Theme: ${theme.name} - ${theme.description}` : 'General cultural exploration'}
|
|
|
| **CULTURAL INSIGHTS PROVIDED:**
|
| ${insights}
|
|
|
| **STUDENT REFLECTIONS:**
|
| ${reflectionText}
|
|
|
| **ANALYSIS REQUIREMENTS:**
|
| Create a detailed analysis table with the following structure. Format your response as a structured table with clear rows and columns:
|
|
|
| | Analysis Category | Cultural Theme Insights | Student Reflection Analysis | Comparison & Growth Indicators |
|
| |-------------------|------------------------|----------------------------|-------------------------------|
|
| | Cultural Understanding | [Key cultural insights from the theme] | [Analysis of student's understanding] | [Evidence of learning and growth] |
|
| | Personal Connection | [How culture relates to universal themes] | [Student's personal connections made] | [Depth of empathy development] |
|
| | Assumption Challenges | [Cultural misconceptions addressed] | [Student's assumption changes] | [Critical thinking evidence] |
|
| | Learning Integration | [Key cultural lessons identified] | [Student's learning takeaways] | [Application potential] |
|
| | Perspective Transformation | [Cultural wisdom shared] | [Student's perspective shifts] | [Global citizenship development] |
|
|
|
| For each row, provide:
|
| - 2-3 specific insights from the cultural theme
|
| - Detailed analysis of the student's reflection quality and depth
|
| - Evidence of learning, growth, and cultural intelligence development
|
| - Specific examples and quotes from student responses where relevant
|
|
|
| Keep the analysis educational, encouraging, and focused on cultural intelligence development. Highlight strengths and areas for continued growth.`;
|
| };
|
|
|
| const completeExploration = () => {
|
| const exploration = {
|
| id: Date.now(),
|
| timestamp: new Date().toISOString(),
|
| country: selectedCountry,
|
| theme: selectedTheme,
|
| insights: insights,
|
| reflections: reflections,
|
| analysis: analysis
|
| };
|
|
|
| setExplorationHistory(prev => [...prev, exploration]);
|
|
|
|
|
| setPhase(1);
|
| setSelectedCountry(null);
|
| setSelectedTheme(null);
|
| setInsights(null);
|
| setReflections({});
|
| setAnalysis(null);
|
| setAiError('');
|
| };
|
|
|
| const validateApiKey = async (key) => {
|
| try {
|
| const response = await fetch('https://api.openai.com/v1/models', {
|
| headers: {
|
| 'Authorization': `Bearer ${key.trim()}`
|
| }
|
| });
|
|
|
| if (!response.ok) {
|
| throw new Error('Invalid API key');
|
| }
|
|
|
| return true;
|
| } catch (error) {
|
| throw new Error('Invalid API key. Please check and try again.');
|
| }
|
| };
|
|
|
| const handleApiKeySubmit = async (key) => {
|
| try {
|
| await validateApiKey(key);
|
| setApiKey(key);
|
| setShowApiKeyModal(false);
|
| setPhase(2);
|
| generateInsights();
|
| } catch (error) {
|
| setAiError(error.message);
|
| }
|
| };
|
|
|
| const formatInsights = (content) => {
|
|
|
| let formatted = content
|
| .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
| .replace(/^• (.+)$/gm, '<li>$1</li>')
|
| .replace(/^- (.+)$/gm, '<li>$1</li>')
|
| .split('\n\n')
|
| .map(paragraph => {
|
| if (paragraph.includes('<li>')) {
|
| return `<ul class="list-disc list-inside space-y-1">${paragraph}</ul>`;
|
| }
|
| return paragraph.trim() ? `<p class="mb-3">${paragraph.trim()}</p>` : '';
|
| })
|
| .join('');
|
|
|
| return formatted;
|
| };
|
|
|
| const parseAnalysisTable = (content) => {
|
|
|
| const lines = content.split('\n').filter(line => line.trim());
|
| const tableData = [];
|
|
|
| let currentRow = null;
|
| let inTable = false;
|
|
|
| for (const line of lines) {
|
| if (line.includes('|') && line.includes('Analysis Category')) {
|
| inTable = true;
|
| continue;
|
| }
|
|
|
| if (line.includes('|') && line.includes('---')) {
|
| continue;
|
| }
|
|
|
| if (inTable && line.includes('|')) {
|
| const cells = line.split('|').map(cell => cell.trim()).filter(cell => cell);
|
| if (cells.length >= 4) {
|
| tableData.push({
|
| category: cells[0],
|
| cultural: cells[1],
|
| reflection: cells[2],
|
| comparison: cells[3]
|
| });
|
| }
|
| }
|
| }
|
|
|
|
|
| if (tableData.length === 0) {
|
| const sections = content.split('\n\n');
|
| sections.forEach((section, index) => {
|
| if (section.trim()) {
|
| tableData.push({
|
| category: `Analysis ${index + 1}`,
|
| cultural: section.substring(0, 200) + '...',
|
| reflection: 'Detailed analysis provided',
|
| comparison: 'Growth indicators identified'
|
| });
|
| }
|
| });
|
| }
|
|
|
| return tableData;
|
| };
|
|
|
| const getProgressPercentage = () => {
|
| return (phase / 4) * 100;
|
| };
|
|
|
|
|
| const ApiKeyModal = () => {
|
| const [keyInput, setKeyInput] = useState('');
|
| const [isValidating, setIsValidating] = useState(false);
|
|
|
| const handleSubmit = async (e) => {
|
| e.preventDefault();
|
| if (!keyInput.trim()) return;
|
|
|
| setIsValidating(true);
|
| try {
|
| await handleApiKeySubmit(keyInput);
|
| } catch (error) {
|
|
|
| } finally {
|
| setIsValidating(false);
|
| }
|
| };
|
|
|
| return (
|
| <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
| <div className="glass p-6 max-w-md w-full">
|
| <h3 className="text-xl font-bold text-gray-800 mb-4">🔑 OpenAI API Key Required</h3>
|
| <p className="text-gray-600 mb-4">
|
| To generate cultural insights, please provide your OpenAI API key.
|
| This will be stored locally and used only for this application.
|
| </p>
|
| <form onSubmit={handleSubmit}>
|
| <input
|
| type="password"
|
| value={keyInput}
|
| onChange={(e) => setKeyInput(e.target.value)}
|
| placeholder="sk-..."
|
| className="w-full px-3 py-2 border rounded-lg mb-4"
|
| autoFocus
|
| />
|
| <div className="flex gap-2">
|
| <button
|
| type="submit"
|
| disabled={!keyInput.trim() || isValidating}
|
| className="flex-1 bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition disabled:opacity-50"
|
| >
|
| {isValidating ? 'Validating...' : 'Continue'}
|
| </button>
|
| <button
|
| type="button"
|
| onClick={() => setShowApiKeyModal(false)}
|
| className="px-4 py-2 bg-gray-300 text-gray-700 rounded-lg hover:bg-gray-400 transition"
|
| >
|
| Cancel
|
| </button>
|
| </div>
|
| </form>
|
| {aiError && (
|
| <div className="mt-3 text-red-600 text-sm">{aiError}</div>
|
| )}
|
| <p className="text-xs text-gray-500 mt-3">
|
| Get your API key at <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="text-blue-600 underline">OpenAI</a>
|
| </p>
|
| </div>
|
| </div>
|
| );
|
| };
|
|
|
| return (
|
| <div className="min-h-screen py-6">
|
| <div className="max-w-6xl mx-auto px-4">
|
| {/* Header */}
|
| <div className="glass p-6 mb-6 text-center">
|
| <h1 className="text-4xl font-bold text-gray-800 mb-2">
|
| 🌍 Cultural Intelligence Explorer
|
| </h1>
|
| <p className="text-gray-600 mb-4">
|
| Develop empathy and global citizenship through cultural exploration
|
| </p>
|
| <p className="text-sm text-blue-600 font-semibold">
|
| Global Edition with AI Analysis - Explore any country in the world!
|
| </p>
|
|
|
| {/* Progress Bar */}
|
| <div className="max-w-md mx-auto mt-4">
|
| <div className="flex justify-between text-xs text-gray-500 mb-2">
|
| <span className={phase >= 1 ? 'text-blue-600 font-semibold' : ''}>Selection</span>
|
| <span className={phase >= 2 ? 'text-blue-600 font-semibold' : ''}>Insights</span>
|
| <span className={phase >= 3 ? 'text-blue-600 font-semibold' : ''}>Reflection</span>
|
| <span className={phase >= 4 ? 'text-blue-600 font-semibold' : ''}>Analysis</span>
|
| </div>
|
| <div className="w-full bg-gray-200 rounded-full h-2">
|
| <div
|
| className="progress-bar rounded-full h-2"
|
| style={{ width: `${getProgressPercentage()}%` }}
|
| ></div>
|
| </div>
|
| </div>
|
| </div>
|
|
|
| {/* Phase 1: Cultural Selection */}
|
| {phase === 1 && (
|
| <div className="fade-in">
|
| <div className="glass p-6 mb-6">
|
| <h2 className="text-2xl font-bold text-gray-800 mb-4">
|
| 🎯 Phase 1: Choose Your Cultural Journey
|
| </h2>
|
| <p className="text-gray-600 mb-6">
|
| Select any country in the world to explore and optionally choose a specific theme to focus on.
|
| </p>
|
|
|
| {/* Country Selection */}
|
| <div className="mb-8">
|
| <div className="country-dropdown">
|
| <h3 className="text-xl font-semibold mb-4">🌎 Select a Country</h3>
|
| <p className="text-sm opacity-90 mb-4">
|
| Search from {WORLD_COUNTRIES.length} countries worldwide
|
| </p>
|
| <CountryDropdown
|
| selectedCountry={selectedCountry}
|
| onCountrySelect={handleCountrySelect}
|
| />
|
| </div>
|
| </div>
|
|
|
| {/* Theme Selection */}
|
| <div className="mb-8">
|
| <h3 className="text-xl font-semibold text-gray-700 mb-4">
|
| Choose a Focus Theme (Optional)
|
| </h3>
|
| <div className="grid grid-cols-2 md:grid-cols-4 gap-3 theme-grid">
|
| {THEMES.map(theme => (
|
| <div
|
| key={theme.id}
|
| className={`theme-card ${selectedTheme?.id === theme.id ? 'selected' : ''}`}
|
| onClick={() => handleThemeSelect(selectedTheme?.id === theme.id ? null : theme)}
|
| tabIndex={0}
|
| role="button"
|
| aria-pressed={selectedTheme?.id === theme.id}
|
| onKeyPress={(e) => e.key === 'Enter' && handleThemeSelect(selectedTheme?.id === theme.id ? null : theme)}
|
| >
|
| <div className="text-2xl mb-2">{theme.icon}</div>
|
| <h4 className="font-semibold text-sm">{theme.name}</h4>
|
| <p className="text-xs opacity-80 mt-1">{theme.description}</p>
|
| </div>
|
| ))}
|
| </div>
|
| </div>
|
|
|
| {/* Selection Summary */}
|
| {selectedCountry && (
|
| <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
|
| <h4 className="font-semibold text-blue-800 mb-2">Your Selection:</h4>
|
| <p className="text-blue-700">
|
| <strong>{selectedCountry.flag} {selectedCountry.name}</strong> ({selectedCountry.region})
|
| {selectedTheme && (
|
| <span> - Focus: {selectedTheme.icon} {selectedTheme.name}</span>
|
| )}
|
| </p>
|
| </div>
|
| )}
|
|
|
| {/* Continue Button */}
|
| <div className="text-center">
|
| <button
|
| onClick={proceedToInsights}
|
| disabled={!selectedCountry}
|
| className="bg-gradient-to-r from-blue-600 to-purple-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-blue-700 hover:to-purple-700 transition disabled:opacity-50 disabled:cursor-not-allowed"
|
| >
|
| Explore Culture 🚀
|
| </button>
|
| </div>
|
| </div>
|
| </div>
|
| )}
|
|
|
| {/* Phase 2: AI Cultural Insights */}
|
| {phase === 2 && (
|
| <div className="fade-in">
|
| <div className="glass p-6 mb-6">
|
| <div className="flex items-center justify-between mb-6">
|
| <div>
|
| <h2 className="text-2xl font-bold text-gray-800">
|
| 🧠 Phase 2: Cultural Insights
|
| </h2>
|
| <p className="text-gray-600">
|
| Exploring {selectedCountry?.flag} {selectedCountry?.name} ({selectedCountry?.region})
|
| {selectedTheme && ` - ${selectedTheme.icon} ${selectedTheme.name}`}
|
| </p>
|
| </div>
|
| <button
|
| onClick={() => setPhase(1)}
|
| className="text-blue-600 hover:text-blue-800 transition"
|
| >
|
| ← Back to Selection
|
| </button>
|
| </div>
|
|
|
| {isGeneratingInsights && (
|
| <div className="text-center py-12">
|
| <div className="typing-indicator mb-4">
|
| <span className="text-lg text-gray-600 mr-3">Generating cultural insights</span>
|
| <div className="typing-dot"></div>
|
| <div className="typing-dot"></div>
|
| <div className="typing-dot"></div>
|
| </div>
|
| <p className="text-gray-500">This may take a moment...</p>
|
| </div>
|
| )}
|
|
|
| {aiError && (
|
| <div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
|
| <h4 className="font-semibold text-red-800 mb-2">Error:</h4>
|
| <p className="text-red-700">{aiError}</p>
|
| <button
|
| onClick={generateInsights}
|
| className="mt-3 bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700 transition"
|
| >
|
| Try Again
|
| </button>
|
| </div>
|
| )}
|
|
|
| {insights && (
|
| <div className="slide-in">
|
| <div className="insight-section">
|
| <div
|
| className="prose prose-lg max-w-none"
|
| dangerouslySetInnerHTML={{ __html: formatInsights(insights) }}
|
| />
|
| </div>
|
|
|
| <div className="text-center mt-8">
|
| <button
|
| onClick={proceedToReflection}
|
| className="bg-gradient-to-r from-green-600 to-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-green-700 hover:to-blue-700 transition"
|
| >
|
| Reflect on Learning 💭
|
| </button>
|
| </div>
|
| </div>
|
| )}
|
| </div>
|
| </div>
|
| )}
|
|
|
| {/* Phase 3: Reflection & Comparison */}
|
| {phase === 3 && (
|
| <div className="fade-in">
|
| <div className="glass p-6 mb-6">
|
| <div className="flex items-center justify-between mb-6">
|
| <div>
|
| <h2 className="text-2xl font-bold text-gray-800">
|
| 💭 Phase 3: Reflection & Growth
|
| </h2>
|
| <p className="text-gray-600">
|
| Reflect on your cultural exploration and personal insights
|
| </p>
|
| </div>
|
| <button
|
| onClick={() => setPhase(2)}
|
| className="text-blue-600 hover:text-blue-800 transition"
|
| >
|
| ← Back to Insights
|
| </button>
|
| </div>
|
|
|
| <div className="space-y-6">
|
| {REFLECTION_PROMPTS.map(prompt => (
|
| <div key={prompt.id} className="reflection-card">
|
| <div className="flex items-start gap-3 mb-3">
|
| <span className="text-2xl">{prompt.icon}</span>
|
| <h3 className="font-semibold text-gray-800 flex-1">
|
| {prompt.question}
|
| </h3>
|
| </div>
|
| <textarea
|
| value={reflections[prompt.id] || ''}
|
| onChange={(e) => handleReflectionChange(prompt.id, e.target.value)}
|
| placeholder="Share your thoughts and insights..."
|
| className="w-full px-4 py-3 border border-gray-300 rounded-lg resize-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
| rows={4}
|
| />
|
| </div>
|
| ))}
|
| </div>
|
|
|
| <div className="text-center mt-8">
|
| <button
|
| onClick={proceedToAnalysis}
|
| className="bg-gradient-to-r from-purple-600 to-pink-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-purple-700 hover:to-pink-700 transition"
|
| >
|
| Generate AI Analysis 📊
|
| </button>
|
| </div>
|
| </div>
|
| </div>
|
| )}
|
|
|
| {/* Phase 4: AI Analysis & Comparison */}
|
| {phase === 4 && (
|
| <div className="fade-in">
|
| <div className="glass p-6 mb-6">
|
| <div className="flex items-center justify-between mb-6">
|
| <div>
|
| <h2 className="text-2xl font-bold text-gray-800">
|
| 📊 Phase 4: AI Analysis & Insights
|
| </h2>
|
| <p className="text-gray-600">
|
| AI-powered comparison between cultural themes and your reflections
|
| </p>
|
| </div>
|
| <button
|
| onClick={() => setPhase(3)}
|
| className="text-blue-600 hover:text-blue-800 transition"
|
| >
|
| ← Back to Reflection
|
| </button>
|
| </div>
|
|
|
| {isGeneratingAnalysis && (
|
| <div className="text-center py-12">
|
| <div className="typing-indicator mb-4">
|
| <span className="text-lg text-gray-600 mr-3">Analyzing your cultural journey</span>
|
| <div className="typing-dot"></div>
|
| <div className="typing-dot"></div>
|
| <div className="typing-dot"></div>
|
| </div>
|
| <p className="text-gray-500">Creating personalized insights...</p>
|
| </div>
|
| )}
|
|
|
| {aiError && (
|
| <div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
|
| <h4 className="font-semibold text-red-800 mb-2">Error:</h4>
|
| <p className="text-red-700">{aiError}</p>
|
| <button
|
| onClick={generateAnalysis}
|
| className="mt-3 bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700 transition"
|
| >
|
| Try Again
|
| </button>
|
| </div>
|
| )}
|
|
|
| {analysis && (
|
| <div className="slide-in">
|
| <div className="mb-6">
|
| <h3 className="text-xl font-semibold text-gray-800 mb-4">
|
| 🎯 Cultural Intelligence Analysis
|
| </h3>
|
| <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
|
| <p className="text-blue-700">
|
| <strong>Analysis for:</strong> {selectedCountry?.flag} {selectedCountry?.name}
|
| {selectedTheme && ` - ${selectedTheme.icon} ${selectedTheme.name}`}
|
| </p>
|
| </div>
|
| </div>
|
|
|
| <div className="analysis-table mb-8">
|
| <table>
|
| <thead>
|
| <tr>
|
| <th className="w-1/4">Analysis Category</th>
|
| <th className="w-1/4">Cultural Theme Insights</th>
|
| <th className="w-1/4">Your Reflection Analysis</th>
|
| <th className="w-1/4">Growth Indicators</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {parseAnalysisTable(analysis).map((row, index) => (
|
| <tr key={index}>
|
| <td className="font-semibold text-gray-800">
|
| {row.category}
|
| <div className="mt-2">
|
| <span className="insight-badge">Cultural Insight</span>
|
| </div>
|
| </td>
|
| <td className="text-gray-700">
|
| {row.cultural}
|
| </td>
|
| <td className="text-gray-700">
|
| {row.reflection}
|
| <div className="mt-2">
|
| <span className="comparison-badge">Personal Growth</span>
|
| </div>
|
| </td>
|
| <td className="text-gray-700">
|
| {row.comparison}
|
| </td>
|
| </tr>
|
| ))}
|
| </tbody>
|
| </table>
|
| </div>
|
|
|
| <div className="text-center mt-8">
|
| <button
|
| onClick={completeExploration}
|
| className="bg-gradient-to-r from-green-600 to-teal-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-green-700 hover:to-teal-700 transition"
|
| >
|
| Complete Exploration ✨
|
| </button>
|
| </div>
|
| </div>
|
| )}
|
| </div>
|
| </div>
|
| )}
|
|
|
| {/* Exploration History */}
|
| {explorationHistory.length > 0 && (
|
| <div className="glass p-6">
|
| <h3 className="text-xl font-bold text-gray-800 mb-4">
|
| 📚 Your Cultural Journey
|
| </h3>
|
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
| {explorationHistory.slice(-6).map(exploration => (
|
| <div key={exploration.id} className="bg-white rounded-lg p-4 border border-gray-200">
|
| <div className="flex items-center gap-2 mb-2">
|
| <span className="text-xl">{exploration.country.flag}</span>
|
| <h4 className="font-semibold text-gray-800">{exploration.country.name}</h4>
|
| </div>
|
| <p className="text-xs text-gray-500 mb-1">{exploration.country.region}</p>
|
| {exploration.theme && (
|
| <p className="text-sm text-gray-600 mb-2">
|
| {exploration.theme.icon} {exploration.theme.name}
|
| </p>
|
| )}
|
| <div className="flex gap-1 mb-2">
|
| <span className="insight-badge text-xs">Insights</span>
|
| <span className="comparison-badge text-xs">Analysis</span>
|
| </div>
|
| <p className="text-xs text-gray-500">
|
| {new Date(exploration.timestamp).toLocaleDateString()}
|
| </p>
|
| </div>
|
| ))}
|
| </div>
|
| <p className="text-center text-gray-500 mt-4">
|
| {explorationHistory.length} culture{explorationHistory.length !== 1 ? 's' : ''} explored with AI analysis
|
| </p>
|
| </div>
|
| )}
|
| </div>
|
|
|
| {/* API Key Modal */}
|
| {showApiKeyModal && <ApiKeyModal />}
|
| </div>
|
| );
|
| }
|
|
|
| ReactDOM.createRoot(document.getElementById("root")).render(<CulturalIntelligenceExplorer />);
|
| </script>
|
| </body>
|
| </html>
|
|
|
|
|