Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ RUN npm ci
# Copy all files
COPY . .

# Copy .env file if it exists
COPY .env* ./

# Print environment for debugging (will be removed in production)
RUN if [ -f .env ]; then cat .env; fi

# Build the app
RUN npm run build

Expand Down
13 changes: 12 additions & 1 deletion cloudbuild.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
steps:
# Build the container image
# Access secrets and create a .env file for the build
- name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
gcloud secrets versions access latest --secret=GOOGLE_CLIENT_ID > /workspace/google_client_id.txt
echo "VITE_GOOGLE_CLIENT_ID=$(cat /workspace/google_client_id.txt)" > /workspace/.env
echo "VITE_API_URL=${_VITE_API_URL}" >> /workspace/.env
cat /workspace/.env

# Build the container image with access to the .env file
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', '${_REGION}-docker.pkg.dev/${PROJECT_ID}/${_REPOSITORY}/${_IMAGE_NAME}', '.']
env:
Expand Down
4 changes: 3 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MindGarden</title>
<!-- Environment configuration and debugging -->
<script src="/env-check.js"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
</html>
29 changes: 29 additions & 0 deletions public/env-check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// This script runs in the browser and logs environment variable status
console.log('Environment Check Script Running');
console.log('VITE_GOOGLE_CLIENT_ID available:', !!window.ENV_CONFIG?.GOOGLE_CLIENT_ID || 'Not available through window.ENV_CONFIG');

// Create a global object to store runtime environment information
window.ENV_DEBUG = {
checkTime: new Date().toISOString(),
checkEnvironment: function() {
console.log('=== Environment Debug Information ===');
console.log('Check Time:', this.checkTime);
console.log('window.ENV_CONFIG:', window.ENV_CONFIG || 'Not defined');

// This will be replaced at build time by Vite
const viteBuildTimeValue = '__VITE_GOOGLE_CLIENT_ID_PLACEHOLDER__';
console.log('Vite Build-time Client ID available:',
viteBuildTimeValue !== '__VITE_GOOGLE_CLIENT_ID_PLACEHOLDER__' ? 'Yes' : 'No');

return {
checkTime: this.checkTime,
hasEnvConfig: !!window.ENV_CONFIG,
hasBuildTimeValue: viteBuildTimeValue !== '__VITE_GOOGLE_CLIENT_ID_PLACEHOLDER__'
};
}
};

// Run the check automatically
document.addEventListener('DOMContentLoaded', () => {
window.ENV_DEBUG.checkEnvironment();
});
46 changes: 30 additions & 16 deletions src/components/ChatInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,45 @@ interface Message {
}

const ChatInterface: React.FC = () => {
const { user, isAuthenticated, logout } = useAuth()
const { user, isAuthenticated } = useAuth()

// Create welcome message based on authentication status
const welcomeMessage = user?.name
? `Hello ${user.name}! I'm here to support you. How are you feeling today?`
: 'Hello! I\'m here to support you. How are you feeling today?'
// Log authentication state for debugging purposes
useEffect(() => {
console.log('Auth state in ChatInterface:', { isAuthenticated, user })
}, [isAuthenticated, user])

// Function to get welcome message based on current auth state
const getWelcomeMessage = () => {
return user?.name
? `Hello ${user.name}! I'm here to support you. How are you feeling today?`
: 'Hello! I\'m here to support you. How are you feeling today?'
}

// Initialize messages with welcome message
const [messages, setMessages] = useState<Message[]>([
{
id: '1',
text: welcomeMessage,
text: getWelcomeMessage(),
type: 'ai',
timestamp: new Date()
}
])

// Update first message when auth state changes
useEffect(() => {
setMessages(prevMessages => {
// Create a copy of the messages array
const updatedMessages = [...prevMessages]
// Update the first message if it exists
if (updatedMessages.length > 0) {
updatedMessages[0] = {
...updatedMessages[0],
text: getWelcomeMessage()
}
}
return updatedMessages
})
}, [user, isAuthenticated])
const [inputText, setInputText] = useState('')
const [isTyping, setIsTyping] = useState(false)
const messagesEndRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -149,16 +173,6 @@ const ChatInterface: React.FC = () => {
<div className="bg-white/90 backdrop-blur-sm border-b border-gray-200 p-6 rounded-t-2xl">
<div className="flex justify-between items-center mb-2">
<h1 className="text-2xl font-bold text-gray-800">You're not alone</h1>
{isAuthenticated && (
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={logout}
className="px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg text-sm font-medium transition-colors"
>
Logout
</motion.button>
)}
</div>
<p className="text-md text-gray-600 italic">
We're here to support and guide you through anything.
Expand Down
180 changes: 147 additions & 33 deletions src/components/TopNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,80 @@
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { motion, AnimatePresence } from 'framer-motion'
import { useAuth } from '../contexts/AuthContext'

// Define types for navigation items
interface BaseNavItem {
path: string
label: string
icon?: string
isEmergency?: boolean
}

interface NavItemWithAction extends BaseNavItem {
onClick?: (e: React.MouseEvent) => void
disabled?: boolean
}

type NavItem = BaseNavItem | NavItemWithAction

const TopNavigation: React.FC = () => {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
const location = useLocation()
const { user, isAuthenticated, logout, isLoading } = useAuth()

// State to track authentication status for UI rendering
const [authState, setAuthState] = useState({
isUserAuthenticated: false,
userName: ''
})

// Effect to update local state when authentication changes
useEffect(() => {
console.log('Auth state changed in TopNavigation:', { isAuthenticated, user })
setAuthState({
isUserAuthenticated: isAuthenticated === true,
userName: user?.name || ''
})
}, [isAuthenticated, user])

const navItems = [
// Basic navigation items that are always shown
const baseNavItems: BaseNavItem[] = [
{ path: '/agents', label: 'Meet the Agents' },
{ path: '/faq', label: 'FAQ' },
{ path: '/login', label: 'Login' },
]

// Type guard function to check if a NavItem has onClick property
const hasOnClick = (item: NavItem): item is NavItemWithAction => {
return 'onClick' in item;
}

// Type guard function to check if a NavItem has disabled property
const hasDisabled = (item: NavItem): item is NavItemWithAction => {
return 'disabled' in item;
}

// Dynamically determine auth-related nav item
const getAuthNavItem = (): NavItemWithAction => {
if (isLoading) {
return { path: '#', label: 'Loading...', disabled: true }
} else if (authState.isUserAuthenticated) {
return {
path: '#',
label: `Logout (${authState.userName.split(' ')[0] || 'User'})`,
onClick: (e: React.MouseEvent) => {
e.preventDefault()
logout()
}
}
} else {
return { path: '/login', label: 'Login' }
}
}

// Combine base items with auth item
const navItems: NavItem[] = [...baseNavItems, getAuthNavItem()]

const emergencyItem = { path: '/emergency', label: 'Emergency', icon: '🚨', isEmergency: true }

const toggleMobileMenu = () => {
Expand All @@ -37,22 +100,47 @@ const TopNavigation: React.FC = () => {

{/* Desktop Navigation & Emergency Button */}
<div className="hidden md:flex items-center space-x-6">
{navItems.map((item) => (
<Link
key={item.path}
to={item.path}
className={`px-4 py-2 rounded-xl transition-all duration-200 font-medium relative
after:content-[''] after:absolute after:bottom-0 after:left-1/2 after:w-0 after:h-0.5 after:bg-primary after:transition-all after:duration-300 after:-translate-x-1/2
hover:after:w-full
${
location.pathname === item.path
? 'text-primary-dark after:w-full'
: 'text-gray-600 hover:text-gray-800'
}`}
>
{item.label}
</Link>
))}
{navItems.map((item) => {
// For items with custom onClick handlers (like logout)
if (hasOnClick(item)) {
return (
<button
key={item.path}
onClick={item.onClick}
disabled={hasDisabled(item) && item.disabled}
className={`px-4 py-2 rounded-xl transition-all duration-200 font-medium relative
after:content-[''] after:absolute after:bottom-0 after:left-1/2 after:w-0 after:h-0.5 after:bg-primary after:transition-all after:duration-300 after:-translate-x-1/2
hover:after:w-full
${hasDisabled(item) && item.disabled ? 'opacity-50 cursor-not-allowed' : ''}
${
location.pathname === item.path
? 'text-primary-dark after:w-full'
: 'text-gray-600 hover:text-gray-800'
}`}
>
{item.label}
</button>
);
}

// Regular link items
return (
<Link
key={item.path}
to={item.path}
className={`px-4 py-2 rounded-xl transition-all duration-200 font-medium relative
after:content-[''] after:absolute after:bottom-0 after:left-1/2 after:w-0 after:h-0.5 after:bg-primary after:transition-all after:duration-300 after:-translate-x-1/2
hover:after:w-full
${
location.pathname === item.path
? 'text-primary-dark after:w-full'
: 'text-gray-600 hover:text-gray-800'
}`}
>
{item.label}
</Link>
);
})}
<Link
to={emergencyItem.path}
className="emergency-button"
Expand Down Expand Up @@ -95,20 +183,46 @@ const TopNavigation: React.FC = () => {
className="md:hidden border-t border-gray-200"
>
<div className="px-2 pt-2 pb-3 space-y-2">
{navItems.map((item) => (
<Link
key={item.path}
to={item.path}
onClick={() => setIsMobileMenuOpen(false)}
className={`flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-200 font-medium ${
location.pathname === item.path
? 'bg-gradient-to-r from-primary to-primary-dark text-white shadow-md'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-800'
}`}
>
<span>{item.label}</span>
</Link>
))}
{navItems.map((item) => {
// For items with custom onClick handlers (like logout)
if (hasOnClick(item)) {
return (
<button
key={item.path}
onClick={(e) => {
item.onClick?.(e);
setIsMobileMenuOpen(false);
}}
disabled={hasDisabled(item) && item.disabled}
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-200 font-medium ${
hasDisabled(item) && item.disabled ? 'opacity-50 cursor-not-allowed' : ''
} ${
location.pathname === item.path
? 'bg-gradient-to-r from-primary to-primary-dark text-white shadow-md'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-800'
}`}
>
<span>{item.label}</span>
</button>
);
}

// Regular link items
return (
<Link
key={item.path}
to={item.path}
onClick={() => setIsMobileMenuOpen(false)}
className={`flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-200 font-medium ${
location.pathname === item.path
? 'bg-gradient-to-r from-primary to-primary-dark text-white shadow-md'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-800'
}`}
>
<span>{item.label}</span>
</Link>
);
})}
<Link
key={emergencyItem.path}
to={emergencyItem.path}
Expand Down
8 changes: 7 additions & 1 deletion src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ interface AuthProviderProps {
}

// Create the API URL based on environment
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080' || 'http://localhost:8081';
const API_URL = import.meta.env.VITE_API_URL || 'https://mindgarden-6xntrakg7q-nw.a.run.app/';

// Log the API URL for debugging
console.log('API URL being used:', API_URL);

export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);
Expand Down Expand Up @@ -223,8 +226,11 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
// Remove default auth header
delete axios.defaults.headers.common['Authorization'];

// Explicitly set user to null to trigger isAuthenticated update
setUser(null);
setIsGuest(false);

console.log('Logout called, user state cleared');
};

// Compute authentication status
Expand Down
Loading