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

Interactive Discussion Forum with CURD operation & Responsive Design #1782

Merged
merged 2 commits into from
Nov 8, 2024
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
1 change: 1 addition & 0 deletions frontend/src/components/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function renderNavbar() {
<a href="/about" class="py-2 px-3 text-gray-900 dark:text-white">About</a>
<a href="/contact" class="py-2 px-3 text-gray-900 dark:text-white">Contact</a>
<a href="/feedback" class="py-2 px-3 text-gray-900 dark:text-white">Feedback</a>
<a href="/discussion" class="py-2 px-3 text-gray-900 dark:text-white">Discussion Forum</a>
<button id="theme-toggle" type="button" class="text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:focus:ring-gray-600 rounded-lg text-sm p-2.5">
<!-- Theme Toggle Icons -->
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
Expand Down
209 changes: 209 additions & 0 deletions frontend/src/pages/DiscussionForum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
export async function renderDiscussionForum(container) {
// Initialize questions data
let questions = JSON.parse(localStorage.getItem('questions')) || [];

// Helper function to save questions to localStorage
const saveQuestions = () => {
localStorage.setItem('questions', JSON.stringify(questions));
};

// Function to handle adding a new question
const addQuestion = (content) => {
const newQuestion = {
id: Date.now(),
content: content,
answered: false,
answer: '',
};
questions.push(newQuestion);
saveQuestions();
render();
};

// Function to handle adding an answer to a question
const addAnswer = (questionId, answerContent) => {
questions = questions.map(q =>
q.id === questionId ? { ...q, answered: true, answer: answerContent } : q
);
saveQuestions();
render();
};

// Function to render the Question Card
const renderQuestionCard = (question) => {
const card = document.createElement('div');
card.classList.add('bg-white', 'dark:bg-gray-800', 'p-4', 'rounded-md', 'shadow-md', 'mb-4', 'w-[30%]');

const questionText = document.createElement('p');
questionText.textContent = question.content;
questionText.classList.add('text-lg', 'text-gray-800', 'dark:text-gray-100');

card.appendChild(questionText);

if (question.answered) {
const answerSection = document.createElement('div');
answerSection.classList.add('mt-4', 'text-gray-600', 'dark:text-gray', 'dark:bg-gray-700', 'p-5', 'rounded-lg')
const answerTitle = document.createElement('h3');
answerTitle.textContent = 'Answer:';
answerTitle.classList.add('text-indigo-600', 'dark:text-indigo-400');

const answerText = document.createElement('p');
answerText.textContent = question.answer;
answerText.classList.add('text-gray-700', 'dark:text-gray-300');

answerSection.appendChild(answerTitle);
answerSection.appendChild(answerText);
card.appendChild(answerSection);
} else {
const answerForm = renderAnswerForm(question.id);
card.appendChild(answerForm);
}

return card;
};
// Function to render the Question Form
const renderQuestionForm = () => {
const formContainer = document.createElement('div');
formContainer.classList.add('bg-white', 'dark:bg-gray-800', 'p-4', 'rounded-md', 'shadow-md', 'mb-6');

const textarea = document.createElement('textarea');
textarea.placeholder = 'Ask your question...';
textarea.classList.add('w-full', 'p-3', 'border', 'rounded-md', 'dark:bg-gray-700', 'dark:text-white', 'focus:outline-none', 'focus:ring-2', 'focus:ring-indigo-500');

const button = document.createElement('button');
button.textContent = 'Ask Question';
button.classList.add('mt-4', 'bg-indigo-500', 'text-white', 'px-4', 'py-2', 'rounded-md', 'hover:bg-indigo-600', 'focus:outline-none', 'focus:ring-2', 'focus:ring-indigo-500');

formContainer.appendChild(textarea);
formContainer.appendChild(button);

button.addEventListener('click', (e) => {
e.preventDefault();
const content = textarea.value.trim();
if (content) {
addQuestion(content);
textarea.value = '';
}
});

return formContainer;
};

// Function to render the Answer Form
const renderAnswerForm = (questionId) => {
const formContainer = document.createElement('div');
formContainer.classList.add('bg-white', 'dark:bg-gray-800', 'p-4', 'rounded-md', 'shadow-md', 'mt-4');

const textarea = document.createElement('textarea');
textarea.placeholder = 'Write your answer...';
textarea.classList.add('w-full', 'p-3', 'border', 'rounded-md', 'dark:bg-gray-700', 'dark:text-white', 'focus:outline-none', 'focus:ring-2', 'focus:ring-indigo-500');

const button = document.createElement('button');
button.textContent = 'Submit Answer';
button.classList.add('mt-4', 'bg-green-500', 'text-white', 'px-4', 'py-2', 'rounded-md', 'hover:bg-green-600', 'focus:outline-none', 'focus:ring-2', 'focus:ring-green-500');

formContainer.appendChild(textarea);
formContainer.appendChild(button);

button.addEventListener('click', (e) => {
e.preventDefault();
const answerContent = textarea.value.trim();
if (answerContent) {
addAnswer(questionId, answerContent);
textarea.value = '';
}
});

return formContainer;
};



// Function to render the list of questions
const renderQuestionList = () => {
// Create container divs for unanswered and answered sections
const unansweredContainer = document.createElement('div');
const answeredContainer = document.createElement('div');

// Create titles for unanswered and answered questions
const unansweredTitle = document.createElement('h2');
unansweredTitle.textContent = 'Unanswered Questions';
unansweredTitle.classList.add('text-2xl', 'font-semibold', 'mb-4', 'text-gray-800', 'dark:text-white');

const answeredTitle = document.createElement('h2');
answeredTitle.textContent = 'Answered Questions';
answeredTitle.classList.add('text-2xl', 'font-semibold', 'mb-4', 'text-gray-800', 'dark:text-white', 'mt-8');

// Append the titles to their respective containers
unansweredContainer.appendChild(unansweredTitle);
answeredContainer.appendChild(answeredTitle);

// Filter questions into answered and unanswered arrays
const unansweredQuestions = questions.filter(q => !q.answered);
const answeredQuestions = questions.filter(q => q.answered);

// Create a wrapper div for unanswered questions
const unansweredCardsContainer = document.createElement('div');
unansweredCardsContainer.classList.add('flex', 'gap-9', 'flex-wrap'); // Add space between cards

// Handle unanswered questions
if (unansweredQuestions.length === 0) {
const noUnansweredMessage = document.createElement('p');
noUnansweredMessage.textContent = 'No unanswered questions yet.';
unansweredCardsContainer.appendChild(noUnansweredMessage);
} else {
unansweredQuestions.forEach(q => {
const questionCard = renderQuestionCard(q);
unansweredCardsContainer.appendChild(questionCard);
});
}

// Create a wrapper div for answered questions
const answeredCardsContainer = document.createElement('div');
answeredCardsContainer.classList.add('flex', 'gap-9', 'flex-wrap'); // Add space between cards

// Handle answered questions
if (answeredQuestions.length === 0) {
const noAnsweredMessage = document.createElement('p');
noAnsweredMessage.textContent = 'No answered questions yet.';
answeredCardsContainer.appendChild(noAnsweredMessage);
} else {
answeredQuestions.forEach(q => {
const questionCard = renderQuestionCard(q);
answeredCardsContainer.appendChild(questionCard);
});
}

// Append the card containers to their respective sections
unansweredContainer.appendChild(unansweredCardsContainer);
answeredContainer.appendChild(answeredCardsContainer);

// Append both sections to the container
container.appendChild(unansweredContainer);
container.appendChild(answeredContainer);
};



const render = () => {
container.innerHTML = ''; // Clear the container

// Create the heading for the forum
const heading = document.createElement('h1');
heading.textContent = 'Wordwise Discussion Forum';
heading.classList.add('text-3xl', 'font-bold', 'mb-6', 'text-gray-800', 'dark:text-white');
container.appendChild(heading);

// First, append the question form at the top
const questionForm = renderQuestionForm();
container.appendChild(questionForm);

// Then, append the question list (answered and unanswered)
const questionList = renderQuestionList();
container.appendChild(questionList);
};



render(); // Initial render
}
92 changes: 90 additions & 2 deletions frontend/src/styles/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,10 @@ video {
height: 2rem;
}

.min-h-screen {
min-height: 100vh;
}

.w-1\/3 {
width: 33.333333%;
}
Expand Down Expand Up @@ -835,6 +839,22 @@ video {
width: 100%;
}

.w-\[27\%\] {
width: 27%;
}

.w-\[28\%\] {
width: 28%;
}

.w-\[29\%\] {
width: 29%;
}

.w-\[30\%\] {
width: 30%;
}

.max-w-4xl {
max-width: 56rem;
}
Expand All @@ -843,6 +863,7 @@ video {
max-width: 1280px;
}


.transform {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
Expand Down Expand Up @@ -903,6 +924,14 @@ video {
gap: 2rem;
}

.gap-2 {
gap: 0.5rem;
}

.gap-9 {
gap: 2.25rem;
}

.space-x-3 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(0.75rem * var(--tw-space-x-reverse));
Expand Down Expand Up @@ -1132,6 +1161,30 @@ video {
background-color: rgb(254 249 195 / var(--tw-bg-opacity));
}

.bg-green-500 {
--tw-bg-opacity: 1;
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
}

.bg-indigo-500 {
--tw-bg-opacity: 1;
background-color: rgb(99 102 241 / var(--tw-bg-opacity));
}

.bg-gradient-to-r {
background-image: linear-gradient(to right, var(--tw-gradient-stops));
}

.from-indigo-100 {
--tw-gradient-from: #e0e7ff var(--tw-gradient-from-position);
--tw-gradient-to: rgb(224 231 255 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}

.to-purple-100 {
--tw-gradient-to: #f3e8ff var(--tw-gradient-to-position);
}

.object-cover {
-o-object-fit: cover;
object-fit: cover;
Expand Down Expand Up @@ -1161,9 +1214,11 @@ video {
padding: 2rem;
}

.p-1 {
.p-5 {
padding: 1.25rem;
.p-1 {
padding: 0.25rem;
}
}

.px-12 {
padding-left: 3rem;
Expand Down Expand Up @@ -1440,6 +1495,12 @@ video {
color: rgb(133 77 14 / var(--tw-text-opacity));
}

.text-indigo-400 {
--tw-text-opacity: 1;
color: rgb(129 140 248 / var(--tw-text-opacity));
}


.shadow-lg {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
Expand All @@ -1458,6 +1519,12 @@ video {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}

.shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}

.shadow-gray-200 {
--tw-shadow-color: #e5e7eb;
--tw-shadow: var(--tw-shadow-colored);
Expand Down Expand Up @@ -1541,6 +1608,12 @@ body.dark-mode {
padding: 20px;
}

.hover\:scale-105:hover {
--tw-scale-x: 1.05;
--tw-scale-y: 1.05;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

.hover\:bg-\[\#753ff1\]:hover {
--tw-bg-opacity: 1;
background-color: rgb(117 63 241 / var(--tw-bg-opacity));
Expand Down Expand Up @@ -1596,6 +1669,16 @@ body.dark-mode {
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}

.hover\:bg-green-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
}

.hover\:bg-indigo-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(79 70 229 / var(--tw-bg-opacity));
}

.hover\:text-blue-700:hover {
--tw-text-opacity: 1;
color: rgb(29 78 216 / var(--tw-text-opacity));
Expand Down Expand Up @@ -1702,6 +1785,11 @@ body.dark-mode {
--tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity));
}

.focus\:ring-green-500:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity));
}

.focus\:ring-opacity-50:focus {
--tw-ring-opacity: 0.5;
}
Expand Down
Loading
Loading