Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,5 @@ dist

client/.vite/

CLAUDE.md
# Claude code
CLAUDE.md
1 change: 0 additions & 1 deletion client/build.err.txt

This file was deleted.

1 change: 0 additions & 1 deletion client/build.out.txt

This file was deleted.

76 changes: 30 additions & 46 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,83 @@
import React, { useContext, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import GameShell from './game/GameShell.jsx';
import { GameContext } from './context/GameContext';
import Login from './pages/Login.jsx';
import Navbar from './components/layout/Navbar.jsx';
import styles from '../src/styles/modules/App.module.css';
import { useSound } from './hooks/useSound.js';

function App() {
const { handleLogin, userId, score, coins, energy, playerName } = useContext(GameContext);

Check warning on line 10 in client/src/App.jsx

View workflow job for this annotation

GitHub Actions / client / test-npm

'energy' is assigned a value but never used. Allowed unused vars must match /^React$/u

Check warning on line 10 in client/src/App.jsx

View workflow job for this annotation

GitHub Actions / client / test-npm

'coins' is assigned a value but never used. Allowed unused vars must match /^React$/u

Check warning on line 10 in client/src/App.jsx

View workflow job for this annotation

GitHub Actions / client / test-npm

'score' is assigned a value but never used. Allowed unused vars must match /^React$/u
const [showLogin, setShowLogin] = useState(false);
const [userPicture, setUserPicture] = useState('');
const [isLoading, setIsLoading] = useState(true);
const { playCoins } = useSound();

Check warning on line 13 in client/src/App.jsx

View workflow job for this annotation

GitHub Actions / client / test-npm

'playCoins' is assigned a value but never used. Allowed unused vars must match /^React$/u
const navigate = useNavigate();

// ✅ בדוק אם יש משתמש מחובר בעת הטעינה
useEffect(() => {
const checkLoggedInUser = async () => {
const storedUserId = localStorage.getItem('userId');

if (storedUserId) {
try {
const response = await fetch(`http://localhost:5000/api/users/${storedUserId}`);
const data = await response.json();

if (data.success) {
console.log('✅ User restored from storage:', data.user);
handleLogin(data.user);

// שמור תמונה אם יש

const storedPicture = localStorage.getItem('userPicture');
if (storedPicture) {
setUserPicture(storedPicture);
}
setIsLoading(false);
} else {
// אם המשתמש לא נמצא, הצג התחברות
setShowLogin(true);
localStorage.removeItem('userId');
setIsLoading(false);
navigate('/login');
}
} catch (error) {
console.error('❌ Failed to restore user:', error);
setShowLogin(true);
localStorage.removeItem('userId');
setIsLoading(false);
navigate('/login');
}
} else {
// אין משתמש שמור - הצג התחברות
setShowLogin(true);
setIsLoading(false);
navigate('/login');
}
};

checkLoggedInUser();
}, [handleLogin]);
}, [handleLogin, navigate]);

// ✅ שמור userId כשהוא משתנה
useEffect(() => {
if (userId) {
localStorage.setItem('userId', userId);
console.log('💾 User ID saved to localStorage:', userId);
}
}, [userId]);

// ✅ פונקציה שמטפלת בהתחברות מוצלחת
const handleLoginSuccess = (userData, googlePicture = '') => {
console.log('🎉 Login successful in App:', userData);

// שמור את התמונה מגוגל
if (googlePicture) {
setUserPicture(googlePicture);
localStorage.setItem('userPicture', googlePicture);
}

// עדכן את ה-Context
handleLogin(userData);

// סגור את מסך ההתחברות
setShowLogin(false);
};

// ✅ פונקציית התנתקות
const handleLogout = () => {
localStorage.removeItem('userId');
localStorage.removeItem('userPicture');
setUserPicture('');
setShowLogin(true);
window.location.reload(); // רענן את הדף
navigate('/login');
};

if (isLoading) {
return (
<div className={styles.loadingContainer}>
<div className={styles.loadingSpinner}>Loading...</div>
</div>
);
}

return (
<div className={styles.appWrap}>
{/* User profile top right */}
{userId && !showLogin && (
<Navbar />

{userId && (
<div className={styles.userProfile}>
<span className={styles.greeting}>Hi, {playerName || 'Player'}!</span>
{userPicture && (
Expand All @@ -97,15 +88,8 @@
</button>
</div>
)}
{/* הצג התחברות או משחק */}
{showLogin || !userId ? (
<Login
onClose={() => setShowLogin(false)}
onLoginSuccess={handleLoginSuccess}
/>
) : (
<GameShell userId={userId} />
)}

{userId && <GameShell userId={userId} />}
</div>
);
}
Expand Down
46 changes: 32 additions & 14 deletions client/src/components/home/AboutSection.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useRef, useEffect } from 'react';
import anime from 'animejs';
import FeatureCard from './FeatureCard';
import styles from './AboutSection.module.css';

export default function AboutSection() {
const sectionRef = useRef(null);
const titleRef = useRef(null);
const introRef = useRef(null);
const cardsRef = useRef(null);
const hasAnimated = useRef(false);

Expand All @@ -15,6 +17,7 @@ export default function AboutSection() {
if (entry.isIntersecting && !hasAnimated.current) {
hasAnimated.current = true;

// Animate title
anime({
targets: titleRef.current,
translateY: [40, 0],
Expand All @@ -23,12 +26,22 @@ export default function AboutSection() {
easing: 'easeOutExpo',
});

// Animate intro text
anime({
targets: cardsRef.current?.children,
translateY: [60, 0],
targets: introRef.current,
translateY: [30, 0],
opacity: [0, 1],
duration: 800,
delay: anime.stagger(150),
delay: 200,
easing: 'easeOutExpo',
});

// Cards will animate via CSS with stagger
anime({
targets: cardsRef.current,
opacity: [0, 1],
duration: 600,
delay: 400,
easing: 'easeOutExpo',
});

Expand Down Expand Up @@ -81,25 +94,30 @@ export default function AboutSection() {

return (
<section ref={sectionRef} className={styles.aboutSection} id="about">
{/* Forest Silhouette Background */}
<div className={styles.forestBackground}></div>
<div className={styles.circuitPattern}></div>

<div className={styles.container}>
<h2 ref={titleRef} className={styles.sectionTitle}>
What is SafeForest?
What is <span className={styles.titleHighlight}>SafeForest</span>?
</h2>

<p className={styles.intro}>
SafeForest is an immersive cybersecurity education game that transforms
complex security concepts into engaging, interactive challenges. Learn to
protect yourself in the digital world while playing through a cyberpunk
adventure.
<p ref={introRef} className={styles.intro}>
SafeForest is a <strong>digital forest sanctuary</strong> where cybersecurity education
meets immersive gameplay. Transform complex security concepts into engaging challenges
while exploring a world where technology and nature harmoniously blend.
</p>

<div ref={cardsRef} className={styles.featuresGrid}>
{features.map((feature, index) => (
<div key={index} className={styles.featureCard}>
<div className={styles.icon}>{feature.icon}</div>
<h3 className={styles.featureTitle}>{feature.title}</h3>
<p className={styles.featureDescription}>{feature.description}</p>
</div>
<FeatureCard
key={index}
icon={feature.icon}
title={feature.title}
description={feature.description}
index={index}
/>
))}
</div>
</div>
Expand Down
Loading
Loading