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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions front-end/components/ChatBox.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import { useState } from "react";
import { useTimelineContext } from "@/context/TimelineContext";

export default function ChatBox() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState("");

const { timelineResult } = useTimelineContext();
const sendMessage = async (e) => {
e.preventDefault();
if (!input.trim()) return;

const userMessage = { sender: "user", text: input };
setMessages([...messages, userMessage]);

const [username, tag] = timelineResult.player_id.split("#");
// Example: send to backend/chatbot API
const response = await fetch("/api/chat", {
const response = await fetch("https://v4ft9564pb.execute-api.us-west-2.amazonaws.com/player/ask", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: input }),
body: JSON.stringify({
game_name: username,
tagline: tag,
question: input
}),
});

const data = await response.json();
Expand Down
129 changes: 89 additions & 40 deletions front-end/components/FlipBook.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SearchAndCompare from "./SearchandCompare";
import MatchSelector from "./MatchSelector";
import MatchTimeline from "./MatchTimeline";
import { useTimelineContext } from "@/context/TimelineContext";
import { useRealTimelineContext } from "@/context/RealTimelineContext";
import ChatInput from "./ChatInput";
import ChatOutput from "./ChatOutput.jsx";
import AncientRunicPage from "./AncientRunicPage.jsx";
Expand All @@ -22,21 +23,14 @@ const FlipBook = () => {
const [onLastPage, setOnLastPage] = useState(false);
const [isTurning, setIsTurning] = useState(false);
const { timelineResult } = useTimelineContext();
const { realTimelineResult, setRealTimelineResult} = useRealTimelineContext();

// Match Timeline state + Match Selector
const [matches, setMatches] = useState([]);
const [selectedMatchId, setSelectedMatchId] = useState(null);

// Player stats state
const [player1Stats, setPlayer1Stats] = useState({
"avg_kda": 0,
"avg_cs_per_min": 0,
"avg_kill_participation": 0,
"avg_dpm": 0,
"avg_gpm": 0,
"avg_solo_kills": 0,
"avg_vision_score": 0,
"avg_cc_time": 0});
const [player1Stats, setPlayer1Stats] = useState(null);

const [player2Stats, setPlayer2Stats] = useState({
"avg_kda": 0,
Expand All @@ -47,7 +41,7 @@ const FlipBook = () => {
"avg_solo_kills": 0,
"avg_vision_score": 0,
"avg_cc_time": 0});

const [mostPlayed, setMostPlayed] = useState(null);
// Chat state
const [chatMessages, setChatMessages] = useState([]);
const [isLoadingChat, setIsLoadingChat] = useState(false);
Expand All @@ -57,22 +51,77 @@ const FlipBook = () => {
}, []);

const [searchQuery, setSearchQuery] = useState("");
async function pollUntilReady(interval = 5000, timeout = 60000) {
const start = Date.now();
while (true) {
if(!timelineResult) continue;
const [username, tag] = timelineResult.player_id.split("#");
try {
const response = await fetch(`https://v4ft9564pb.execute-api.us-west-2.amazonaws.com/player/profile?game_name=${username}&tagline=${tag}`);
if (response.ok) {
const data = await response.json();
return data;
}
} catch (err) {
console.warn("Polling error:", err.message);
}

if (Date.now() - start > timeout) {
throw new Error("Polling timed out");
}

await new Promise(resolve => setTimeout(resolve, interval));
}
}

useEffect(() => {
let isActive = true;
async function fetchRealTimelineResult() {
try {
console.log("Polling for timeline result...");
const data = await pollUntilReady(
5000,
600000
);

if (isActive) {
console.log("Received 200 response:", data);
setRealTimelineResult(data);
}
} catch (err) {
console.error("Polling failed:", err);
}
}

fetchRealTimelineResult();

return () => {
isActive = false;
};
}, []);

const pageRefs = useRef([]);
const isInitialized = useRef(false);

useEffect(() => {
if (!timelineResult) return;


if (!timelineResult) return;
setPlayer1Stats(timelineResult.stats)
const mostPlayedList = timelineResult.most_played_champions
const championsArray = Object.entries(mostPlayedList).map(([name, games]) => ({ name, games }));
setMostPlayed(championsArray);

if (!realTimelineResult) return;

if (timelineResult.timeline_data) {
if (realTimelineResult.timeline_data) {

setMatches(timelineResult.timeline_data);
setSelectedMatchId(timelineResult.timeline_data[0]?.match_id || null);
setMatches(realTimelineResult.timeline_data);
setSelectedMatchId(realTimelineResult.timeline_data[0]?.match_id || null);
} else {
console.warn("No timeline data found in search result:", timelineResult);
console.warn("No timeline data found in search result:", realTimelineResult);
}
setPlayer1Stats(timelineResult.stats)
}, [timelineResult]);
}, [timelineResult, realTimelineResult]);


// Get the currently selected match
Expand Down Expand Up @@ -103,19 +152,23 @@ const FlipBook = () => {
setIsLoadingChat(true);

try {
// TODO: Replace with actual API call to your backend
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: userMessage }),
});
const [username, tag] = timelineResult.player_id.split("#");
const response = await fetch("https://v4ft9564pb.execute-api.us-west-2.amazonaws.com/player/ask", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
game_name: username,
tagline: tag,
question: userMessage
}),
});

const data = await response.json();

// Add bot response
const botMessage = {
sender: "bot",
text: data.reply || "I received your question. Let me analyze that for you...",
text: data.answer || "I received your question. Let me analyze that for you...",
timestamp: new Date().toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
Expand All @@ -140,9 +193,10 @@ const FlipBook = () => {
}, []);

// Create page structure once and store it in a ref
const pageStructure = useMemo(() =>
const pageStructure = useMemo(() => {
if (!mostPlayed || !timelineResult || !player1Stats) return [];

[
return [
{ cover: "book_cover.jpg", frontCover: true, id: 0 },
{ front: <AncientRunicPage
variant="power" // "default", "power", "mystical", "elements"
Expand All @@ -165,14 +219,12 @@ const FlipBook = () => {
runeCount={10} // Number of runes (default 9)
/>,
back: <SummaryBack data={{
region: "Shurima",
username: timelineResult.player_id,
region: timelineResult.playstyle.archetype,
profile:["Late-Game", "Scaling", "Empire-Building"],
statistics: { gamesPlayed: 120, winRate: "55%", averageKDA: "3.5", cspm: "7.8"},
mostPlayed: [
{ name: "Azir", games: 23 },
{ name: "Sivir", games: 17 },
{ name: "Cassiopeia", games: 12 },
],
statistics: { goldpm: player1Stats.avg_gpm.toFixed(2), winRate: player1Stats.win_rate.toFixed(2), averageKDA: player1Stats.avg_kda.toFixed(2), cspm: player1Stats.avg_cs_per_min.toFixed(2)},
mostPlayed: mostPlayed,
playerStats: player1Stats
}}/>,
id: 2
},
Expand Down Expand Up @@ -285,20 +337,17 @@ const FlipBook = () => {
id: 11
},
{ cover: "green-cover.jpg", id: 12 },
], []);
]}, [timelineResult, mostPlayed, player1Stats]);

// Initialize pages only once
useEffect(() => {
if (isInitialized.current) return;
if (!pageStructure.length) return;

const total = pageStructure.length;
setPages(pageStructure);
setZIndices(Array.from({ length: total }, (_, i) => total - i + 1));
setFlippedStates(Array(total).fill(false));

pageRefs.current = Array(total)
.fill()
.map(() => React.createRef());
pageRefs.current = Array(total).fill().map(() => React.createRef());

isInitialized.current = true;
}, [pageStructure]);
Expand Down
5 changes: 3 additions & 2 deletions front-end/components/SearchandCompare.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const SearchAndCompare = ({ player1Stats, onPlayer2Found }) => {
"avg_vision_score": 0,
"avg_cc_time": 0});
const [hasSearched, setHasSearched] = useState(false); // Track if user has searched
const { setFriendResult } = useFriendContext();
const { friendResult, setFriendResult } = useFriendContext();
const stats = ["avg_kda", "avg_cs_per_min", "avg_kill_participation", "avg_dpm", "avg_gpm", "avg_solo_kills", "avg_vision_score", "avg_cc_time"];
const displayStats = {
"avg_kda": "KDA",
Expand Down Expand Up @@ -116,7 +116,8 @@ const SearchAndCompare = ({ player1Stats, onPlayer2Found }) => {
{/* Stats Display - Identical to Social */}
<div className="bg-black bg-opacity-70 rounded-lg p-8 w-full max-w-md">
<h3 className="text-xl font-semibold text-white mb-6 text-center">
Searched Player&apos;s Name
{!friendResult && "Search for a player"}
{friendResult && `${friendResult.player_id}`}
</h3>

<div className="space-y-1">
Expand Down
4 changes: 3 additions & 1 deletion front-end/components/Social.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from "react";
import { useTimelineContext } from "@/context/TimelineContext";

const Social = ({ input1 = {}, input2 = {} }) => {
const stats1 = input1;
const stats2 = input2;
const { timelineResult } = useTimelineContext();

const getStatColor = (stat1, stat2) => {
const val1 = stat1;
Expand Down Expand Up @@ -36,7 +38,7 @@ const Social = ({ input1 = {}, input2 = {} }) => {
{/* Stats Display */}
<div className="bg-black bg-opacity-70 rounded-lg p-8 w-full max-w-md">
<h3 className="text-xl font-semibold text-white mb-6 text-center">
Player Name
{timelineResult.player_id}
</h3>

<div className="space-y-1">
Expand Down
Loading