UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

935 lines (894 loc) 35.2 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _chunkKYUUQXB7js = require('./chunk-KYUUQXB7.js'); var _chunk23TTOILXjs = require('./chunk-23TTOILX.js'); var _chunkHZE6VBF5js = require('./chunk-HZE6VBF5.js'); var _chunkSZD7L65Qjs = require('./chunk-SZD7L65Q.js'); var _chunkJR4KWECXjs = require('./chunk-JR4KWECX.js'); var _chunkNEDUOGWUjs = require('./chunk-NEDUOGWU.js'); var _chunkLGPTKTMPjs = require('./chunk-LGPTKTMP.js'); var _chunk76VXVNA2js = require('./chunk-76VXVNA2.js'); var _chunkTCLRDUMFjs = require('./chunk-TCLRDUMF.js'); var _chunk34ST3MKOjs = require('./chunk-34ST3MKO.js'); var _chunkANT66KVKjs = require('./chunk-ANT66KVK.js'); var _chunkTN3AYOMVjs = require('./chunk-TN3AYOMV.js'); // src/components/Forum/Forum.tsx var _react = require('react'); var _react3 = require('@phosphor-icons/react'); var _jsxruntime = require('react/jsx-runtime'); var MONTHS_PT = [ "Janeiro", "Fevereiro", "Mar\xE7o", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" ]; function formatForumDate(isoDate) { const d = new Date(isoDate); const day = d.getDate(); const month = MONTHS_PT[d.getMonth()]; const year = d.getFullYear(); const hours = d.getHours(); const minutes = d.getMinutes().toString().padStart(2, "0"); return `${day} ${month} ${year} \u2022 ${hours}:${minutes}`; } function getInitials(name) { return name.split(" ").slice(0, 2).map((word) => _nullishCoalesce(word[0], () => ( ""))).join("").toUpperCase(); } function formatReplyCount(count) { if (count === 1) return "1 resposta"; return `${count} respostas`; } function AuthorAvatar({ name, photoUrl }) { if (photoUrl) { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { src: photoUrl, alt: name, className: "w-8 h-8 rounded-full object-cover shrink-0" } ); } return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-8 h-8 rounded-full bg-primary-600 flex items-center justify-center shrink-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "xs", weight: "semibold", color: "text-white", children: getInitials(name) }) }); } function AuthorMeta({ authorName, authorPhoto, createdAt }) { return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AuthorAvatar, { name: authorName, photoUrl: authorPhoto }), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-1", children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkANT66KVKjs.Text_default, { size: "xs", weight: "semibold", color: "text-primary-700", children: [ "Postado por ", authorName ] }), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkANT66KVKjs.Text_default, { size: "xs", color: "text-text-600", children: [ "\u2022 ", formatForumDate(createdAt) ] }) ] }) ] }); } function PostActionsMenu({ onEdit, onDelete }) { return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk76VXVNA2js.DropdownMenu_default, { children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk76VXVNA2js.DropdownMenuTrigger, { "aria-label": "Abrir a\xE7\xF5es do post", className: "p-1 rounded hover:bg-background-100 transition-colors shrink-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.DotsThreeVerticalIcon, { size: 20, className: "text-text-600" }) } ), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk76VXVNA2js.DropdownMenuContent, { align: "end", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk76VXVNA2js.DropdownMenuItem, { iconLeft: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.PencilSimpleIcon, { size: 16 }), onClick: onEdit, children: "Editar" } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk76VXVNA2js.DropdownMenuItem, { iconLeft: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.TrashIcon, { size: 16 }), onClick: onDelete, children: "Deletar" }) ] }) ] }); } function PostContentModal({ isOpen, title, submitLabel, content, imageUrl, isSubmitting, onContentChange, onClose, onSubmit, onUploadImage, onImageUploaded, maxLength, richText = false, showGradeQuestion = false, countsForGrade, onCountsForGradeChange }) { const handleImageSelect = _react.useCallback.call(void 0, async (file) => { if (!onUploadImage) return; try { const url = await onUploadImage(file); onImageUploaded(url); } catch (e2) { } }, [onUploadImage, onImageUploaded] ); const plainTextContent = richText ? _chunkHZE6VBF5js.stripHtml.call(void 0, content) : content; const isContentEmpty = !plainTextContent.trim(); const exceedsMaxLength = plainTextContent.trim().length > maxLength; return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTCLRDUMFjs.Modal_default, { isOpen, onClose, title, size: "md", footer: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex justify-end gap-2", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34ST3MKOjs.Button_default, { variant: "outline", action: "secondary", size: "medium", onClick: onClose, children: "Cancelar" } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34ST3MKOjs.Button_default, { variant: "solid", action: "primary", size: "medium", disabled: isContentEmpty || exceedsMaxLength || isSubmitting, onClick: onSubmit, children: submitLabel } ) ] }), children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-3", children: [ richText ? ( // key forces remount (and thus reset) whenever the modal opens/closes /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkKYUUQXB7js.RichEditor, { content, onChange: (data) => onContentChange(data.html), placeholder: "Escreva o conte\xFAdo do seu post aqui." }, String(isOpen) ) ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkSZD7L65Qjs.TextArea_default, { placeholder: "Escreva o conte\xFAdo do seu post aqui.", value: content, onChange: (e) => onContentChange(e.target.value), autoResize: true, minHeight: 120, maxLength } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk23TTOILXjs.ImageUpload, { variant: "compact", onFileSelect: handleImageSelect, onRemoveFile: () => onImageUploaded(""), buttonIcon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.ImageIcon, { size: 16 }) } ), imageUrl && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { src: imageUrl, alt: "Preview da imagem", className: "max-h-40 rounded-lg object-contain" } ), showGradeQuestion && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-2", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", weight: "medium", children: "Este f\xF3rum vale nota no boletim?" }), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-4", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJR4KWECXjs.Radio_default, { name: "countsForGrade", value: "yes", label: "Sim", size: "small", checked: countsForGrade === true, onChange: () => _optionalChain([onCountsForGradeChange, 'optionalCall', _ => _(true)]) } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJR4KWECXjs.Radio_default, { name: "countsForGrade", value: "no", label: "N\xE3o", size: "small", checked: countsForGrade === false, onChange: () => _optionalChain([onCountsForGradeChange, 'optionalCall', _2 => _2(false)]) } ) ] }) ] }) ] }) } ); } function TopicListSkeleton() { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkNEDUOGWUjs.SkeletonList, { items: 3, showAvatar: true, showTitle: true, showDescription: true, lines: 2 }); } function TopicDetailSkeleton() { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkNEDUOGWUjs.SkeletonCard, { showAvatar: true, showTitle: true, showDescription: true, showActions: false, lines: 2 } ); } function Forum({ apiClient, currentUserId, userRole = "STUDENT" /* STUDENT */, title = "F\xF3rum", subtitle = "Espa\xE7o para troca de conhecimento", onUploadImage, onEvaluateReply, className }) { const isTeacher = userRole === "TEACHER" /* TEACHER */; const [view, setView] = _react.useState.call(void 0, "list"); const [topics, setTopics] = _react.useState.call(void 0, []); const [isLoadingTopics, setIsLoadingTopics] = _react.useState.call(void 0, false); const [selectedTopic, setSelectedTopic] = _react.useState.call(void 0, null); const [replies, setReplies] = _react.useState.call(void 0, []); const [isLoadingTopic, setIsLoadingTopic] = _react.useState.call(void 0, false); const [isSubmitting, setIsSubmitting] = _react.useState.call(void 0, false); const [isCreateModalOpen, setIsCreateModalOpen] = _react.useState.call(void 0, false); const [createContent, setCreateContent] = _react.useState.call(void 0, ""); const [createImageUrl, setCreateImageUrl] = _react.useState.call(void 0, ); const [createCountsForGrade, setCreateCountsForGrade] = _react.useState.call(void 0, ); const [isReplyModalOpen, setIsReplyModalOpen] = _react.useState.call(void 0, false); const [replyContent, setReplyContent] = _react.useState.call(void 0, ""); const [replyImageUrl, setReplyImageUrl] = _react.useState.call(void 0, ); const [editingTopic, setEditingTopic] = _react.useState.call(void 0, null); const [editTopicContent, setEditTopicContent] = _react.useState.call(void 0, ""); const [editTopicImageUrl, setEditTopicImageUrl] = _react.useState.call(void 0, ); const [editingReply, setEditingReply] = _react.useState.call(void 0, null); const [editReplyContent, setEditReplyContent] = _react.useState.call(void 0, ""); const [editReplyImageUrl, setEditReplyImageUrl] = _react.useState.call(void 0, ); const [evaluatingReplyId, setEvaluatingReplyId] = _react.useState.call(void 0, null ); const [gradeValue, setGradeValue] = _react.useState.call(void 0, null); const [isEditingGrade, setIsEditingGrade] = _react.useState.call(void 0, false); const [pendingDelete, setPendingDelete] = _react.useState.call(void 0, null); const fetchTopics = _react.useCallback.call(void 0, async () => { setIsLoadingTopics(true); try { const response = await apiClient.getTopics({ limit: 50 }); setTopics(response.topics); } finally { setIsLoadingTopics(false); } }, [apiClient]); const fetchTopic = _react.useCallback.call(void 0, async (topicId) => { setIsLoadingTopic(true); try { const response = await apiClient.getTopic(topicId); setSelectedTopic(response.topic); setReplies(response.replies); } finally { setIsLoadingTopic(false); } }, [apiClient] ); _react.useEffect.call(void 0, () => { fetchTopics(); }, [fetchTopics]); const handleTopicClick = _react.useCallback.call(void 0, (topic) => { setSelectedTopic(topic); setView("detail"); fetchTopic(topic.id); }, [fetchTopic] ); const handleBack = _react.useCallback.call(void 0, () => { setView("list"); setSelectedTopic(null); setReplies([]); fetchTopics(); }, [fetchTopics]); const handleCloseCreateModal = _react.useCallback.call(void 0, () => { setIsCreateModalOpen(false); setCreateContent(""); setCreateImageUrl(void 0); setCreateCountsForGrade(void 0); }, []); const handleCreateTopic = _react.useCallback.call(void 0, async () => { if (!createContent.trim()) return; setIsSubmitting(true); try { await apiClient.createTopic({ content: createContent.trim(), ...createImageUrl && { imageUrl: createImageUrl }, ...isTeacher && createCountsForGrade !== void 0 && { countsForGrade: createCountsForGrade } }); handleCloseCreateModal(); await fetchTopics(); } finally { setIsSubmitting(false); } }, [ apiClient, createContent, createImageUrl, createCountsForGrade, isTeacher, handleCloseCreateModal, fetchTopics ]); const handleOpenEditTopic = _react.useCallback.call(void 0, (topic) => { setEditingTopic(topic); setEditTopicContent(topic.content); setEditTopicImageUrl(_nullishCoalesce(topic.imageUrl, () => ( void 0))); }, []); const handleCloseEditTopic = _react.useCallback.call(void 0, () => { setEditingTopic(null); setEditTopicContent(""); setEditTopicImageUrl(void 0); }, []); const handleEditTopic = _react.useCallback.call(void 0, async () => { if (!editingTopic || !editTopicContent.trim()) return; setIsSubmitting(true); try { await apiClient.updateTopic(editingTopic.id, { content: editTopicContent.trim(), imageUrl: _nullishCoalesce(editTopicImageUrl, () => ( null)) }); handleCloseEditTopic(); if (view === "list") { await fetchTopics(); } else { await fetchTopic(editingTopic.id); } } finally { setIsSubmitting(false); } }, [ apiClient, editingTopic, editTopicContent, editTopicImageUrl, handleCloseEditTopic, view, fetchTopics, fetchTopic ]); const handleDeleteTopic = _react.useCallback.call(void 0, async (topicId) => { setIsSubmitting(true); try { await apiClient.deleteTopic(topicId); if (view === "detail") { handleBack(); } else { await fetchTopics(); } } finally { setIsSubmitting(false); setPendingDelete(null); } }, [apiClient, view, handleBack, fetchTopics] ); const handleCloseReplyModal = _react.useCallback.call(void 0, () => { setIsReplyModalOpen(false); setReplyContent(""); setReplyImageUrl(void 0); }, []); const handleCreateReply = _react.useCallback.call(void 0, async () => { if (!selectedTopic || !replyContent.trim()) return; setIsSubmitting(true); try { await apiClient.createReply(selectedTopic.id, { content: replyContent.trim(), ...replyImageUrl && { imageUrl: replyImageUrl } }); handleCloseReplyModal(); await fetchTopic(selectedTopic.id); } finally { setIsSubmitting(false); } }, [ apiClient, selectedTopic, replyContent, replyImageUrl, handleCloseReplyModal, fetchTopic ]); const handleOpenEditReply = _react.useCallback.call(void 0, (reply) => { setEditingReply(reply); setEditReplyContent(reply.content); setEditReplyImageUrl(_nullishCoalesce(reply.imageUrl, () => ( void 0))); }, []); const handleCloseEditReply = _react.useCallback.call(void 0, () => { setEditingReply(null); setEditReplyContent(""); setEditReplyImageUrl(void 0); }, []); const handleEditReply = _react.useCallback.call(void 0, async () => { if (!editingReply || !editReplyContent.trim() || !selectedTopic) return; setIsSubmitting(true); try { await apiClient.updateReply(editingReply.id, { content: editReplyContent.trim(), imageUrl: _nullishCoalesce(editReplyImageUrl, () => ( null)) }); handleCloseEditReply(); await fetchTopic(selectedTopic.id); } finally { setIsSubmitting(false); } }, [ apiClient, editingReply, editReplyContent, editReplyImageUrl, handleCloseEditReply, selectedTopic, fetchTopic ]); const handleDeleteReply = _react.useCallback.call(void 0, async (replyId) => { if (!selectedTopic) return; setIsSubmitting(true); try { await apiClient.deleteReply(replyId); await fetchTopic(selectedTopic.id); } finally { setIsSubmitting(false); setPendingDelete(null); } }, [apiClient, selectedTopic, fetchTopic] ); const handleCloseEvaluateModal = _react.useCallback.call(void 0, () => { setEvaluatingReplyId(null); setGradeValue(null); setIsEditingGrade(false); }, []); const handleOpenEvaluate = _react.useCallback.call(void 0, (reply) => { setEvaluatingReplyId(reply.id); setGradeValue(null); setIsEditingGrade(false); }, []); const handleOpenEditGrade = _react.useCallback.call(void 0, (reply) => { setEvaluatingReplyId(reply.id); setGradeValue(_nullishCoalesce(reply.grade, () => ( null))); setIsEditingGrade(true); }, []); const isValidGrade = gradeValue !== null && Number.isInteger(gradeValue) && gradeValue >= 0 && gradeValue <= 10; const handleSubmitEvaluation = _react.useCallback.call(void 0, async () => { if (!evaluatingReplyId || !isValidGrade || !onEvaluateReply) return; setIsSubmitting(true); try { await onEvaluateReply(evaluatingReplyId, gradeValue); setReplies( (prev) => prev.map( (r) => r.id === evaluatingReplyId ? { ...r, grade: gradeValue } : r ) ); handleCloseEvaluateModal(); } finally { setIsSubmitting(false); } }, [ evaluatingReplyId, gradeValue, isValidGrade, onEvaluateReply, handleCloseEvaluateModal ]); const handleConfirmDelete = _react.useCallback.call(void 0, async () => { if (!pendingDelete) return; if (pendingDelete.type === "topic") { await handleDeleteTopic(pendingDelete.id); } else { await handleDeleteReply(pendingDelete.id); } }, [pendingDelete, handleDeleteTopic, handleDeleteReply]); const topicListContent = (() => { if (isLoadingTopics) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TopicListSkeleton, {}); if (topics.length === 0) { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex items-center justify-center py-16 border border-border-200 rounded-lg", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "md", color: "text-text-500", children: "Nenhum t\xF3pico ainda. Seja o primeiro a criar um post!" }) }); } return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-col gap-4", children: topics.map((topic) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-start bg-white border border-border-100 rounded-lg pt-4 pr-2 pb-4 pl-2 shadow-sm", children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { type: "button", className: "flex flex-col gap-3 flex-1 min-w-0 text-left hover:opacity-90 transition-opacity", onClick: () => handleTopicClick(topic), children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AuthorMeta, { authorName: topic.authorName, authorPhoto: topic.authorPhoto, createdAt: topic.createdAt } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", className: "leading-normal line-clamp-3 ml-10", children: _chunkHZE6VBF5js.stripHtml.call(void 0, topic.content) }), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-1 text-text-600 ml-10", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.ChatCircleTextIcon, { size: 16 }), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "xs", children: formatReplyCount(topic.replyCount) }) ] }) ] } ), topic.userInstitutionId === currentUserId && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, PostActionsMenu, { onEdit: () => handleOpenEditTopic(topic), onDelete: () => setPendingDelete({ type: "topic", id: topic.id }) } ) ] }, topic.id )) }); })(); const topicDetailContent = (() => { if (isLoadingTopic) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TopicDetailSkeleton, {}); if (!selectedTopic) return null; return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-start gap-2", children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-3 flex-1 min-w-0", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AuthorMeta, { authorName: selectedTopic.authorName, authorPhoto: selectedTopic.authorPhoto, createdAt: selectedTopic.createdAt } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "text-sm text-text-950 leading-normal ml-10 prose prose-sm max-w-none", dangerouslySetInnerHTML: { __html: _chunkHZE6VBF5js.sanitizeHtmlForDisplay.call(void 0, selectedTopic.content) } } ), selectedTopic.imageUrl && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { src: selectedTopic.imageUrl, alt: "Imagem do post", className: "ml-10 max-w-lg rounded-lg" } ) ] }), selectedTopic.userInstitutionId === currentUserId && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, PostActionsMenu, { onEdit: () => handleOpenEditTopic(selectedTopic), onDelete: () => setPendingDelete({ type: "topic", id: selectedTopic.id }) } ) ] }), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", color: "text-text-600", children: formatReplyCount(selectedTopic.replyCount) }), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34ST3MKOjs.Button_default, { variant: "solid", action: "primary", size: "medium", onClick: () => setIsReplyModalOpen(true), children: "Responder" } ) ] }), replies.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-col divide-y divide-border-200", children: replies.map((reply) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-start gap-2 py-4", children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-3 flex-1 min-w-0", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AuthorMeta, { authorName: reply.authorName, authorPhoto: reply.authorPhoto, createdAt: reply.createdAt } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "text-sm text-text-950 leading-normal ml-10 prose prose-sm max-w-none", dangerouslySetInnerHTML: { __html: _chunkHZE6VBF5js.sanitizeHtmlForDisplay.call(void 0, reply.content) } } ), reply.imageUrl && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { src: reply.imageUrl, alt: "Imagem da resposta", className: "ml-10 max-w-lg rounded-lg" } ), isTeacher && _optionalChain([selectedTopic, 'optionalAccess', _3 => _3.countsForGrade]) && onEvaluateReply && (reply.grade == null ? ( /* Not yet graded — show Avaliar button */ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { type: "button", className: "flex items-center gap-1.5 text-text-600 text-sm hover:text-text-950 transition-colors w-fit ml-10", onClick: () => handleOpenEvaluate(reply), children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.CheckIcon, { size: 16 }), "Avaliar" ] } ) ) : ( /* Grade already set — teacher sees it with edit pencil */ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-1.5 ml-10", children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", color: "text-text-600", children: [ "Nota ", reply.grade ] }), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "text-text-600 hover:text-text-950 transition-colors", onClick: () => handleOpenEditGrade(reply), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.PencilSimpleIcon, { size: 14 }) } ) ] }) )), !isTeacher && reply.grade != null && reply.userInstitutionId === currentUserId && _optionalChain([selectedTopic, 'optionalAccess', _4 => _4.countsForGrade]) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", color: "text-text-600", className: "ml-10", children: [ "Nota ", reply.grade ] }) ] }), reply.userInstitutionId === currentUserId && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, PostActionsMenu, { onEdit: () => handleOpenEditReply(reply), onDelete: () => setPendingDelete({ type: "reply", id: reply.id }) } ) ] }, reply.id)) }) ] }); })(); return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkTN3AYOMVjs.cn.call(void 0, "flex flex-col gap-4", className), children: [ view === "list" ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { as: "h1", size: "2xl", weight: "bold", color: "text-text-950", children: title }), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34ST3MKOjs.Button_default, { variant: "solid", action: "primary", size: "medium", onClick: () => setIsCreateModalOpen(true), children: "Criar post" } ) ] }), /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-3", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { as: "h2", size: "lg", weight: "semibold", color: "text-text-950", children: subtitle }), topicListContent ] }) ] }) : ( /* Detail view */ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { type: "button", className: "flex items-center gap-2 hover:opacity-80 transition-opacity w-fit", onClick: handleBack, children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.CaretLeftIcon, { size: 24, className: "text-text-950" }), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "2xl", weight: "bold", children: "Voltar" }) ] } ), topicDetailContent ] }) ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, PostContentModal, { isOpen: isCreateModalOpen, title: "Criar post", submitLabel: "Criar post", content: createContent, imageUrl: createImageUrl, isSubmitting, onContentChange: setCreateContent, onClose: handleCloseCreateModal, onSubmit: handleCreateTopic, onUploadImage, onImageUploaded: setCreateImageUrl, maxLength: 1e4, richText: true, showGradeQuestion: isTeacher, countsForGrade: createCountsForGrade, onCountsForGradeChange: setCreateCountsForGrade } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, PostContentModal, { isOpen: isReplyModalOpen, title: "Responder", submitLabel: "Responder", content: replyContent, imageUrl: replyImageUrl, isSubmitting, onContentChange: setReplyContent, onClose: handleCloseReplyModal, onSubmit: handleCreateReply, onUploadImage, onImageUploaded: setReplyImageUrl, maxLength: 5e3, richText: true } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, PostContentModal, { isOpen: !!editingTopic, title: "Editar", submitLabel: "Salvar", content: editTopicContent, imageUrl: editTopicImageUrl, isSubmitting, onContentChange: setEditTopicContent, onClose: handleCloseEditTopic, onSubmit: handleEditTopic, onUploadImage, onImageUploaded: setEditTopicImageUrl, maxLength: 1e4, richText: true } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, PostContentModal, { isOpen: !!editingReply, title: "Editar", submitLabel: "Salvar", content: editReplyContent, imageUrl: editReplyImageUrl, isSubmitting, onContentChange: setEditReplyContent, onClose: handleCloseEditReply, onSubmit: handleEditReply, onUploadImage, onImageUploaded: setEditReplyImageUrl, maxLength: 5e3, richText: true } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTCLRDUMFjs.Modal_default, { isOpen: !!evaluatingReplyId, onClose: handleCloseEvaluateModal, title: isEditingGrade ? "Edite a nota da atividade" : "Defina a nota da atividade", size: "md", footer: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex justify-end gap-2", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34ST3MKOjs.Button_default, { variant: "outline", action: "secondary", size: "medium", onClick: handleCloseEvaluateModal, children: "Cancelar" } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34ST3MKOjs.Button_default, { variant: "solid", action: "primary", size: "medium", disabled: !isValidGrade || isSubmitting, onClick: handleSubmitEvaluation, children: isEditingGrade ? "Salvar" : "Avaliar" } ) ] }), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLGPTKTMPjs.Input_default, { type: "number", min: 0, max: 10, step: 1, placeholder: "Nota (0-10)", value: _nullishCoalesce(gradeValue, () => ( "")), onChange: (e) => { const val = e.target.value; const parsed = Number(val); setGradeValue(val === "" || Number.isNaN(parsed) ? null : parsed); } } ) } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTCLRDUMFjs.Modal_default, { isOpen: !!pendingDelete, onClose: () => setPendingDelete(null), title: "Deseja deletar post?", size: "sm", footer: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex justify-end gap-2", children: [ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34ST3MKOjs.Button_default, { variant: "outline", action: "secondary", size: "medium", onClick: () => setPendingDelete(null), children: "Cancelar" } ), /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34ST3MKOjs.Button_default, { variant: "solid", action: "negative", size: "medium", disabled: isSubmitting, onClick: handleConfirmDelete, children: "Deletar" } ) ] }), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "md", color: "text-text-600", children: "Essa a\xE7\xE3o \xE9 permanente e n\xE3o poder\xE1 ser desfeita." }) } ) ] }); } var Forum_default = Forum; exports.Forum = Forum; exports.Forum_default = Forum_default; //# sourceMappingURL=chunk-4INYKH2P.js.map