|
|
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<title>Enhanced Virtual Internship Simulator with AI Chatbot</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>
|
|
|
<style>
|
|
|
* {
|
|
|
margin: 0;
|
|
|
padding: 0;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
body {
|
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
min-height: 100vh;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.app-container {
|
|
|
max-width: 1400px;
|
|
|
margin: 0 auto;
|
|
|
padding: 20px;
|
|
|
}
|
|
|
|
|
|
.header {
|
|
|
text-align: center;
|
|
|
margin-bottom: 30px;
|
|
|
color: white;
|
|
|
}
|
|
|
|
|
|
.header h1 {
|
|
|
font-size: 2.5rem;
|
|
|
margin-bottom: 10px;
|
|
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
|
|
}
|
|
|
|
|
|
.header p {
|
|
|
font-size: 1.2rem;
|
|
|
opacity: 0.9;
|
|
|
}
|
|
|
|
|
|
.progress-bar {
|
|
|
background: rgba(255,255,255,0.2);
|
|
|
border-radius: 25px;
|
|
|
padding: 5px;
|
|
|
margin-bottom: 30px;
|
|
|
backdrop-filter: blur(10px);
|
|
|
}
|
|
|
|
|
|
.progress-steps {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.progress-step {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
padding: 10px 15px;
|
|
|
border-radius: 20px;
|
|
|
color: white;
|
|
|
font-weight: 500;
|
|
|
transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.progress-step.active {
|
|
|
background: rgba(255,255,255,0.3);
|
|
|
transform: scale(1.05);
|
|
|
}
|
|
|
|
|
|
.progress-step.completed {
|
|
|
background: rgba(76, 175, 80, 0.8);
|
|
|
}
|
|
|
|
|
|
.main-content {
|
|
|
background: rgba(255,255,255,0.95);
|
|
|
border-radius: 20px;
|
|
|
padding: 30px;
|
|
|
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
|
|
backdrop-filter: blur(10px);
|
|
|
min-height: 600px;
|
|
|
position: relative;
|
|
|
}
|
|
|
|
|
|
.role-grid {
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
|
gap: 20px;
|
|
|
margin-bottom: 30px;
|
|
|
}
|
|
|
|
|
|
.role-card {
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
border-radius: 15px;
|
|
|
padding: 25px;
|
|
|
color: white;
|
|
|
cursor: pointer;
|
|
|
transition: all 0.3s ease;
|
|
|
border: 3px solid transparent;
|
|
|
position: relative;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.role-card:hover {
|
|
|
transform: translateY(-5px);
|
|
|
box-shadow: 0 15px 30px rgba(0,0,0,0.2);
|
|
|
}
|
|
|
|
|
|
.role-card.selected {
|
|
|
border-color: #4CAF50;
|
|
|
transform: scale(1.02);
|
|
|
}
|
|
|
|
|
|
.role-card.custom-role {
|
|
|
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
|
|
|
border: 3px dashed rgba(255,255,255,0.5);
|
|
|
}
|
|
|
|
|
|
.role-card.custom-role:hover {
|
|
|
border-color: rgba(255,255,255,0.8);
|
|
|
}
|
|
|
|
|
|
.role-icon {
|
|
|
font-size: 3rem;
|
|
|
margin-bottom: 15px;
|
|
|
display: block;
|
|
|
}
|
|
|
|
|
|
.role-title {
|
|
|
font-size: 1.5rem;
|
|
|
font-weight: bold;
|
|
|
margin-bottom: 10px;
|
|
|
}
|
|
|
|
|
|
.role-description {
|
|
|
opacity: 0.9;
|
|
|
line-height: 1.5;
|
|
|
font-size: 0.95rem;
|
|
|
}
|
|
|
|
|
|
.category-section {
|
|
|
margin-bottom: 40px;
|
|
|
}
|
|
|
|
|
|
.category-title {
|
|
|
font-size: 1.5rem;
|
|
|
font-weight: bold;
|
|
|
color: #333;
|
|
|
margin-bottom: 20px;
|
|
|
padding-bottom: 10px;
|
|
|
border-bottom: 3px solid #667eea;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
.custom-role-form {
|
|
|
background: #f8f9fa;
|
|
|
border-radius: 15px;
|
|
|
padding: 25px;
|
|
|
margin-top: 20px;
|
|
|
border: 2px solid #e9ecef;
|
|
|
}
|
|
|
|
|
|
.custom-role-form h3 {
|
|
|
color: #333;
|
|
|
margin-bottom: 20px;
|
|
|
font-size: 1.3rem;
|
|
|
}
|
|
|
|
|
|
.form-group {
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
.form-group label {
|
|
|
display: block;
|
|
|
margin-bottom: 8px;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.form-group input,
|
|
|
.form-group textarea,
|
|
|
.form-group select {
|
|
|
width: 100%;
|
|
|
padding: 12px;
|
|
|
border: 2px solid #e0e0e0;
|
|
|
border-radius: 8px;
|
|
|
font-size: 16px;
|
|
|
transition: border-color 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.form-group input:focus,
|
|
|
.form-group textarea:focus,
|
|
|
.form-group select:focus {
|
|
|
outline: none;
|
|
|
border-color: #667eea;
|
|
|
}
|
|
|
|
|
|
.form-group textarea {
|
|
|
resize: vertical;
|
|
|
min-height: 100px;
|
|
|
}
|
|
|
|
|
|
.btn {
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
color: white;
|
|
|
border: none;
|
|
|
padding: 15px 30px;
|
|
|
border-radius: 25px;
|
|
|
font-size: 16px;
|
|
|
font-weight: 600;
|
|
|
cursor: pointer;
|
|
|
transition: all 0.3s ease;
|
|
|
display: inline-block;
|
|
|
text-decoration: none;
|
|
|
margin-right: 10px;
|
|
|
}
|
|
|
|
|
|
.btn:hover {
|
|
|
transform: translateY(-2px);
|
|
|
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
|
|
|
}
|
|
|
|
|
|
.btn:disabled {
|
|
|
opacity: 0.6;
|
|
|
cursor: not-allowed;
|
|
|
transform: none;
|
|
|
}
|
|
|
|
|
|
.btn-secondary {
|
|
|
background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
|
|
|
}
|
|
|
|
|
|
.task-dashboard {
|
|
|
display: grid;
|
|
|
gap: 20px;
|
|
|
}
|
|
|
|
|
|
.task-card {
|
|
|
background: #f8f9fa;
|
|
|
border-radius: 15px;
|
|
|
padding: 25px;
|
|
|
border: 2px solid #e9ecef;
|
|
|
transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.task-card.active {
|
|
|
border-color: #667eea;
|
|
|
background: #f0f4ff;
|
|
|
}
|
|
|
|
|
|
.task-card.completed {
|
|
|
border-color: #28a745;
|
|
|
background: #f0fff4;
|
|
|
}
|
|
|
|
|
|
.task-header {
|
|
|
display: flex;
|
|
|
justify-content: between;
|
|
|
align-items: center;
|
|
|
margin-bottom: 15px;
|
|
|
}
|
|
|
|
|
|
.task-number {
|
|
|
background: #667eea;
|
|
|
color: white;
|
|
|
width: 30px;
|
|
|
height: 30px;
|
|
|
border-radius: 50%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
font-weight: bold;
|
|
|
margin-right: 15px;
|
|
|
}
|
|
|
|
|
|
.task-title {
|
|
|
font-size: 1.3rem;
|
|
|
font-weight: bold;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.task-status {
|
|
|
padding: 5px 15px;
|
|
|
border-radius: 20px;
|
|
|
font-size: 0.9rem;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
.status-pending {
|
|
|
background: #fff3cd;
|
|
|
color: #856404;
|
|
|
}
|
|
|
|
|
|
.status-active {
|
|
|
background: #cce5ff;
|
|
|
color: #004085;
|
|
|
}
|
|
|
|
|
|
.status-completed {
|
|
|
background: #d4edda;
|
|
|
color: #155724;
|
|
|
}
|
|
|
|
|
|
.ai-feedback {
|
|
|
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
|
|
border-radius: 15px;
|
|
|
padding: 25px;
|
|
|
margin: 20px 0;
|
|
|
border-left: 5px solid #2196f3;
|
|
|
}
|
|
|
|
|
|
.ai-feedback h4 {
|
|
|
color: #1976d2;
|
|
|
margin-bottom: 15px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
.ai-feedback p {
|
|
|
line-height: 1.6;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.skill-radar {
|
|
|
background: white;
|
|
|
border-radius: 15px;
|
|
|
padding: 25px;
|
|
|
margin: 20px 0;
|
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
|
|
}
|
|
|
|
|
|
.badges-container {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 10px;
|
|
|
margin: 20px 0;
|
|
|
}
|
|
|
|
|
|
.badge {
|
|
|
background: linear-gradient(135deg, #ffd700 0%, #ffb300 100%);
|
|
|
color: #333;
|
|
|
padding: 8px 16px;
|
|
|
border-radius: 20px;
|
|
|
font-weight: bold;
|
|
|
font-size: 0.9rem;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 5px;
|
|
|
}
|
|
|
|
|
|
.loading-spinner {
|
|
|
border: 4px solid #f3f3f3;
|
|
|
border-top: 4px solid #667eea;
|
|
|
border-radius: 50%;
|
|
|
width: 40px;
|
|
|
height: 40px;
|
|
|
animation: spin 1s linear infinite;
|
|
|
margin: 0 auto 20px;
|
|
|
}
|
|
|
|
|
|
@keyframes spin {
|
|
|
0% { transform: rotate(0deg); }
|
|
|
100% { transform: rotate(360deg); }
|
|
|
}
|
|
|
|
|
|
.error-message {
|
|
|
background: #f8d7da;
|
|
|
color: #721c24;
|
|
|
padding: 15px;
|
|
|
border-radius: 8px;
|
|
|
margin: 20px 0;
|
|
|
border: 1px solid #f5c6cb;
|
|
|
}
|
|
|
|
|
|
.success-message {
|
|
|
background: #d4edda;
|
|
|
color: #155724;
|
|
|
padding: 15px;
|
|
|
border-radius: 8px;
|
|
|
margin: 20px 0;
|
|
|
border: 1px solid #c3e6cb;
|
|
|
}
|
|
|
|
|
|
|
|
|
.chat-button {
|
|
|
position: fixed;
|
|
|
bottom: 30px;
|
|
|
right: 30px;
|
|
|
width: 60px;
|
|
|
height: 60px;
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
border-radius: 50%;
|
|
|
border: none;
|
|
|
color: white;
|
|
|
font-size: 24px;
|
|
|
cursor: pointer;
|
|
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
|
|
transition: all 0.3s ease;
|
|
|
z-index: 1000;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
animation: pulse 2s infinite;
|
|
|
}
|
|
|
|
|
|
.chat-button:hover {
|
|
|
transform: scale(1.1);
|
|
|
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.6);
|
|
|
}
|
|
|
|
|
|
.chat-button.hidden {
|
|
|
display: none;
|
|
|
}
|
|
|
|
|
|
@keyframes pulse {
|
|
|
0% {
|
|
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
|
|
}
|
|
|
50% {
|
|
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.8);
|
|
|
}
|
|
|
100% {
|
|
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.chat-popup {
|
|
|
position: fixed;
|
|
|
bottom: 100px;
|
|
|
right: 30px;
|
|
|
width: 400px;
|
|
|
height: 500px;
|
|
|
background: rgba(255, 255, 255, 0.95);
|
|
|
border-radius: 20px;
|
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
|
|
backdrop-filter: blur(10px);
|
|
|
z-index: 1001;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
overflow: hidden;
|
|
|
transform: translateY(20px);
|
|
|
opacity: 0;
|
|
|
transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.chat-popup.open {
|
|
|
transform: translateY(0);
|
|
|
opacity: 1;
|
|
|
}
|
|
|
|
|
|
.chat-header {
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
color: white;
|
|
|
padding: 15px 20px;
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.chat-header h3 {
|
|
|
font-size: 1.1rem;
|
|
|
margin: 0;
|
|
|
}
|
|
|
|
|
|
.chat-header .task-context {
|
|
|
font-size: 0.9rem;
|
|
|
opacity: 0.9;
|
|
|
}
|
|
|
|
|
|
.chat-close {
|
|
|
background: none;
|
|
|
border: none;
|
|
|
color: white;
|
|
|
font-size: 20px;
|
|
|
cursor: pointer;
|
|
|
padding: 5px;
|
|
|
border-radius: 50%;
|
|
|
transition: background 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.chat-close:hover {
|
|
|
background: rgba(255, 255, 255, 0.2);
|
|
|
}
|
|
|
|
|
|
.chat-messages {
|
|
|
flex: 1;
|
|
|
padding: 20px;
|
|
|
overflow-y: auto;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 15px;
|
|
|
}
|
|
|
|
|
|
.chat-message {
|
|
|
max-width: 80%;
|
|
|
padding: 12px 16px;
|
|
|
border-radius: 18px;
|
|
|
line-height: 1.4;
|
|
|
font-size: 0.95rem;
|
|
|
}
|
|
|
|
|
|
.chat-message.ai {
|
|
|
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
|
|
color: #1976d2;
|
|
|
align-self: flex-start;
|
|
|
border-bottom-left-radius: 6px;
|
|
|
}
|
|
|
|
|
|
.chat-message.user {
|
|
|
background: #f0f0f0;
|
|
|
color: #333;
|
|
|
align-self: flex-end;
|
|
|
border-bottom-right-radius: 6px;
|
|
|
}
|
|
|
|
|
|
.chat-timestamp {
|
|
|
font-size: 0.8rem;
|
|
|
opacity: 0.6;
|
|
|
margin-top: 5px;
|
|
|
}
|
|
|
|
|
|
.chat-typing {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 8px;
|
|
|
padding: 12px 16px;
|
|
|
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
|
|
border-radius: 18px;
|
|
|
border-bottom-left-radius: 6px;
|
|
|
align-self: flex-start;
|
|
|
max-width: 80%;
|
|
|
}
|
|
|
|
|
|
.typing-dots {
|
|
|
display: flex;
|
|
|
gap: 4px;
|
|
|
}
|
|
|
|
|
|
.typing-dot {
|
|
|
width: 8px;
|
|
|
height: 8px;
|
|
|
background: #1976d2;
|
|
|
border-radius: 50%;
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.chat-input-container {
|
|
|
padding: 20px;
|
|
|
border-top: 1px solid #e0e0e0;
|
|
|
background: white;
|
|
|
}
|
|
|
|
|
|
.chat-input {
|
|
|
width: 100%;
|
|
|
min-height: 60px;
|
|
|
max-height: 120px;
|
|
|
padding: 12px 50px 12px 15px;
|
|
|
border: 2px solid #e0e0e0;
|
|
|
border-radius: 25px;
|
|
|
resize: none;
|
|
|
font-family: inherit;
|
|
|
font-size: 14px;
|
|
|
outline: none;
|
|
|
transition: border-color 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.chat-input:focus {
|
|
|
border-color: #667eea;
|
|
|
}
|
|
|
|
|
|
.chat-send {
|
|
|
position: absolute;
|
|
|
right: 25px;
|
|
|
bottom: 32px;
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
border: none;
|
|
|
color: white;
|
|
|
width: 35px;
|
|
|
height: 35px;
|
|
|
border-radius: 50%;
|
|
|
cursor: pointer;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.chat-send:hover {
|
|
|
transform: scale(1.1);
|
|
|
}
|
|
|
|
|
|
.chat-send:disabled {
|
|
|
opacity: 0.5;
|
|
|
cursor: not-allowed;
|
|
|
transform: none;
|
|
|
}
|
|
|
|
|
|
.quick-questions {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 8px;
|
|
|
margin-bottom: 15px;
|
|
|
}
|
|
|
|
|
|
.quick-question {
|
|
|
background: rgba(102, 126, 234, 0.1);
|
|
|
color: #667eea;
|
|
|
border: 1px solid rgba(102, 126, 234, 0.3);
|
|
|
padding: 6px 12px;
|
|
|
border-radius: 15px;
|
|
|
font-size: 0.85rem;
|
|
|
cursor: pointer;
|
|
|
transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.quick-question:hover {
|
|
|
background: rgba(102, 126, 234, 0.2);
|
|
|
transform: translateY(-1px);
|
|
|
}
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
.app-container {
|
|
|
padding: 10px;
|
|
|
}
|
|
|
|
|
|
.header h1 {
|
|
|
font-size: 2rem;
|
|
|
}
|
|
|
|
|
|
.role-grid {
|
|
|
grid-template-columns: 1fr;
|
|
|
}
|
|
|
|
|
|
.progress-steps {
|
|
|
flex-direction: column;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
.main-content {
|
|
|
padding: 20px;
|
|
|
}
|
|
|
|
|
|
.category-title {
|
|
|
font-size: 1.3rem;
|
|
|
}
|
|
|
|
|
|
.chat-popup {
|
|
|
width: calc(100vw - 40px);
|
|
|
right: 20px;
|
|
|
left: 20px;
|
|
|
height: 450px;
|
|
|
}
|
|
|
|
|
|
.chat-button {
|
|
|
bottom: 20px;
|
|
|
right: 20px;
|
|
|
}
|
|
|
}
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
<div id="root"></div>
|
|
|
|
|
|
<script type="text/babel">
|
|
|
const { useState, useEffect, useRef } = React;
|
|
|
|
|
|
|
|
|
const AVAILABLE_ROLES = [
|
|
|
|
|
|
{
|
|
|
id: 'scientist',
|
|
|
title: 'Research Scientist',
|
|
|
icon: '🔬',
|
|
|
description: 'Design experiments, analyze data, and make scientific discoveries',
|
|
|
category: 'Science & Research',
|
|
|
tasks: [
|
|
|
'Formulate research hypothesis',
|
|
|
'Design experimental methodology',
|
|
|
'Analyze research data',
|
|
|
'Present findings and conclusions'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'historian',
|
|
|
title: 'Digital Historian',
|
|
|
icon: '📚',
|
|
|
description: 'Curate historical exhibits and analyze historical patterns',
|
|
|
category: 'Education & Research',
|
|
|
tasks: [
|
|
|
'Research historical sources',
|
|
|
'Analyze historical context',
|
|
|
'Create digital exhibition',
|
|
|
'Present historical narrative'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'entrepreneur',
|
|
|
title: 'Social Entrepreneur',
|
|
|
icon: '💡',
|
|
|
description: 'Develop sustainable business solutions for social impact',
|
|
|
category: 'Business & Finance',
|
|
|
tasks: [
|
|
|
'Identify market opportunity',
|
|
|
'Develop business model',
|
|
|
'Create pitch presentation',
|
|
|
'Plan implementation strategy'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'engineer',
|
|
|
title: 'Software Engineer',
|
|
|
icon: '💻',
|
|
|
description: 'Design and develop technological solutions',
|
|
|
category: 'Technology & Engineering',
|
|
|
tasks: [
|
|
|
'Analyze technical requirements',
|
|
|
'Design system architecture',
|
|
|
'Develop prototype solution',
|
|
|
'Test and optimize performance'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'designer',
|
|
|
title: 'UX Designer',
|
|
|
icon: '🎨',
|
|
|
description: 'Create user-centered design solutions',
|
|
|
category: 'Creative & Media',
|
|
|
tasks: [
|
|
|
'Conduct user research',
|
|
|
'Create design wireframes',
|
|
|
'Develop user interface',
|
|
|
'Test usability and iterate'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'journalist',
|
|
|
title: 'Investigative Journalist',
|
|
|
icon: '📰',
|
|
|
description: 'Research and report on important stories',
|
|
|
category: 'Creative & Media',
|
|
|
tasks: [
|
|
|
'Investigate story leads',
|
|
|
'Conduct interviews',
|
|
|
'Verify facts and sources',
|
|
|
'Write compelling article'
|
|
|
]
|
|
|
},
|
|
|
|
|
|
{
|
|
|
id: 'medical_researcher',
|
|
|
title: 'Medical Researcher',
|
|
|
icon: '🧬',
|
|
|
description: 'Conduct clinical research and analyze medical data',
|
|
|
category: 'Healthcare & Life Sciences',
|
|
|
tasks: [
|
|
|
'Design clinical study protocol',
|
|
|
'Collect and analyze patient data',
|
|
|
'Evaluate treatment effectiveness',
|
|
|
'Publish research findings'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'public_health',
|
|
|
title: 'Public Health Analyst',
|
|
|
icon: '🏥',
|
|
|
description: 'Analyze health trends and develop intervention strategies',
|
|
|
category: 'Healthcare & Life Sciences',
|
|
|
tasks: [
|
|
|
'Analyze population health data',
|
|
|
'Identify health risk factors',
|
|
|
'Design intervention programs',
|
|
|
'Evaluate program effectiveness'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'biotech_engineer',
|
|
|
title: 'Biotech Engineer',
|
|
|
icon: '🧪',
|
|
|
description: 'Design biotechnology solutions for medical applications',
|
|
|
category: 'Healthcare & Life Sciences',
|
|
|
tasks: [
|
|
|
'Research biotechnology applications',
|
|
|
'Design biomedical devices',
|
|
|
'Test prototype solutions',
|
|
|
'Optimize for clinical use'
|
|
|
]
|
|
|
},
|
|
|
|
|
|
{
|
|
|
id: 'marketing_strategist',
|
|
|
title: 'Marketing Strategist',
|
|
|
icon: '📈',
|
|
|
description: 'Develop comprehensive marketing campaigns and brand strategies',
|
|
|
category: 'Business & Finance',
|
|
|
tasks: [
|
|
|
'Analyze target market segments',
|
|
|
'Develop marketing strategy',
|
|
|
'Create campaign materials',
|
|
|
'Measure campaign effectiveness'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'financial_analyst',
|
|
|
title: 'Financial Analyst',
|
|
|
icon: '💰',
|
|
|
description: 'Analyze financial data and investment opportunities',
|
|
|
category: 'Business & Finance',
|
|
|
tasks: [
|
|
|
'Analyze financial statements',
|
|
|
'Evaluate investment opportunities',
|
|
|
'Create financial models',
|
|
|
'Present investment recommendations'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'management_consultant',
|
|
|
title: 'Management Consultant',
|
|
|
icon: '🎯',
|
|
|
description: 'Solve complex business problems for organizations',
|
|
|
category: 'Business & Finance',
|
|
|
tasks: [
|
|
|
'Analyze organizational challenges',
|
|
|
'Develop strategic solutions',
|
|
|
'Create implementation plan',
|
|
|
'Present recommendations to leadership'
|
|
|
]
|
|
|
},
|
|
|
|
|
|
{
|
|
|
id: 'game_developer',
|
|
|
title: 'Game Developer',
|
|
|
icon: '🎮',
|
|
|
description: 'Design and develop interactive gaming experiences',
|
|
|
category: 'Creative & Media',
|
|
|
tasks: [
|
|
|
'Design game mechanics',
|
|
|
'Develop game prototype',
|
|
|
'Create user interface',
|
|
|
'Test and refine gameplay'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'content_creator',
|
|
|
title: 'Content Creator',
|
|
|
icon: '📹',
|
|
|
description: 'Produce engaging digital content across platforms',
|
|
|
category: 'Creative & Media',
|
|
|
tasks: [
|
|
|
'Develop content strategy',
|
|
|
'Create multimedia content',
|
|
|
'Optimize for different platforms',
|
|
|
'Analyze audience engagement'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'graphic_designer',
|
|
|
title: 'Graphic Designer',
|
|
|
icon: '🖌️',
|
|
|
description: 'Create visual communication and branding solutions',
|
|
|
category: 'Creative & Media',
|
|
|
tasks: [
|
|
|
'Analyze design requirements',
|
|
|
'Create visual concepts',
|
|
|
'Develop brand materials',
|
|
|
'Present design solutions'
|
|
|
]
|
|
|
},
|
|
|
|
|
|
{
|
|
|
id: 'environmental_scientist',
|
|
|
title: 'Environmental Scientist',
|
|
|
icon: '🌱',
|
|
|
description: 'Study environmental issues and develop solutions',
|
|
|
category: 'Environmental & Sustainability',
|
|
|
tasks: [
|
|
|
'Conduct environmental assessments',
|
|
|
'Analyze pollution data',
|
|
|
'Develop remediation strategies',
|
|
|
'Present environmental recommendations'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'sustainability_consultant',
|
|
|
title: 'Sustainability Consultant',
|
|
|
icon: '♻️',
|
|
|
description: 'Help organizations implement sustainable practices',
|
|
|
category: 'Environmental & Sustainability',
|
|
|
tasks: [
|
|
|
'Assess current sustainability practices',
|
|
|
'Identify improvement opportunities',
|
|
|
'Develop sustainability plan',
|
|
|
'Monitor implementation progress'
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
id: 'urban_planner',
|
|
|
title: 'Urban Planner',
|
|
|
icon: '🏙️',
|
|
|
description: 'Design sustainable and livable urban environments',
|
|
|
category: 'Environmental & Sustainability',
|
|
|
tasks: [
|
|
|
'Analyze urban development needs',
|
|
|
'Design community spaces',
|
|
|
'Create zoning proposals',
|
|
|
'Present planning recommendations'
|
|
|
]
|
|
|
}
|
|
|
];
|
|
|
|
|
|
|
|
|
const INDUSTRY_CATEGORIES = [
|
|
|
'Technology & Engineering',
|
|
|
'Healthcare & Life Sciences',
|
|
|
'Business & Finance',
|
|
|
'Creative & Media',
|
|
|
'Education & Research',
|
|
|
'Environmental & Sustainability',
|
|
|
'Government & Public Service',
|
|
|
'Other'
|
|
|
];
|
|
|
|
|
|
|
|
|
const QUICK_QUESTIONS = [
|
|
|
"How should I approach this task?",
|
|
|
"What are the key points to include?",
|
|
|
"Can you give me an example?",
|
|
|
"What skills should I focus on?",
|
|
|
"How long should my response be?"
|
|
|
];
|
|
|
|
|
|
|
|
|
const groupRolesByCategory = (roles) => {
|
|
|
const grouped = {};
|
|
|
roles.forEach(role => {
|
|
|
if (!grouped[role.category]) {
|
|
|
grouped[role.category] = [];
|
|
|
}
|
|
|
grouped[role.category].push(role);
|
|
|
});
|
|
|
return grouped;
|
|
|
};
|
|
|
|
|
|
|
|
|
const VirtualInternshipSimulator = () => {
|
|
|
const [currentPhase, setCurrentPhase] = useState(1);
|
|
|
const [selectedRole, setSelectedRole] = useState(null);
|
|
|
const [showCustomRoleForm, setShowCustomRoleForm] = useState(false);
|
|
|
const [customRole, setCustomRole] = useState({
|
|
|
title: '',
|
|
|
description: '',
|
|
|
category: '',
|
|
|
skills: '',
|
|
|
tasks: ''
|
|
|
});
|
|
|
const [userProfile, setUserProfile] = useState({
|
|
|
experience: '',
|
|
|
goals: '',
|
|
|
difficulty: 'medium'
|
|
|
});
|
|
|
const [currentTask, setCurrentTask] = useState(0);
|
|
|
const [taskSubmissions, setTaskSubmissions] = useState({});
|
|
|
const [aiApiKey, setAiApiKey] = useState('');
|
|
|
const [aiFeedback, setAiFeedback] = useState(null);
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
const [error, setError] = useState('');
|
|
|
const [badges, setBadges] = useState([]);
|
|
|
const [skillScores, setSkillScores] = useState({});
|
|
|
const [reflection, setReflection] = useState('');
|
|
|
|
|
|
|
|
|
const [chatOpen, setChatOpen] = useState(false);
|
|
|
const [chatMessages, setChatMessages] = useState([]);
|
|
|
const [chatInput, setChatInput] = useState('');
|
|
|
const [chatLoading, setChatLoading] = useState(false);
|
|
|
|
|
|
const chartRef = useRef(null);
|
|
|
const chatMessagesRef = useRef(null);
|
|
|
|
|
|
|
|
|
const phases = [
|
|
|
'Role Selection',
|
|
|
'Task Assignment',
|
|
|
'AI Guidance',
|
|
|
'Evaluation',
|
|
|
'Reflection'
|
|
|
];
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
const savedApiKey = localStorage.getItem('internship_api_key');
|
|
|
if (savedApiKey) {
|
|
|
setAiApiKey(savedApiKey);
|
|
|
}
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
if (aiApiKey) {
|
|
|
localStorage.setItem('internship_api_key', aiApiKey);
|
|
|
}
|
|
|
}, [aiApiKey]);
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
if (currentPhase === 2 && selectedRole && chatMessages.length === 0) {
|
|
|
const welcomeMessage = {
|
|
|
id: Date.now(),
|
|
|
sender: 'ai',
|
|
|
content: `Hi! I'm your AI Task Assistant for the ${selectedRole.title} internship. I'm here to help you understand and complete your tasks effectively. Feel free to ask me anything about your current task or how to approach it!`,
|
|
|
timestamp: new Date(),
|
|
|
taskContext: selectedRole.tasks[currentTask]
|
|
|
};
|
|
|
setChatMessages([welcomeMessage]);
|
|
|
}
|
|
|
}, [currentPhase, selectedRole, currentTask]);
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
if (chatMessagesRef.current) {
|
|
|
chatMessagesRef.current.scrollTop = chatMessagesRef.current.scrollHeight;
|
|
|
}
|
|
|
}, [chatMessages, chatLoading]);
|
|
|
|
|
|
|
|
|
const callOpenAI = async (messages, maxTokens = 800) => {
|
|
|
if (!aiApiKey) {
|
|
|
throw new Error('Please provide your OpenAI API key');
|
|
|
}
|
|
|
|
|
|
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
'Authorization': `Bearer ${aiApiKey}`
|
|
|
},
|
|
|
body: JSON.stringify({
|
|
|
model: 'gpt-4o-mini',
|
|
|
messages: messages,
|
|
|
max_tokens: maxTokens,
|
|
|
temperature: 0.7
|
|
|
})
|
|
|
});
|
|
|
|
|
|
if (!response.ok) {
|
|
|
const errorData = await response.json();
|
|
|
throw new Error(errorData.error?.message || 'API call failed');
|
|
|
}
|
|
|
|
|
|
const data = await response.json();
|
|
|
return data.choices[0].message.content;
|
|
|
};
|
|
|
|
|
|
|
|
|
const sendChatMessage = async (message) => {
|
|
|
if (!message.trim() || chatLoading) return;
|
|
|
|
|
|
const userMessage = {
|
|
|
id: Date.now(),
|
|
|
sender: 'user',
|
|
|
content: message,
|
|
|
timestamp: new Date(),
|
|
|
taskContext: selectedRole.tasks[currentTask]
|
|
|
};
|
|
|
|
|
|
setChatMessages(prev => [...prev, userMessage]);
|
|
|
setChatInput('');
|
|
|
setChatLoading(true);
|
|
|
|
|
|
try {
|
|
|
const roleInfo = selectedRole.isCustom ?
|
|
|
`Custom Role: ${selectedRole.title} - ${selectedRole.description}` :
|
|
|
`${selectedRole.title} - ${selectedRole.description}`;
|
|
|
|
|
|
const systemPrompt = `You are a helpful AI assistant for a virtual internship program. You're helping a student with their ${roleInfo} internship.
|
|
|
|
|
|
Current Task: ${selectedRole.tasks[currentTask]}
|
|
|
Student Profile: Experience: ${userProfile.experience}, Goals: ${userProfile.goals}, Difficulty: ${userProfile.difficulty}
|
|
|
|
|
|
Provide helpful, encouraging guidance about the current task. Keep responses concise (under 200 words), practical, and supportive. Focus on:
|
|
|
1. Understanding what the task requires
|
|
|
2. Practical steps to approach it
|
|
|
3. Key points to include in their response
|
|
|
4. Encouragement and motivation
|
|
|
|
|
|
Be friendly, professional, and educational.`;
|
|
|
|
|
|
const messages = [
|
|
|
{ role: 'system', content: systemPrompt },
|
|
|
{ role: 'user', content: message }
|
|
|
];
|
|
|
|
|
|
const aiResponse = await callOpenAI(messages, 300);
|
|
|
|
|
|
const aiMessage = {
|
|
|
id: Date.now() + 1,
|
|
|
sender: 'ai',
|
|
|
content: aiResponse,
|
|
|
timestamp: new Date(),
|
|
|
taskContext: selectedRole.tasks[currentTask]
|
|
|
};
|
|
|
|
|
|
setChatMessages(prev => [...prev, aiMessage]);
|
|
|
} catch (err) {
|
|
|
const errorMessage = {
|
|
|
id: Date.now() + 1,
|
|
|
sender: 'ai',
|
|
|
content: `Sorry, I'm having trouble connecting right now. Please make sure your API key is correct and try again. In the meantime, focus on understanding what the task is asking and break it down into smaller steps.`,
|
|
|
timestamp: new Date(),
|
|
|
taskContext: selectedRole.tasks[currentTask]
|
|
|
};
|
|
|
setChatMessages(prev => [...prev, errorMessage]);
|
|
|
} finally {
|
|
|
setChatLoading(false);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
const handleQuickQuestion = (question) => {
|
|
|
sendChatMessage(question);
|
|
|
};
|
|
|
|
|
|
|
|
|
const createCustomRole = () => {
|
|
|
if (!customRole.title || !customRole.description || !customRole.category) {
|
|
|
setError('Please fill in all required fields for your custom role');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const newRole = {
|
|
|
id: 'custom_' + Date.now(),
|
|
|
title: customRole.title,
|
|
|
icon: '⭐',
|
|
|
description: customRole.description,
|
|
|
category: customRole.category,
|
|
|
isCustom: true,
|
|
|
tasks: customRole.tasks ?
|
|
|
customRole.tasks.split('\n').filter(task => task.trim()) :
|
|
|
[
|
|
|
'Analyze project requirements',
|
|
|
'Develop strategic approach',
|
|
|
'Implement solution',
|
|
|
'Evaluate and present results'
|
|
|
]
|
|
|
};
|
|
|
|
|
|
setSelectedRole(newRole);
|
|
|
setShowCustomRoleForm(false);
|
|
|
setError('');
|
|
|
};
|
|
|
|
|
|
|
|
|
const generateAIGuidance = async (taskIndex, submission) => {
|
|
|
setLoading(true);
|
|
|
setError('');
|
|
|
|
|
|
try {
|
|
|
const roleInfo = selectedRole.isCustom ?
|
|
|
`Custom Role: ${selectedRole.title} - ${selectedRole.description}` :
|
|
|
`${selectedRole.title} - ${selectedRole.description}`;
|
|
|
|
|
|
const messages = [
|
|
|
{
|
|
|
role: 'system',
|
|
|
content: `You are an expert internship mentor for ${roleInfo}. Provide encouraging, constructive feedback and practical next steps for students. Focus on professional development and real-world application.`
|
|
|
},
|
|
|
{
|
|
|
role: 'user',
|
|
|
content: `Student Profile:
|
|
|
- Experience: ${userProfile.experience}
|
|
|
- Goals: ${userProfile.goals}
|
|
|
- Difficulty Level: ${userProfile.difficulty}
|
|
|
|
|
|
Current Task: ${selectedRole.tasks[taskIndex]}
|
|
|
Student Submission: ${submission}
|
|
|
|
|
|
Please provide:
|
|
|
1. Specific feedback on their submission
|
|
|
2. Practical suggestions for improvement
|
|
|
3. Next steps for this task
|
|
|
4. Encouragement and motivation
|
|
|
|
|
|
Keep response professional but encouraging, around 200-300 words.`
|
|
|
}
|
|
|
];
|
|
|
|
|
|
const feedback = await callOpenAI(messages, 400);
|
|
|
setAiFeedback(feedback);
|
|
|
} catch (err) {
|
|
|
setError(`AI guidance failed: ${err.message}`);
|
|
|
} finally {
|
|
|
setLoading(false);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
const generateEvaluation = async () => {
|
|
|
setLoading(true);
|
|
|
setError('');
|
|
|
|
|
|
try {
|
|
|
const roleInfo = selectedRole.isCustom ?
|
|
|
`Custom Role: ${selectedRole.title} - ${selectedRole.description}` :
|
|
|
`${selectedRole.title} - ${selectedRole.description}`;
|
|
|
|
|
|
const allSubmissions = Object.values(taskSubmissions).join('\n\n');
|
|
|
|
|
|
const messages = [
|
|
|
{
|
|
|
role: 'system',
|
|
|
content: `You are a professional evaluator for ${roleInfo} internships. Assess student performance and provide skill scores (1-10) and constructive feedback.`
|
|
|
},
|
|
|
{
|
|
|
role: 'user',
|
|
|
content: `Student Profile:
|
|
|
- Experience: ${userProfile.experience}
|
|
|
- Goals: ${userProfile.goals}
|
|
|
- Difficulty Level: ${userProfile.difficulty}
|
|
|
|
|
|
Role: ${roleInfo}
|
|
|
|
|
|
All Task Submissions:
|
|
|
${allSubmissions}
|
|
|
|
|
|
Please provide:
|
|
|
1. Overall performance assessment
|
|
|
2. Skill scores (1-10) for: Problem Solving, Communication, Technical Skills, Creativity, Leadership
|
|
|
3. Specific achievements and strengths
|
|
|
4. Areas for improvement
|
|
|
5. Recommended next steps for professional development
|
|
|
|
|
|
Format skill scores as: Problem Solving: X, Communication: X, Technical Skills: X, Creativity: X, Leadership: X`
|
|
|
}
|
|
|
];
|
|
|
|
|
|
const evaluation = await callOpenAI(messages, 600);
|
|
|
|
|
|
|
|
|
const skillRegex = /Problem Solving:\s*(\d+).*Communication:\s*(\d+).*Technical Skills:\s*(\d+).*Creativity:\s*(\d+).*Leadership:\s*(\d+)/i;
|
|
|
const match = evaluation.match(skillRegex);
|
|
|
|
|
|
if (match) {
|
|
|
setSkillScores({
|
|
|
'Problem Solving': parseInt(match[1]),
|
|
|
'Communication': parseInt(match[2]),
|
|
|
'Technical Skills': parseInt(match[3]),
|
|
|
'Creativity': parseInt(match[4]),
|
|
|
'Leadership': parseInt(match[5])
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
const newBadges = [];
|
|
|
if (Object.keys(taskSubmissions).length === selectedRole.tasks.length) {
|
|
|
newBadges.push('🏆 Internship Completed');
|
|
|
}
|
|
|
if (aiFeedback) {
|
|
|
newBadges.push('🤖 AI Mentorship');
|
|
|
}
|
|
|
if (selectedRole.isCustom) {
|
|
|
newBadges.push('⭐ Custom Role Pioneer');
|
|
|
}
|
|
|
if (chatMessages.length > 2) {
|
|
|
newBadges.push('💬 AI Chat Expert');
|
|
|
}
|
|
|
setBadges(newBadges);
|
|
|
|
|
|
setAiFeedback(evaluation);
|
|
|
} catch (err) {
|
|
|
setError(`Evaluation failed: ${err.message}`);
|
|
|
} finally {
|
|
|
setLoading(false);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
const generateReflectionFeedback = async () => {
|
|
|
setLoading(true);
|
|
|
setError('');
|
|
|
|
|
|
try {
|
|
|
const roleInfo = selectedRole.isCustom ?
|
|
|
`Custom Role: ${selectedRole.title} - ${selectedRole.description}` :
|
|
|
`${selectedRole.title} - ${selectedRole.description}`;
|
|
|
|
|
|
const messages = [
|
|
|
{
|
|
|
role: 'system',
|
|
|
content: `You are a career development coach specializing in ${roleInfo}. Provide encouraging feedback on student reflection and suggest concrete next steps for professional growth.`
|
|
|
},
|
|
|
{
|
|
|
role: 'user',
|
|
|
content: `Student completed a virtual internship in: ${roleInfo}
|
|
|
|
|
|
Their reflection: ${reflection}
|
|
|
|
|
|
Please provide:
|
|
|
1. Validation of their insights and growth
|
|
|
2. Additional perspectives they might consider
|
|
|
3. Specific next steps for continued development
|
|
|
4. Resources or opportunities to explore
|
|
|
5. Encouragement for their career journey
|
|
|
|
|
|
Keep response supportive and actionable, around 250-300 words.`
|
|
|
}
|
|
|
];
|
|
|
|
|
|
const feedback = await callOpenAI(messages, 400);
|
|
|
setAiFeedback(feedback);
|
|
|
} catch (err) {
|
|
|
setError(`Reflection feedback failed: ${err.message}`);
|
|
|
} finally {
|
|
|
setLoading(false);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
if (Object.keys(skillScores).length > 0 && chartRef.current) {
|
|
|
const ctx = chartRef.current.getContext('2d');
|
|
|
|
|
|
|
|
|
if (chartRef.current.chart) {
|
|
|
chartRef.current.chart.destroy();
|
|
|
}
|
|
|
|
|
|
chartRef.current.chart = new Chart(ctx, {
|
|
|
type: 'radar',
|
|
|
data: {
|
|
|
labels: Object.keys(skillScores),
|
|
|
datasets: [{
|
|
|
label: 'Skill Level',
|
|
|
data: Object.values(skillScores),
|
|
|
backgroundColor: 'rgba(102, 126, 234, 0.2)',
|
|
|
borderColor: 'rgba(102, 126, 234, 1)',
|
|
|
borderWidth: 2,
|
|
|
pointBackgroundColor: 'rgba(102, 126, 234, 1)',
|
|
|
pointBorderColor: '#fff',
|
|
|
pointHoverBackgroundColor: '#fff',
|
|
|
pointHoverBorderColor: 'rgba(102, 126, 234, 1)'
|
|
|
}]
|
|
|
},
|
|
|
options: {
|
|
|
responsive: true,
|
|
|
scales: {
|
|
|
r: {
|
|
|
beginAtZero: true,
|
|
|
max: 10,
|
|
|
ticks: {
|
|
|
stepSize: 2
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
plugins: {
|
|
|
legend: {
|
|
|
display: false
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}, [skillScores]);
|
|
|
|
|
|
|
|
|
const renderChatbot = () => {
|
|
|
if (currentPhase !== 2) return null;
|
|
|
|
|
|
return (
|
|
|
<>
|
|
|
<button
|
|
|
className={`chat-button ${chatOpen ? 'hidden' : ''}`}
|
|
|
onClick={() => setChatOpen(true)}
|
|
|
title="Get AI help with your task"
|
|
|
>
|
|
|
💬
|
|
|
</button>
|
|
|
|
|
|
<div className={`chat-popup ${chatOpen ? 'open' : ''}`}>
|
|
|
<div className="chat-header">
|
|
|
<div>
|
|
|
<h3>🤖 AI Task Assistant</h3>
|
|
|
<div className="task-context">
|
|
|
{selectedRole?.title} - Task {currentTask + 1}
|
|
|
</div>
|
|
|
</div>
|
|
|
<button
|
|
|
className="chat-close"
|
|
|
onClick={() => setChatOpen(false)}
|
|
|
>
|
|
|
×
|
|
|
</button>
|
|
|
</div>
|
|
|
|
|
|
<div className="chat-messages" ref={chatMessagesRef}>
|
|
|
{chatMessages.map(message => (
|
|
|
<div key={message.id} className={`chat-message ${message.sender}`}>
|
|
|
<div>{message.content}</div>
|
|
|
<div className="chat-timestamp">
|
|
|
{message.timestamp.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
|
|
|
</div>
|
|
|
</div>
|
|
|
))}
|
|
|
|
|
|
{chatLoading && (
|
|
|
<div className="chat-typing">
|
|
|
<span>AI is typing</span>
|
|
|
<div className="typing-dots">
|
|
|
<div className="typing-dot"></div>
|
|
|
<div className="typing-dot"></div>
|
|
|
<div className="typing-dot"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
{chatMessages.length <= 1 && (
|
|
|
<div className="quick-questions">
|
|
|
{QUICK_QUESTIONS.map((question, index) => (
|
|
|
<button
|
|
|
key={index}
|
|
|
className="quick-question"
|
|
|
onClick={() => handleQuickQuestion(question)}
|
|
|
>
|
|
|
{question}
|
|
|
</button>
|
|
|
))}
|
|
|
</div>
|
|
|
)}
|
|
|
</div>
|
|
|
|
|
|
<div className="chat-input-container">
|
|
|
<textarea
|
|
|
className="chat-input"
|
|
|
value={chatInput}
|
|
|
onChange={(e) => setChatInput(e.target.value)}
|
|
|
placeholder="Ask me anything about this task..."
|
|
|
onKeyPress={(e) => {
|
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
|
e.preventDefault();
|
|
|
sendChatMessage(chatInput);
|
|
|
}
|
|
|
}}
|
|
|
maxLength={500}
|
|
|
/>
|
|
|
<button
|
|
|
className="chat-send"
|
|
|
onClick={() => sendChatMessage(chatInput)}
|
|
|
disabled={!chatInput.trim() || chatLoading}
|
|
|
>
|
|
|
➤
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
|
|
|
const renderRoleSelection = () => {
|
|
|
const groupedRoles = groupRolesByCategory(AVAILABLE_ROLES);
|
|
|
|
|
|
return (
|
|
|
<div>
|
|
|
<h2 style={{marginBottom: '30px', textAlign: 'center', color: '#333'}}>
|
|
|
Choose Your Virtual Internship Role
|
|
|
</h2>
|
|
|
|
|
|
{Object.entries(groupedRoles).map(([category, roles]) => (
|
|
|
<div key={category} className="category-section">
|
|
|
<div className="category-title">
|
|
|
<span>{category}</span>
|
|
|
</div>
|
|
|
<div className="role-grid">
|
|
|
{roles.map(role => (
|
|
|
<div
|
|
|
key={role.id}
|
|
|
className={`role-card ${selectedRole?.id === role.id ? 'selected' : ''}`}
|
|
|
onClick={() => setSelectedRole(role)}
|
|
|
>
|
|
|
<span className="role-icon">{role.icon}</span>
|
|
|
<div className="role-title">{role.title}</div>
|
|
|
<div className="role-description">{role.description}</div>
|
|
|
</div>
|
|
|
))}
|
|
|
</div>
|
|
|
</div>
|
|
|
))}
|
|
|
|
|
|
{/* Custom Role Option */}
|
|
|
<div className="category-section">
|
|
|
<div className="category-title">
|
|
|
<span>Custom Role</span>
|
|
|
</div>
|
|
|
<div className="role-grid">
|
|
|
<div
|
|
|
className="role-card custom-role"
|
|
|
onClick={() => setShowCustomRoleForm(!showCustomRoleForm)}
|
|
|
>
|
|
|
<span className="role-icon">⭐</span>
|
|
|
<div className="role-title">Create Your Own Role</div>
|
|
|
<div className="role-description">
|
|
|
Don't see your dream internship? Create a custom role tailored to your interests and career goals.
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
{/* Custom Role Form */}
|
|
|
{showCustomRoleForm && (
|
|
|
<div className="custom-role-form">
|
|
|
<h3>Create Your Custom Internship Role</h3>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Role Title *</label>
|
|
|
<input
|
|
|
type="text"
|
|
|
value={customRole.title}
|
|
|
onChange={(e) => setCustomRole({...customRole, title: e.target.value})}
|
|
|
placeholder="e.g., Space Mission Planner, Food Scientist, etc."
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Role Description *</label>
|
|
|
<textarea
|
|
|
value={customRole.description}
|
|
|
onChange={(e) => setCustomRole({...customRole, description: e.target.value})}
|
|
|
placeholder="Describe what this role involves and what you'd like to learn..."
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Industry Category *</label>
|
|
|
<select
|
|
|
value={customRole.category}
|
|
|
onChange={(e) => setCustomRole({...customRole, category: e.target.value})}
|
|
|
>
|
|
|
<option value="">Select a category</option>
|
|
|
{INDUSTRY_CATEGORIES.map(category => (
|
|
|
<option key={category} value={category}>{category}</option>
|
|
|
))}
|
|
|
</select>
|
|
|
</div>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Key Skills Focus (Optional)</label>
|
|
|
<input
|
|
|
type="text"
|
|
|
value={customRole.skills}
|
|
|
onChange={(e) => setCustomRole({...customRole, skills: e.target.value})}
|
|
|
placeholder="e.g., Data Analysis, Creative Problem Solving, etc."
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Typical Tasks (Optional - one per line)</label>
|
|
|
<textarea
|
|
|
value={customRole.tasks}
|
|
|
onChange={(e) => setCustomRole({...customRole, tasks: e.target.value})}
|
|
|
placeholder="Research market trends Develop prototype Test with users Present findings"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<button className="btn" onClick={createCustomRole}>
|
|
|
Create Custom Role
|
|
|
</button>
|
|
|
<button
|
|
|
className="btn btn-secondary"
|
|
|
onClick={() => setShowCustomRoleForm(false)}
|
|
|
>
|
|
|
Cancel
|
|
|
</button>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
{/* Profile Setup */}
|
|
|
{selectedRole && (
|
|
|
<div style={{marginTop: '40px', padding: '30px', background: '#f8f9fa', borderRadius: '15px'}}>
|
|
|
<h3 style={{marginBottom: '20px', color: '#333'}}>
|
|
|
Complete Your Profile for {selectedRole.title}
|
|
|
</h3>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Your Experience Background</label>
|
|
|
<textarea
|
|
|
value={userProfile.experience}
|
|
|
onChange={(e) => setUserProfile({...userProfile, experience: e.target.value})}
|
|
|
placeholder="Describe your relevant experience, skills, or interests..."
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Learning Goals</label>
|
|
|
<textarea
|
|
|
value={userProfile.goals}
|
|
|
onChange={(e) => setUserProfile({...userProfile, goals: e.target.value})}
|
|
|
placeholder="What do you hope to learn or achieve in this internship?"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Difficulty Level</label>
|
|
|
<select
|
|
|
value={userProfile.difficulty}
|
|
|
onChange={(e) => setUserProfile({...userProfile, difficulty: e.target.value})}
|
|
|
>
|
|
|
<option value="easy">Easy - Beginner friendly</option>
|
|
|
<option value="medium">Medium - Some challenge</option>
|
|
|
<option value="hard">Hard - Advanced level</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>OpenAI API Key (for AI mentorship)</label>
|
|
|
<input
|
|
|
type="password"
|
|
|
value={aiApiKey}
|
|
|
onChange={(e) => setAiApiKey(e.target.value)}
|
|
|
placeholder="Enter your OpenAI API key for personalized AI guidance"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<button
|
|
|
className="btn"
|
|
|
onClick={() => setCurrentPhase(2)}
|
|
|
disabled={!userProfile.experience || !userProfile.goals || !aiApiKey}
|
|
|
>
|
|
|
Start Internship
|
|
|
</button>
|
|
|
</div>
|
|
|
)}
|
|
|
</div>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
|
|
|
const renderTaskAssignment = () => (
|
|
|
<div>
|
|
|
<h2 style={{marginBottom: '30px', textAlign: 'center', color: '#333'}}>
|
|
|
{selectedRole.title} - Task Dashboard
|
|
|
<div style={{fontSize: '1rem', fontWeight: 'normal', marginTop: '10px', color: '#666'}}>
|
|
|
💬 Click the chat button for AI assistance with any task!
|
|
|
</div>
|
|
|
</h2>
|
|
|
|
|
|
<div className="task-dashboard">
|
|
|
{selectedRole.tasks.map((task, index) => (
|
|
|
<div
|
|
|
key={index}
|
|
|
className={`task-card ${
|
|
|
index === currentTask ? 'active' :
|
|
|
taskSubmissions[index] ? 'completed' : ''
|
|
|
}`}
|
|
|
>
|
|
|
<div className="task-header">
|
|
|
<div style={{display: 'flex', alignItems: 'center'}}>
|
|
|
<div className="task-number">{index + 1}</div>
|
|
|
<div className="task-title">{task}</div>
|
|
|
</div>
|
|
|
<div className={`task-status ${
|
|
|
index === currentTask ? 'status-active' :
|
|
|
taskSubmissions[index] ? 'status-completed' : 'status-pending'
|
|
|
}`}>
|
|
|
{index === currentTask ? 'Active' :
|
|
|
taskSubmissions[index] ? 'Completed' : 'Pending'}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
{index === currentTask && (
|
|
|
<div style={{marginTop: '20px'}}>
|
|
|
<div className="form-group">
|
|
|
<label>Your Response</label>
|
|
|
<textarea
|
|
|
value={taskSubmissions[index] || ''}
|
|
|
onChange={(e) => setTaskSubmissions({
|
|
|
...taskSubmissions,
|
|
|
[index]: e.target.value
|
|
|
})}
|
|
|
placeholder="Describe your approach, findings, or solution for this task..."
|
|
|
rows="4"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<button
|
|
|
className="btn"
|
|
|
onClick={() => {
|
|
|
if (taskSubmissions[index]) {
|
|
|
generateAIGuidance(index, taskSubmissions[index]);
|
|
|
setCurrentPhase(3);
|
|
|
}
|
|
|
}}
|
|
|
disabled={!taskSubmissions[index] || loading}
|
|
|
>
|
|
|
{loading ? 'Getting AI Guidance...' : 'Submit & Get AI Guidance'}
|
|
|
</button>
|
|
|
</div>
|
|
|
)}
|
|
|
</div>
|
|
|
))}
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|
|
|
|
|
|
|
|
|
const renderAIGuidance = () => (
|
|
|
<div>
|
|
|
<h2 style={{marginBottom: '30px', textAlign: 'center', color: '#333'}}>
|
|
|
AI Mentorship & Guidance
|
|
|
</h2>
|
|
|
|
|
|
{loading && (
|
|
|
<div style={{textAlign: 'center'}}>
|
|
|
<div className="loading-spinner"></div>
|
|
|
<p>AI mentor is analyzing your submission...</p>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
{aiFeedback && (
|
|
|
<div className="ai-feedback">
|
|
|
<h4>🤖 Your AI Mentor Says:</h4>
|
|
|
<div style={{whiteSpace: 'pre-line'}}>{aiFeedback}</div>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
<div style={{marginTop: '30px', textAlign: 'center'}}>
|
|
|
<button
|
|
|
className="btn"
|
|
|
onClick={() => {
|
|
|
if (currentTask < selectedRole.tasks.length - 1) {
|
|
|
setCurrentTask(currentTask + 1);
|
|
|
setCurrentPhase(2);
|
|
|
setAiFeedback(null);
|
|
|
} else {
|
|
|
generateEvaluation();
|
|
|
setCurrentPhase(4);
|
|
|
}
|
|
|
}}
|
|
|
disabled={loading}
|
|
|
>
|
|
|
{currentTask < selectedRole.tasks.length - 1 ? 'Next Task' : 'Complete Internship'}
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|
|
|
|
|
|
|
|
|
const renderEvaluation = () => (
|
|
|
<div>
|
|
|
<h2 style={{marginBottom: '30px', textAlign: 'center', color: '#333'}}>
|
|
|
Internship Evaluation & Results
|
|
|
</h2>
|
|
|
|
|
|
{badges.length > 0 && (
|
|
|
<div className="badges-container">
|
|
|
<h4 style={{width: '100%', marginBottom: '15px', color: '#333'}}>Achievements Earned:</h4>
|
|
|
{badges.map((badge, index) => (
|
|
|
<div key={index} className="badge">{badge}</div>
|
|
|
))}
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
{Object.keys(skillScores).length > 0 && (
|
|
|
<div className="skill-radar">
|
|
|
<h4 style={{marginBottom: '20px', color: '#333', textAlign: 'center'}}>
|
|
|
Professional Skill Assessment
|
|
|
</h4>
|
|
|
<canvas ref={chartRef} width="400" height="400"></canvas>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
{loading && (
|
|
|
<div style={{textAlign: 'center'}}>
|
|
|
<div className="loading-spinner"></div>
|
|
|
<p>AI is evaluating your complete internship performance...</p>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
{aiFeedback && (
|
|
|
<div className="ai-feedback">
|
|
|
<h4>📊 Complete Performance Evaluation:</h4>
|
|
|
<div style={{whiteSpace: 'pre-line'}}>{aiFeedback}</div>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
<div style={{marginTop: '30px', textAlign: 'center'}}>
|
|
|
<button
|
|
|
className="btn"
|
|
|
onClick={() => setCurrentPhase(5)}
|
|
|
disabled={loading}
|
|
|
>
|
|
|
Continue to Reflection
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|
|
|
|
|
|
|
|
|
const renderReflection = () => (
|
|
|
<div>
|
|
|
<h2 style={{marginBottom: '30px', textAlign: 'center', color: '#333'}}>
|
|
|
Reflection & Future Planning
|
|
|
</h2>
|
|
|
|
|
|
<div className="form-group">
|
|
|
<label>Reflect on Your Virtual Internship Experience</label>
|
|
|
<textarea
|
|
|
value={reflection}
|
|
|
onChange={(e) => setReflection(e.target.value)}
|
|
|
placeholder="What did you learn? How did this experience change your perspective on this career? What are your next steps?"
|
|
|
rows="6"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
<button
|
|
|
className="btn"
|
|
|
onClick={generateReflectionFeedback}
|
|
|
disabled={!reflection || loading}
|
|
|
>
|
|
|
{loading ? 'Getting Feedback...' : 'Get AI Career Guidance'}
|
|
|
</button>
|
|
|
|
|
|
{loading && (
|
|
|
<div style={{textAlign: 'center', marginTop: '20px'}}>
|
|
|
<div className="loading-spinner"></div>
|
|
|
<p>AI career coach is reviewing your reflection...</p>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
{aiFeedback && (
|
|
|
<div className="ai-feedback">
|
|
|
<h4>🎯 Your Career Development Coach Says:</h4>
|
|
|
<div style={{whiteSpace: 'pre-line'}}>{aiFeedback}</div>
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
<div style={{marginTop: '30px', textAlign: 'center'}}>
|
|
|
<button
|
|
|
className="btn"
|
|
|
onClick={() => {
|
|
|
// Reset for new internship
|
|
|
setCurrentPhase(1);
|
|
|
setSelectedRole(null);
|
|
|
setCurrentTask(0);
|
|
|
setTaskSubmissions({});
|
|
|
setAiFeedback(null);
|
|
|
setBadges([]);
|
|
|
setSkillScores({});
|
|
|
setReflection('');
|
|
|
setShowCustomRoleForm(false);
|
|
|
setCustomRole({title: '', description: '', category: '', skills: '', tasks: ''});
|
|
|
setChatMessages([]);
|
|
|
setChatOpen(false);
|
|
|
}}
|
|
|
>
|
|
|
Start New Internship
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|
|
|
|
|
|
return (
|
|
|
<div className="app-container">
|
|
|
<div className="header">
|
|
|
<h1>🎓Virtual Internship Simulator</h1>
|
|
|
<p>Explore diverse careers through AI-powered realistic internship experiences. By Shift Mind AI Labs</p>
|
|
|
</div>
|
|
|
|
|
|
<div className="progress-bar">
|
|
|
<div className="progress-steps">
|
|
|
{phases.map((phase, index) => (
|
|
|
<div
|
|
|
key={index}
|
|
|
className={`progress-step ${
|
|
|
index + 1 === currentPhase ? 'active' :
|
|
|
index + 1 < currentPhase ? 'completed' : ''
|
|
|
}`}
|
|
|
>
|
|
|
{index + 1}. {phase}
|
|
|
</div>
|
|
|
))}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div className="main-content">
|
|
|
{error && (
|
|
|
<div className="error-message">
|
|
|
{error}
|
|
|
</div>
|
|
|
)}
|
|
|
|
|
|
{currentPhase === 1 && renderRoleSelection()}
|
|
|
{currentPhase === 2 && renderTaskAssignment()}
|
|
|
{currentPhase === 3 && renderAIGuidance()}
|
|
|
{currentPhase === 4 && renderEvaluation()}
|
|
|
{currentPhase === 5 && renderReflection()}
|
|
|
</div>
|
|
|
|
|
|
{renderChatbot()}
|
|
|
</div>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
|
|
|
ReactDOM.render(<VirtualInternshipSimulator />, document.getElementById('root'));
|
|
|
</script>
|
|
|
</body>
|
|
|
</html>
|
|
|
|
|
|
|