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
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export API_URL=http://localhost:5001
export REACT_APP_API_URL=http://localhost:5001
20,669 changes: 13,381 additions & 7,288 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@
"private": true,
"dependencies": {
"@apollo/client": "^3.7.2",
"@stagewise-plugins/react": "^0.6.1",
"@stagewise/toolbar-react": "^0.6.1",
"axios": "^1.2.1",
"classnames": "^2.3.2",
"formik": "^2.2.9",
"graphql": "^15.8.0",
"graphql-tag": "^2.12.6",
"graphql-ws": "^5.11.2",
"moment": "^2.29.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"graphql-ws": "^5.11.2",
"react-router-dom": "^6.4.5",
"react-scripts": "5.0.1",
"react-textarea-autosize": "^8.4.0",
"semantic-ui-css": "^2.5.0",
"semantic-ui-react": "^2.1.4",
"semantic-ui-react": "^2.1.5",
"store2": "^2.14.2"
},
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Chat Kodala</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
268 changes: 216 additions & 52 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,173 @@
import React, {useState} from 'react';
import './App.css'
import React, { useState, useEffect, useCallback, useRef } from 'react';
import './css/App.css'
import cx from 'classnames'
import CreateChat from './CreateChat'
import Offline from './Offline'
import axios from 'axios'
import moment from 'moment'
import store from 'store2'
import Query from './Components/Query'
import ChatContainer from './ChatContainer'
import { GET_CHAT, GET_CONTRACT } from './queries'
import ToggleOpeningStateButton from './ToggleOpeningStateButton'
import Rate from './Rate'
import ContractNotFound from './ContractNotFound'
import ErrorBoundary from './ErrorBoundary'
import { isWorkingHours, getCurrentTime } from './utils';
import { CHAT_STATUS, STORAGE_KEYS, ERROR_MESSAGES } from './constants';

export const isWorkingHours = (session, currentTime) => {
switch (session) {
case 1:
return currentTime.hours() >= 0 && currentTime.hours() < 8
case 2:
return currentTime.hours() >= 8 && currentTime.hours() < 16
case 3:
return currentTime.hours() >= 16 && currentTime.hours() < 24
default:
return "non existing"
const App = () => {
// Get activeChat from localStorage
let activeChat = store(STORAGE_KEYS.ACTIVE_CHAT)
console.log('Active chat from localStorage:', activeChat)

// If activeChat exists and its status is "finished", reset it
if (activeChat && activeChat.status === CHAT_STATUS.FINISHED) {
console.log('Resetting finished chat on page load')
store.remove(STORAGE_KEYS.ACTIVE_CHAT)
activeChat = null
}
}

const App = () => {
const activeChat = store('activeChat')
const contractId = store('contractId')
console.log('contractId', contractId)
const contractId = store(STORAGE_KEYS.CONTRACT_ID)
const [showCreate, setCreate] = useState(!activeChat)
const [isOpen, setOpen] = useState(true)
const [showOffline, setOffline] = useState(false)
const [contractSession, setContractSession] = useState(null)
const sessionUpdatedRef = useRef({})

// Use useCallback to memoize the function - DEFINE BEFORE USING IT
const checkWorkingHours = useCallback((contractSession) => {
console.log('checkWorkingHours called with session:', contractSession);

getCurrentTime()
.then(function (response) {
console.log('getCurrentTime response:', response);
const now = moment(response.data, 'YYYY-MM-DDTHH:mm:ss.SSSSZ').utc()
console.log('Current time (UTC):', now.format('YYYY-MM-DD HH:mm:ss'));

const isWorking = isWorkingHours(contractSession, now);
console.log('isWorkingHours result:', isWorking);

setOffline(!isWorking);
})
.catch(function (error) {
console.error('getCurrentTime error:', error);
// Fallback to local time if API fails
const now = moment()
console.log('Fallback to local time:', now.format('YYYY-MM-DD HH:mm:ss'));

const isWorking = isWorkingHours(contractSession, now);
console.log('isWorkingHours result (fallback):', isWorking);

setOffline(!isWorking);
})
}, [setOffline]) // Only depends on setOffline

// Use effect to check working hours when contractSession changes
useEffect(() => {
if (contractSession) {
console.log('Contract session changed to:', contractSession);
checkWorkingHours(contractSession);

// For testing: Force online status after a short delay
// setTimeout(() => {
// console.log('Forcing online status for testing');
// setOffline(false);
// }, 1000);
}
}, [contractSession, checkWorkingHours])

// Handle case where contractId is missing
if (!contractId) {
return <ContractNotFound />
}
const panelStyles = cx(`panel drop-shadow radius overflow-hidden ${isOpen ? 'fadeInUp' : 'hide'}`)
const checkWorkingHours = (contractSession) => {
axios.get('http://localhost:4000/api/time', {
headers: {"Content-Type" : "application/json"}
})
.then(function ({current_time}) {
const now = moment(current_time)
console.log(isWorkingHours(contractSession, now))
setOffline(!isWorkingHours(contractSession, now))
})
};

if (showCreate) {
return (
<div className='App'>
<div>
<div className='container'>
<div className={panelStyles}>
<Query query={GET_CONTRACT} variables={{ id: contractId }}>
{({data}) => {
checkWorkingHours(data.contract.session)
if (data.contract.status !== "active" || showOffline) {
return(<Offline />)
}
<ErrorBoundary
fallback={(error, errorInfo) => (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Error Loading Contract</h3>
<p>We're sorry, but there was an error loading the contract information.</p>
<button
onClick={() => window.location.reload()}
style={{
padding: '10px 20px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Try Again
</button>
</div>
)}
>
<Query query={GET_CONTRACT} variables={{ id: contractId }}>
{({ data }) => {
if (!data?.contract) {
return <div>{ERROR_MESSAGES.LOADING_CONTRACT}</div>
}

return(<CreateChat show={showCreate} setCreate={setCreate}/>)
}}
</Query>
// Update the session in the parent component using a ref to track updates
// This prevents state updates during render
const sessionKey = `${data?.contract?.id}-${data?.contract?.session}`;
if (data?.contract?.session && !sessionUpdatedRef.current[sessionKey]) {
// Mark this session as processed to avoid repeated state updates
sessionUpdatedRef.current[sessionKey] = true;

console.log('Contract session from API:', data.contract.session, 'type:', typeof data.contract.session);

// Schedule the state update after render
setTimeout(() => {
// Convert session to number if it's a string
const sessionNum = typeof data.contract.session === 'string'
? parseInt(data.contract.session, 10)
: data.contract.session;

console.log('Setting contract session to:', sessionNum);
setContractSession(sessionNum);
}, 0);
}

if (data.contract.status !== CHAT_STATUS.ACTIVE || showOffline) {
return (<Offline />)
}

return (
<ErrorBoundary
fallback={(error, errorInfo) => (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Error Creating Chat</h3>
<p>We're sorry, but there was an error creating the chat.</p>
<button
onClick={() => window.location.reload()}
style={{
padding: '10px 20px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Try Again
</button>
</div>
)}
>
<CreateChat show={showCreate} setCreate={setCreate} />
</ErrorBoundary>
)
}}
</Query>
</ErrorBoundary>
</div>
<ToggleOpeningStateButton
isOpen={isOpen}
Expand All @@ -74,29 +181,86 @@ const App = () => {
}

return (
<div className='App'>
<div>
<div className='container'>
<div className={panelStyles}>
<Query query={GET_CHAT} variables={{ chatId: activeChat.id }}>
{({ data}) => {
if (data.chat.status === "finished") {
return(<Rate chat={data.chat} setCreate={setCreate} />)
<div className='App'>
<div>
<div className='container'>
<div className={panelStyles}>
<ErrorBoundary
fallback={(error, errorInfo) => (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Error Loading Chat</h3>
<p>We're sorry, but there was an error loading the chat information.</p>
<button
onClick={() => {
store.remove('activeChat')
window.location.reload()
}}
style={{
padding: '10px 20px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Start New Chat
</button>
</div>
)}
>
<Query query={GET_CHAT} variables={{ chatId: activeChat?.id }}>
{({ data }) => {
if (!data?.chat) {
return <div>{ERROR_MESSAGES.LOADING_CHAT}</div>
}
if (data.chat.status === CHAT_STATUS.FINISHED) {
return (
<ErrorBoundary>
<Rate chat={data.chat} setCreate={setCreate} />
</ErrorBoundary>
)
}
return (
<ChatContainer chat={data.chat} />
<ErrorBoundary
fallback={(error, errorInfo) => (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Error in Chat</h3>
<p>We're sorry, but there was an error in the chat component.</p>
<button
onClick={() => {
store.remove('activeChat')
window.location.reload()
}}
style={{
padding: '10px 20px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Start New Chat
</button>
</div>
)}
>
<ChatContainer chat={data.chat} />
</ErrorBoundary>
)
}}
</Query>
</div>
<ToggleOpeningStateButton
isOpen={isOpen}
togglePanel={() => setOpen(!isOpen)}
mainColor={'rgba(39,175,96,1)'}
/>
</ErrorBoundary>
</div>
<ToggleOpeningStateButton
isOpen={isOpen}
togglePanel={() => setOpen(!isOpen)}
mainColor={'rgba(39,175,96,1)'}
/>
</div>
</div>
</div>
)
}

Expand Down
16 changes: 15 additions & 1 deletion src/Chat.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import MessageBox from './MessageBox'
import MessageForm from './MessageForm'
import './Chat.css'
import './css/Chat.css'

class Chat extends Component {
componentDidMount() {
Expand All @@ -10,6 +10,20 @@ class Chat extends Component {

render() {
const { data, chatId } = this.props

// Add null check for data and messages
if (!data || !data.messages) {
return (
<div className="dropzone relative">
<div className='message-body chat-container'>
<div style={{ padding: '20px', textAlign: 'center' }}>
<p>Loading messages...</p>
</div>
<MessageForm chatId={chatId} />
</div>
</div>
);
}

return (
<div className="dropzone relative">
Expand Down
Loading