Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Haoyue faq tool implementation #3118

Draft
wants to merge 4 commits into
base: development
Choose a base branch
from
Draft
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
82 changes: 82 additions & 0 deletions src/components/Faq/FaqHistory.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { getFAQHistory } from './api';

function FaqHistory() {
const { id } = useParams();
// console.log('useParams output:', useParams());
// console.log('FAQ ID:', id);
const [faq, setFaq] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchFAQHistory = async () => {
try {
const response = await getFAQHistory(id);
setFaq(response.data);
console.log('FAQ History:', response.data);
} catch (error) {
console.error('Error fetching FAQ History:', error);
} finally {
setLoading(false);
}
};
fetchFAQHistory();
}, [id]);

if (loading) return <p>Loading FAQ...</p>;
if (!faq) return <p>FAQ not found</p>;

return (
<div>
<h2>FAQ History</h2>

<h3>Current Question:</h3>
<p>
<strong>Question:</strong> {faq.question}
</p>
<p>
<strong>Answer:</strong> {faq.answer}
</p>

<h4>Created By:</h4>
<p>
<strong>User:</strong> {faq.createdBy ? faq.createdBy : 'Unknown'}
</p>
<p>
<strong>Date:</strong> {new Date(faq.createdAt).toLocaleString()}
</p>
<h4>Change History:</h4>
{faq.changeHistory && faq.changeHistory.length > 0 ? (
<ul>
{faq.changeHistory.map((change, index) => (
<li key={index}>
<p>
<strong>Updated By:</strong> {change.updatedBy ? change.updatedBy : 'Unknown'}
</p>
<p>
<strong>Updated At:</strong> {new Date(change.updatedAt).toLocaleString()}
</p>
<p>
<strong>Previous Question:</strong> {change.previousQuestion}
</p>
<p>
<strong>Previous Answer:</strong> {change.previousAnswer}
</p>
<p>
<strong>Updated Question:</strong> {change.updatedQuestion}
</p>
<p>
<strong>Updated Answer:</strong> {change.updatedAnswer}
</p>
</li>
))}
</ul>
) : (
<p>No modification history available.</p>
)}
</div>
);
}

export default FaqHistory;
153 changes: 153 additions & 0 deletions src/components/Faq/FaqManagement.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import React, { useState, useEffect } from 'react';
import { Button } from 'reactstrap';
import { Link } from 'react-router-dom';
import { addFAQ, editFAQ, getAllFAQs, deleteFAQ } from './api';

function FaqManagement({ editMode = false, faqToEdit = {} }) {
const [question, setQuestion] = useState(editMode ? faqToEdit.question : '');
const [answer, setAnswer] = useState(editMode ? faqToEdit.answer : '');
const [faqs, setFaqs] = useState([]);
const [loading, setLoading] = useState(true);
const [editingFAQ, setEditingFAQ] = useState(null);

useEffect(() => {
let isMounted = true;

const fetchFAQs = async () => {
try {
const allFAQs = await getAllFAQs();
console.log('Fetched FAQs:', allFAQs);

if (isMounted) {
setFaqs(Array.isArray(allFAQs.data) ? allFAQs.data : []);
}
} catch (error) {
console.error('Error fetching FAQs:', error);
if (isMounted) setFaqs([]);
} finally {
if (isMounted) setLoading(false);
}
};

fetchFAQs();

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

const handleAddFAQ = async () => {
try {
await addFAQ(question, answer);
alert('FAQ added successfully!');
setQuestion('');
setAnswer('');

const updatedFAQs = await getAllFAQs();
setFaqs(Array.isArray(updatedFAQs.data) ? updatedFAQs.data : []);
} catch (error) {
console.error('Error adding FAQ:', error);
}
};

const handleEditFAQ = async () => {
if (!editingFAQ || !editingFAQ._id) {
console.error('No FAQ selected for editing.');
return;
}

try {
await editFAQ(editingFAQ._id, question, answer);
alert('FAQ updated successfully!');

setEditingFAQ(null);
setQuestion('');
setAnswer('');

const updatedFAQs = await getAllFAQs();
setFaqs(Array.isArray(updatedFAQs.data) ? updatedFAQs.data : []);
} catch (error) {
console.error('Error updating FAQ:', error);
}
};

const handleDeleteFAQ = async faqId => {
try {
await deleteFAQ(faqId);
alert('FAQ deleted successfully!');

const updatedFAQs = await getAllFAQs();
setFaqs(Array.isArray(updatedFAQs.data) ? updatedFAQs.data : []);
} catch (error) {
console.error('Error deleting FAQ:', error);
}
};

const handleEditClick = faq => {
setEditingFAQ(faq); // Set the currently selected FAQ to be edited
setQuestion(faq.question);
setAnswer(faq.answer);
};

const openFaqDetailInNewTab = faqId => {
window.open(`/faqs/${faqId}/history`, '_blank');
};

return (
<div>
<h2>{editingFAQ ? 'Edit FAQ' : 'Add FAQ'}</h2>
<input
type="text"
value={question}
onChange={e => setQuestion(e.target.value)}
placeholder="Enter question"
/>
<input
type="text"
value={answer}
onChange={e => setAnswer(e.target.value)}
placeholder="Enter answer"
/>
<button onClick={editingFAQ ? handleEditFAQ : handleAddFAQ}>
{editingFAQ ? 'Save Changes' : 'Add FAQ'}
</button>

<Link to="/unanswered-faqs" style={{ color: 'blue', textDecoration: 'underline' }}>
View Unanswered Questions
</Link>

<h4>All FAQs</h4>
{loading ? (
<p>Loading FAQs...</p>
) : Array.isArray(faqs) && faqs.length === 0 ? (
<p>No FAQs available at the moment. Please add new FAQs.</p>
) : (
<ul>
{Array.isArray(faqs) ? (
faqs.map(faq => (
<li key={faq._id}>
<span
onClick={() => openFaqDetailInNewTab(faq._id)}
style={{ color: 'blue', textDecoration: 'underline', cursor: 'pointer' }}
>
{faq.question}
</span>
<p>{faq.answer}</p>
<Button color="primary" onClick={() => handleEditClick(faq)}>
Edit FAQ
</Button>
<Button color="danger" onClick={() => handleDeleteFAQ(faq._id)}>
Delete FAQ
</Button>
</li>
))
) : (
<p>Error: FAQs data is not in the correct format.</p>
)}
</ul>
)}
</div>
);
}

export default FaqManagement;
107 changes: 107 additions & 0 deletions src/components/Faq/FaqSearch.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useState, useEffect, useRef } from 'react';
import { debounce } from 'lodash';
import { Button } from 'reactstrap';
import { searchFAQs, logUnansweredQuestion } from './api';

function FaqSearch() {
const [searchQuery, setSearchQuery] = useState('');
const [suggestions, setSuggestions] = useState([]);
const [notFound, setNotFound] = useState(false);
const [selectedFAQ, setSelectedFAQ] = useState(null);
const [logging, setLogging] = useState(false);

const fetchSuggestions = debounce(async query => {
try {
const response = await searchFAQs(query);
setSuggestions(response.data);
setNotFound(response.data.length === 0);
} catch (error) {
console.error('Error fetching FAQ suggestions:', error);
}
}, 300);

const handleSearchChange = e => {
const query = e.target.value;
setSearchQuery(query);
setSelectedFAQ(null);

if (query.length >= 1) {
fetchSuggestions(query);
} else {
setSuggestions([]);
setNotFound(false);
}
};

const handleSelectFAQ = faq => {
setSelectedFAQ(faq);
setSearchQuery('');
setSuggestions([]);
setNotFound(false);
};

const handleLogUnanswered = async () => {
if (!searchQuery.trim()) return;

setLogging(true);
try {
const response = await logUnansweredQuestion(searchQuery);
alert(response.data.message || 'Your question has been recorded.');
} catch (error) {
console.error('Error logging unanswered question:', error);
alert('Failed to log question. It may already exist.');
} finally {
setLogging(false);
}
};

return (
<div>
<h2>Frequently Asked Questions</h2>
<input
type="text"
value={searchQuery}
onChange={handleSearchChange}
placeholder="Search FAQs"
/>

{selectedFAQ ? (
<div>
<h4>{selectedFAQ.question}</h4>
<p>{selectedFAQ.answer}</p>
</div>
) : suggestions.length > 0 ? (
<ul>
{suggestions.map(faq => (
<li
key={faq._id}
onClick={() => handleSelectFAQ(faq)}
style={{
padding: '10px',
margin: '5px 0',
border: '1px solid #ddd',
borderRadius: '5px',
cursor: 'pointer',
backgroundColor: '#f9f9f9',
transition: 'background-color 0.3s',
}}
onMouseOver={e => (e.currentTarget.style.backgroundColor = '#e0e0e0')}
onMouseOut={e => (e.currentTarget.style.backgroundColor = '#f9f9f9')}
>
{faq.question}
</li>
))}
</ul>
) : notFound ? (
<div>
<p>No results found.</p>
<Button color="primary" onClick={handleLogUnanswered} disabled={logging}>
{logging ? 'Logging...' : 'Log this question and notify Owner'}
</Button>
</div>
) : null}
</div>
);
}

export default FaqSearch;
Loading