2023-12-13 14:03:02 +01:00
|
|
|
<script lang="ts">
|
2023-08-17 14:05:01 +01:00
|
|
|
import { goto } from '$app/navigation';
|
|
|
|
|
import pluralize from 'pluralize';
|
|
|
|
|
import TrashCanIcon from 'carbon-icons-svelte/lib/TrashCan.svelte';
|
2024-02-13 22:17:15 +01:00
|
|
|
import { Dropdown, SkeletonPlaceholder, SkeletonText } from 'carbon-components-svelte';
|
2023-08-17 14:05:01 +01:00
|
|
|
import ArrowLeftIcon from 'carbon-icons-svelte/lib/ArrowLeft.svelte';
|
|
|
|
|
import Vote from '$lib/components/Vote/index.svelte';
|
|
|
|
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
|
|
|
|
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
|
|
|
|
import Avatar from '$lib/components/Avatar/index.svelte';
|
|
|
|
|
import IconButton from '$lib/components/IconButton/index.svelte';
|
|
|
|
|
import CheckmarkOutlineIcon from 'carbon-icons-svelte/lib/CheckmarkOutline.svelte';
|
2024-02-13 22:17:15 +01:00
|
|
|
import { currentOrg, isOrgAdmin } from '$lib/utils/store/org';
|
2023-08-17 14:05:01 +01:00
|
|
|
import { profile } from '$lib/utils/store/user';
|
|
|
|
|
import { supabase } from '$lib/utils/functions/supabase';
|
|
|
|
|
import {
|
|
|
|
|
askCommunityValidation,
|
|
|
|
|
commentInCommunityValidation
|
|
|
|
|
} from '$lib/utils/functions/validator';
|
2023-09-27 05:35:06 +01:00
|
|
|
import { snackbar } from '$lib/components/Snackbar/store';
|
2023-08-17 14:05:01 +01:00
|
|
|
import TextField from '$lib/components/Form/TextField.svelte';
|
2023-12-13 14:03:02 +01:00
|
|
|
import DeleteModal from '$lib/components/Org/Community/DeleteModal.svelte';
|
2023-09-05 13:21:44 +00:00
|
|
|
import TextEditor from '$lib/components/TextEditor/index.svelte';
|
2023-10-02 04:15:01 +01:00
|
|
|
import { calDateDiff } from '$lib/utils/functions/date';
|
2023-12-13 14:03:02 +01:00
|
|
|
import { browser } from '$app/environment';
|
2024-02-13 22:17:15 +01:00
|
|
|
import { fetchCourses } from '$lib/components/Courses/api.js';
|
2023-08-17 14:05:01 +01:00
|
|
|
|
|
|
|
|
export let data;
|
|
|
|
|
const { slug } = data;
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
interface Comment {
|
|
|
|
|
id: string;
|
|
|
|
|
authorId: string;
|
|
|
|
|
name: string;
|
|
|
|
|
avatar: string;
|
|
|
|
|
votes: number;
|
|
|
|
|
comment: string;
|
|
|
|
|
createdAt: string;
|
|
|
|
|
}
|
|
|
|
|
interface Question {
|
|
|
|
|
id: string;
|
|
|
|
|
title: string;
|
|
|
|
|
votes: number;
|
|
|
|
|
author: {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
avatar: string;
|
|
|
|
|
};
|
|
|
|
|
body: string;
|
|
|
|
|
createdAt: string;
|
|
|
|
|
comments: Comment[];
|
|
|
|
|
totalComments: number;
|
2024-02-13 22:17:15 +01:00
|
|
|
courseId: string;
|
2023-12-13 14:03:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let question: Question;
|
2023-08-17 14:05:01 +01:00
|
|
|
let comment = '';
|
2023-12-13 14:03:02 +01:00
|
|
|
let errors: {
|
|
|
|
|
title?: string;
|
2024-02-13 22:17:15 +01:00
|
|
|
courseId?: string;
|
2023-12-13 14:03:02 +01:00
|
|
|
} = {};
|
2023-08-17 14:05:01 +01:00
|
|
|
let isValidAnswer = false; // V2 allow admin mark an answer as accepted
|
|
|
|
|
let resetInput = 1;
|
2023-12-13 14:03:02 +01:00
|
|
|
let voted: {
|
|
|
|
|
question: boolean;
|
|
|
|
|
comment: {
|
|
|
|
|
[key: string]: boolean;
|
|
|
|
|
};
|
|
|
|
|
} = { question: false, comment: {} };
|
2023-08-17 14:05:01 +01:00
|
|
|
let isEditMode = false;
|
|
|
|
|
let deleteComment = {
|
|
|
|
|
shouldDelete: false,
|
2023-12-13 14:28:10 +03:00
|
|
|
commentId: '',
|
|
|
|
|
isDeleting: false
|
2023-12-13 14:03:02 +01:00
|
|
|
};
|
|
|
|
|
let deleteQuestion = {
|
|
|
|
|
shouldDelete: false,
|
2023-12-13 14:28:10 +03:00
|
|
|
questionId: '',
|
|
|
|
|
isDeleting: false
|
2023-08-17 14:05:01 +01:00
|
|
|
};
|
|
|
|
|
let editContent = {
|
|
|
|
|
title: '',
|
2024-02-13 22:17:15 +01:00
|
|
|
body: '',
|
|
|
|
|
courseId: ''
|
2023-08-17 14:05:01 +01:00
|
|
|
};
|
|
|
|
|
|
2023-09-05 13:21:44 +00:00
|
|
|
let editorInstance = false;
|
2024-02-13 22:17:15 +01:00
|
|
|
let fetchedCourses = [];
|
2023-09-05 13:21:44 +00:00
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
function mapResToQuestion(data): Question {
|
2023-08-17 14:05:01 +01:00
|
|
|
return {
|
|
|
|
|
id: data.id,
|
|
|
|
|
title: data.title,
|
|
|
|
|
votes: data.votes,
|
|
|
|
|
author: {
|
2023-12-13 14:03:02 +01:00
|
|
|
id: data?.author?.id || '',
|
|
|
|
|
name: data?.author?.fullname || '',
|
|
|
|
|
avatar: data?.author?.avatar_url || ''
|
2023-08-17 14:05:01 +01:00
|
|
|
},
|
|
|
|
|
body: data.body,
|
2023-10-02 04:15:01 +01:00
|
|
|
createdAt: calDateDiff(data.created_at),
|
2023-08-17 14:05:01 +01:00
|
|
|
comments: data.comments.map((c) => ({
|
|
|
|
|
id: c.id,
|
2023-12-13 14:03:02 +01:00
|
|
|
authorId: c.author?.id || '',
|
|
|
|
|
name: c.author?.fullname || '',
|
|
|
|
|
avatar: c.author?.avatar_url || '',
|
2023-08-17 14:05:01 +01:00
|
|
|
votes: c.votes,
|
|
|
|
|
comment: c.body,
|
2023-10-02 04:15:01 +01:00
|
|
|
createdAt: calDateDiff(c.created_at)
|
2023-08-17 14:05:01 +01:00
|
|
|
})),
|
2024-02-13 22:17:15 +01:00
|
|
|
totalComments: 0,
|
|
|
|
|
courseId: data.course_id
|
2023-08-17 14:05:01 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-13 22:17:15 +01:00
|
|
|
async function getCourses(userId: string | null, orgId: string) {
|
|
|
|
|
const coursesResults = await fetchCourses(userId, orgId);
|
|
|
|
|
fetchedCourses = coursesResults.allCourses;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
async function fetchCommunityQuestion(slug: string) {
|
2023-08-17 14:05:01 +01:00
|
|
|
if (!slug) return;
|
|
|
|
|
|
|
|
|
|
const { data, error } = await supabase
|
|
|
|
|
.from('community_question')
|
|
|
|
|
.select(
|
|
|
|
|
`
|
|
|
|
|
id,
|
|
|
|
|
title,
|
|
|
|
|
body,
|
|
|
|
|
votes,
|
|
|
|
|
created_at,
|
2024-02-13 22:17:15 +01:00
|
|
|
course_id,
|
2023-12-13 14:03:02 +01:00
|
|
|
slug,
|
2023-08-17 14:05:01 +01:00
|
|
|
comments:community_answer(
|
|
|
|
|
id,
|
|
|
|
|
body,
|
|
|
|
|
votes,
|
|
|
|
|
created_at,
|
2023-12-13 14:03:02 +01:00
|
|
|
author:profile(id, fullname, avatar_url)
|
2023-08-17 14:05:01 +01:00
|
|
|
),
|
2024-02-13 22:17:15 +01:00
|
|
|
author:profile(id, fullname, avatar_url),
|
|
|
|
|
course!inner (
|
|
|
|
|
title
|
|
|
|
|
)
|
2023-08-17 14:05:01 +01:00
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
.eq('slug', slug)
|
|
|
|
|
.single();
|
|
|
|
|
|
|
|
|
|
if (error) {
|
2023-12-13 14:03:02 +01:00
|
|
|
console.error('[LMS] Error loading community', error);
|
|
|
|
|
return goto(`/lms`);
|
2023-08-17 14:05:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
question = mapResToQuestion(data);
|
|
|
|
|
question.totalComments = question.comments.length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function submitComment() {
|
|
|
|
|
errors = commentInCommunityValidation({ comment });
|
|
|
|
|
console.log('submitComment errors', errors);
|
|
|
|
|
|
|
|
|
|
if (Object.keys(errors).length) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { data, error } = await supabase
|
|
|
|
|
.from('community_answer')
|
|
|
|
|
.insert({
|
|
|
|
|
id: undefined,
|
|
|
|
|
body: comment,
|
|
|
|
|
question_id: question.id,
|
2023-12-13 14:03:02 +01:00
|
|
|
author_profile_id: $profile.id,
|
2023-08-17 14:05:01 +01:00
|
|
|
votes: 0
|
|
|
|
|
})
|
|
|
|
|
.select();
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error('Error: commenting', error);
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.error('snackbar.community.error.try_again');
|
2023-08-17 14:05:01 +01:00
|
|
|
} else {
|
|
|
|
|
console.log('Success: commenting', data);
|
|
|
|
|
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.success('snackbar.community.success.comment_submitted');
|
2023-08-17 14:05:01 +01:00
|
|
|
|
|
|
|
|
// Add to comment
|
|
|
|
|
const _c = data?.[0];
|
|
|
|
|
question.comments = [
|
|
|
|
|
...question.comments,
|
|
|
|
|
{
|
|
|
|
|
id: _c.id,
|
2023-12-13 14:03:02 +01:00
|
|
|
authorId: $profile.id || '',
|
2023-08-17 14:05:01 +01:00
|
|
|
name: $profile?.fullname || '',
|
|
|
|
|
avatar: $profile?.avatar_url || '',
|
|
|
|
|
votes: 0,
|
|
|
|
|
comment: _c.body,
|
2023-10-02 04:15:01 +01:00
|
|
|
createdAt: calDateDiff(_c.created_at)
|
2023-08-17 14:05:01 +01:00
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Reset input
|
|
|
|
|
comment = '';
|
|
|
|
|
resetInput = new Date().getTime();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
async function upvoteQuestion(type: string, commentId?: string) {
|
2023-08-17 14:05:01 +01:00
|
|
|
const isQuestion = type === 'question';
|
|
|
|
|
|
|
|
|
|
if (isQuestion && voted.question) return;
|
2023-12-13 14:03:02 +01:00
|
|
|
if (!isQuestion && commentId && voted.comment[commentId]) return;
|
2023-08-17 14:05:01 +01:00
|
|
|
|
|
|
|
|
const table = isQuestion ? 'community_question' : 'community_answer';
|
|
|
|
|
const matchId = isQuestion ? question.id : commentId;
|
|
|
|
|
let votes = 0;
|
|
|
|
|
|
|
|
|
|
if (isQuestion) {
|
|
|
|
|
question.votes = question.votes + 1;
|
|
|
|
|
votes = question.votes;
|
|
|
|
|
} else {
|
|
|
|
|
question.comments = question.comments.map((c) => {
|
|
|
|
|
if (c.id === commentId) {
|
|
|
|
|
c.votes = c.votes + 1;
|
|
|
|
|
votes = c.votes;
|
|
|
|
|
}
|
|
|
|
|
return c;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
const { error } = await supabase.from(table).update({ votes }).match({ id: matchId });
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error('Error: upvoteQuestion', error);
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.error('snackbar.community.error.try_again');
|
2023-08-17 14:05:01 +01:00
|
|
|
} else {
|
|
|
|
|
if (isQuestion) {
|
|
|
|
|
voted.question = true;
|
2023-12-13 14:03:02 +01:00
|
|
|
} else if (commentId) {
|
2023-08-17 14:05:01 +01:00
|
|
|
voted.comment[commentId] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleQuestionEdit() {
|
|
|
|
|
if (isEditMode) {
|
2024-02-13 22:17:15 +01:00
|
|
|
errors = askCommunityValidation(editContent);
|
2023-08-17 14:05:01 +01:00
|
|
|
console.log('handleQuestionEdit errors', errors);
|
|
|
|
|
|
|
|
|
|
if (Object.keys(errors).length) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isEditMode = !isEditMode;
|
2023-09-05 13:21:44 +00:00
|
|
|
editorInstance = !editorInstance;
|
2023-08-17 14:05:01 +01:00
|
|
|
|
|
|
|
|
if (!isEditMode) {
|
2024-02-13 22:17:15 +01:00
|
|
|
errors = askCommunityValidation(editContent);
|
2023-08-17 14:05:01 +01:00
|
|
|
console.log('handleQuestionEdit errors', errors);
|
|
|
|
|
|
|
|
|
|
if (Object.keys(errors).length) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const { error } = await supabase
|
|
|
|
|
.from('community_question')
|
2024-02-13 22:17:15 +01:00
|
|
|
.update({
|
2024-04-19 05:45:48 +01:00
|
|
|
title: editContent.title,
|
|
|
|
|
body: editContent.body,
|
2024-02-13 22:17:15 +01:00
|
|
|
course_id: editContent.courseId
|
|
|
|
|
})
|
2023-08-17 14:05:01 +01:00
|
|
|
.match({ id: question.id });
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error('Error: handleQuestionEdit', error);
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.error('snackbar.community.error.try_again');
|
2023-08-17 14:05:01 +01:00
|
|
|
} else {
|
2024-02-13 22:17:15 +01:00
|
|
|
question.title = editContent.title;
|
|
|
|
|
question.body = editContent.body;
|
|
|
|
|
question.courseId = editContent.courseId;
|
2023-08-17 14:05:01 +01:00
|
|
|
|
|
|
|
|
editContent.title = '';
|
|
|
|
|
editContent.body = '';
|
2024-02-13 22:17:15 +01:00
|
|
|
editContent.courseId = '';
|
2023-08-17 14:05:01 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
editContent.title = question.title;
|
|
|
|
|
editContent.body = question.body;
|
2024-02-13 22:17:15 +01:00
|
|
|
editContent.courseId = question.courseId;
|
2023-08-17 14:05:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
async function handleDelete(isQuestion: boolean) {
|
|
|
|
|
if (!isQuestion) {
|
2023-12-13 14:28:10 +03:00
|
|
|
deleteComment.isDeleting = true;
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
const { error } = await supabase
|
|
|
|
|
.from('community_answer')
|
|
|
|
|
.delete()
|
|
|
|
|
.match({ id: deleteComment.commentId });
|
|
|
|
|
|
2023-12-13 14:28:10 +03:00
|
|
|
deleteComment.isDeleting = false;
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
if (error) {
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.error('snackbar.community.error.deleting_comments');
|
2023-12-13 14:03:02 +01:00
|
|
|
console.log('Error deleting comments', error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.success('snackbar.community.success.success_delete');
|
2023-12-13 14:03:02 +01:00
|
|
|
|
|
|
|
|
question.comments = question.comments.filter((c) => c.id !== deleteComment.commentId);
|
|
|
|
|
deleteComment.shouldDelete = false;
|
|
|
|
|
deleteComment.commentId = '';
|
|
|
|
|
|
|
|
|
|
// Handle only delete comment
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-12-13 14:28:10 +03:00
|
|
|
deleteQuestion.isDeleting = true;
|
2023-12-13 14:03:02 +01:00
|
|
|
|
|
|
|
|
const { error: commentDeleteError } = await supabase
|
2023-08-17 14:05:01 +01:00
|
|
|
.from('community_answer')
|
|
|
|
|
.delete()
|
2023-12-13 14:03:02 +01:00
|
|
|
.match({ question_id: deleteQuestion.questionId });
|
2023-08-17 14:05:01 +01:00
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
if (commentDeleteError) {
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.error('snackbar.community.error.deleting_comments');
|
2023-12-13 14:03:02 +01:00
|
|
|
console.log('Error deleting comments', commentDeleteError);
|
2023-12-13 14:28:10 +03:00
|
|
|
|
|
|
|
|
deleteQuestion.isDeleting = false;
|
2023-12-13 14:03:02 +01:00
|
|
|
return;
|
2023-08-17 14:05:01 +01:00
|
|
|
}
|
2023-12-13 14:03:02 +01:00
|
|
|
|
|
|
|
|
const { error: questionDeleteError } = await supabase
|
|
|
|
|
.from('community_question')
|
|
|
|
|
.delete()
|
|
|
|
|
.match({ id: deleteQuestion.questionId });
|
|
|
|
|
|
|
|
|
|
if (questionDeleteError) {
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.error('snackbar.community.error.deleting_question');
|
2023-12-13 14:03:02 +01:00
|
|
|
console.log('Error deleting question', questionDeleteError);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-19 05:45:48 +01:00
|
|
|
snackbar.success('snackbar.community.success.success_delete');
|
2023-12-13 14:03:02 +01:00
|
|
|
goto(`/lms/community`);
|
2023-12-13 14:28:10 +03:00
|
|
|
deleteQuestion.isDeleting = false;
|
2023-08-17 14:05:01 +01:00
|
|
|
}
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
$: browser && fetchCommunityQuestion(slug);
|
2024-02-13 22:17:15 +01:00
|
|
|
$: {
|
|
|
|
|
if ($profile.id && $currentOrg.id) {
|
|
|
|
|
getCourses($profile.id, $currentOrg.id);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-17 14:05:01 +01:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<svelte:head>
|
|
|
|
|
<title>{question?.title || 'Question'}</title>
|
|
|
|
|
</svelte:head>
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
<DeleteModal
|
|
|
|
|
bind:open={deleteQuestion.shouldDelete}
|
2023-12-13 14:28:10 +03:00
|
|
|
bind:isDeleting={deleteQuestion.isDeleting}
|
2023-12-13 14:03:02 +01:00
|
|
|
onCancel={() => {
|
|
|
|
|
deleteQuestion.shouldDelete = false;
|
|
|
|
|
deleteQuestion.questionId = '';
|
|
|
|
|
}}
|
|
|
|
|
onDelete={() => handleDelete(true)}
|
|
|
|
|
isQuestion={true}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<DeleteModal
|
2023-08-17 14:05:01 +01:00
|
|
|
bind:open={deleteComment.shouldDelete}
|
2023-12-13 14:28:10 +03:00
|
|
|
isDeleting={deleteComment.isDeleting}
|
2023-08-17 14:05:01 +01:00
|
|
|
onCancel={() => {
|
|
|
|
|
deleteComment.shouldDelete = false;
|
2023-12-13 14:03:02 +01:00
|
|
|
deleteComment.commentId = '';
|
2023-08-17 14:05:01 +01:00
|
|
|
}}
|
2023-12-13 14:03:02 +01:00
|
|
|
onDelete={() => handleDelete(false)}
|
2023-08-17 14:05:01 +01:00
|
|
|
/>
|
|
|
|
|
<section class="max-w-3xl mx-auto md:mx-10 lg:mb-20">
|
|
|
|
|
{#if !question}
|
|
|
|
|
<div class="py-10 px-5 mb-3">
|
|
|
|
|
<SkeletonText style="width: 25%;" />
|
|
|
|
|
<SkeletonText style="width: 100%; margin-bottom: 2rem" />
|
|
|
|
|
<SkeletonPlaceholder style="width: 100%; height: 20rem;" />
|
|
|
|
|
</div>
|
|
|
|
|
{:else}
|
|
|
|
|
<div class="py-10 px-5">
|
|
|
|
|
<a class="text-gray-500 dark:text-white text-md flex items-center" href={`/lms/community`}>
|
|
|
|
|
<ArrowLeftIcon size={24} class="carbon-icon dark:text-white" /> Go Back
|
|
|
|
|
</a>
|
|
|
|
|
<div class="my-5 flex justify-between items-center">
|
|
|
|
|
{#if isEditMode}
|
|
|
|
|
<TextField
|
|
|
|
|
bind:value={editContent.title}
|
|
|
|
|
className="w-full mr-2"
|
|
|
|
|
errorMessage={errors.title}
|
|
|
|
|
/>
|
2024-02-13 22:17:15 +01:00
|
|
|
<Dropdown
|
2024-04-19 05:45:48 +01:00
|
|
|
class="w-[25%] h-full"
|
2024-02-13 22:17:15 +01:00
|
|
|
size="xl"
|
|
|
|
|
label="Select Course"
|
|
|
|
|
items={fetchedCourses.map((course) => ({ id: course.id, text: course.title }))}
|
|
|
|
|
bind:selectedId={editContent.courseId}
|
|
|
|
|
/>
|
2023-08-17 14:05:01 +01:00
|
|
|
{:else}
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<Vote
|
|
|
|
|
value={question.votes}
|
|
|
|
|
upVote={() => upvoteQuestion('question')}
|
|
|
|
|
disabled={voted.question}
|
|
|
|
|
/>
|
|
|
|
|
<h2 class="text-3xl">{question.title}</h2>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if question.author.id === $profile.id}
|
|
|
|
|
<PrimaryButton
|
|
|
|
|
label={isEditMode ? 'Save' : 'Edit'}
|
|
|
|
|
variant={VARIANTS.OUTLINED}
|
|
|
|
|
onClick={handleQuestionEdit}
|
|
|
|
|
className="h-fit"
|
|
|
|
|
/>
|
|
|
|
|
{#if isEditMode}
|
|
|
|
|
<PrimaryButton
|
|
|
|
|
label="Cancel"
|
|
|
|
|
variant={VARIANTS.TEXT}
|
|
|
|
|
onClick={() => (isEditMode = !isEditMode)}
|
|
|
|
|
className="py-3 px-6 rounded-sm h-fit"
|
|
|
|
|
disablePadding={true}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="my-1 px-1 rounded-lg border border-1 border-gray">
|
|
|
|
|
<header class="flex items-center justify-between leading-none p-2">
|
|
|
|
|
<div class="flex items-center no-underline hover:underline text-black">
|
|
|
|
|
<Avatar
|
|
|
|
|
src={question.author.avatar}
|
|
|
|
|
name={question.author.name}
|
|
|
|
|
width="w-7"
|
|
|
|
|
height="h-7"
|
|
|
|
|
/>
|
|
|
|
|
<p class="dark:text-white ml-2 text-sm">{question.author.name}</p>
|
|
|
|
|
<p class="dark:text-white ml-2 text-sm text-gray-500">
|
|
|
|
|
{question.createdAt}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2023-12-13 14:03:02 +01:00
|
|
|
{#if question.author.id === $profile.id || $isOrgAdmin}
|
|
|
|
|
<IconButton
|
|
|
|
|
value="delete-question"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
deleteQuestion.shouldDelete = true;
|
|
|
|
|
deleteQuestion.questionId = question.id;
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<TrashCanIcon size={16} class="carbon-icon dark:text-white" />
|
|
|
|
|
</IconButton>
|
|
|
|
|
{/if}
|
2023-08-17 14:05:01 +01:00
|
|
|
</header>
|
2023-09-05 13:21:44 +00:00
|
|
|
{#if isEditMode && editorInstance}
|
2023-08-17 14:05:01 +01:00
|
|
|
<div class="my-2">
|
|
|
|
|
<TextEditor
|
2023-09-05 13:21:44 +00:00
|
|
|
bind:value={editContent.body}
|
|
|
|
|
placeholder="Give an answer"
|
|
|
|
|
onChange={(html) => (editContent.body = html)}
|
2023-08-17 14:05:01 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{:else}
|
|
|
|
|
<section class="prose prose-sm sm:prose p-2">
|
2023-10-08 15:34:37 +01:00
|
|
|
{@html question.body}
|
2023-08-17 14:05:01 +01:00
|
|
|
</section>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="my-8 font-bold">
|
|
|
|
|
{pluralize('answers', question.totalComments, true)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{#each question.comments as comment}
|
|
|
|
|
<div class="my-5 px-1 flex items-start">
|
|
|
|
|
<Vote
|
|
|
|
|
value={comment.votes}
|
|
|
|
|
upVote={() => upvoteQuestion('comment', comment.id)}
|
|
|
|
|
disabled={voted.comment[comment.id]}
|
|
|
|
|
/>
|
|
|
|
|
<div class="w-full rounded-lg border border-1 border-gray">
|
|
|
|
|
<header class="flex items-center justify-between leading-none p-2">
|
|
|
|
|
<div class="flex items-center text-black">
|
|
|
|
|
<Avatar src={comment.avatar} name={comment.name} width="w-7" height="h-7" />
|
|
|
|
|
<p class="dark:text-white ml-2 text-sm">{comment.name}</p>
|
|
|
|
|
<p class="dark:text-white ml-2 text-sm text-gray-500">
|
|
|
|
|
{comment.createdAt}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{#if isValidAnswer}
|
|
|
|
|
<CheckmarkOutlineIcon size={20} />
|
|
|
|
|
{/if}
|
|
|
|
|
|
2023-12-13 14:03:02 +01:00
|
|
|
{#if comment.authorId === $profile.id || $isOrgAdmin}
|
2023-08-17 14:05:01 +01:00
|
|
|
<IconButton
|
|
|
|
|
value="delete-comment"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
deleteComment.shouldDelete = true;
|
|
|
|
|
deleteComment.commentId = comment.id;
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<TrashCanIcon size={16} class="carbon-icon dark:text-white" />
|
|
|
|
|
</IconButton>
|
|
|
|
|
{/if}
|
|
|
|
|
</header>
|
|
|
|
|
<article class="prose prose-sm sm:prose p-2">
|
2023-10-08 15:34:37 +01:00
|
|
|
{@html comment.comment}
|
2023-08-17 14:05:01 +01:00
|
|
|
</article>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
|
|
|
|
|
<hr />
|
|
|
|
|
|
|
|
|
|
<div>
|
2023-09-05 13:21:44 +00:00
|
|
|
{#if !editorInstance}
|
|
|
|
|
<TextEditor
|
|
|
|
|
bind:value={comment}
|
|
|
|
|
placeholder="Give an answer"
|
|
|
|
|
onChange={(html) => (comment = html)}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
2023-08-17 14:05:01 +01:00
|
|
|
|
|
|
|
|
<div class="flex justify-end mr-2">
|
|
|
|
|
<PrimaryButton label="Comment" onClick={submitComment} />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
</section>
|