feat: discussions

This commit is contained in:
Jannat Patel
2024-01-15 23:26:31 +05:30
parent bcee74ce77
commit 3a5977a718
14 changed files with 771 additions and 336 deletions

View File

@@ -0,0 +1,110 @@
<template>
<div class="mt-6">
<div class="flex items-center mb-5">
<Button variant="subtle" @click="showTopics = true">
<ChevronLeft class="w-4 h-4 stroke-1.5" />
</Button>
<span class="text-lg font-semibold ml-2">
{{ topic.title }}
</span>
</div>
<div v-for="(reply, index) in replies.data">
<div
class="py-3"
:class="{ 'border-b': index + 1 != replies.data.length }"
>
<div class="flex items-center mb-2">
<UserAvatar :user="reply.user" class="mr-2" />
<span>
{{ reply.user.full_name }}
</span>
<span class="text-sm ml-2">
{{ timeAgo(reply.creation) }}
</span>
</div>
<div v-html="reply.reply"></div>
</div>
</div>
<TextEditor
:content="newReply"
@change="(val) => (newReply = val)"
placeholder="Type your reply here..."
editorClass="prose-sm py-2 px-2 min-h-[100px] border-gray-300 hover:border-gray-400 rounded-md bg-gray-200 w-full mt-5"
/>
<div class="flex justify-between mt-2">
<span> </span>
<Button @click="postReply()">
<span>
{{ __('Post') }}
</span>
</Button>
</div>
</div>
</template>
<script setup>
import { createResource, TextEditor, Button } from 'frappe-ui'
import { timeAgo } from '../utils'
import UserAvatar from '@/components/UserAvatar.vue'
import { ChevronLeft } from 'lucide-vue-next'
import { ref, inject, onMounted } from 'vue'
const showTopics = defineModel('showTopics')
const newReply = ref('')
const socket = inject('$socket')
const props = defineProps({
topic: {
type: Object,
required: true,
},
})
console.log(socket)
socket.on('publish_message', (data) => {
console.log('publish')
console.log(data)
replies.reload()
})
const replies = createResource({
url: 'lms.lms.utils.get_discussion_replies',
cache: ['replies', props.topic],
makeParams(values) {
return {
topic: props.topic.name,
}
},
auto: true,
})
const newReplyResource = createResource({
url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'Discussion Reply',
reply: newReply.value,
topic: props.topic.name,
},
}
},
})
const postReply = () => {
newReplyResource.submit(
{},
{
validate() {
if (!newReply.value) {
return __('Reply cannot be empty')
}
},
onSuccess() {
newReply.value = ''
replies.reload()
},
}
)
}
</script>

View File

@@ -0,0 +1,84 @@
<template>
<div v-if="topics.data">
<div>
<div class="text-xl font-semibold">
{{ __(title) }}
</div>
</div>
<div v-if="showTopics" v-for="(topic, index) in topics.data">
<div
@click="showReplies(topic)"
class="flex items-center cursor-pointer py-5"
:class="{ 'border-b': index + 1 != topics.data.length }"
>
<UserAvatar :user="topic.user" size="2xl" class="mr-4" />
<div>
<div class="text-lg font-semibold mb-1">
{{ topic.title }}
</div>
<div class="flex items-center">
<span>
{{ topic.user.full_name }}
</span>
<span class="text-sm ml-2">
{{ timeAgo(topic.creation) }}
</span>
</div>
</div>
</div>
</div>
<div v-else>
<DiscussionReplies
:topic="currentTopic"
v-model:showTopics="showTopics"
/>
</div>
</div>
</template>
<script setup>
import { createResource } from 'frappe-ui'
import UserAvatar from '@/components/UserAvatar.vue'
import { timeAgo } from '../utils'
import { ref, onMounted, inject } from 'vue'
import DiscussionReplies from '@/components/DiscussionReplies.vue'
const showTopics = ref(true)
const currentTopic = ref(null)
const socket = inject('$socket')
const props = defineProps({
title: {
type: String,
required: true,
},
doctype: {
type: String,
required: true,
},
docname: {
type: String,
required: true,
},
})
onMounted(() => {
socket.on('new_discussion_topic', (data) => {
topics.refresh()
})
})
const topics = createResource({
url: 'lms.lms.utils.get_discussion_topics',
cache: ['topics', props.doctype, props.docname],
params: {
doctype: props.doctype,
docname: props.docname,
},
auto: true,
})
const showReplies = (topic) => {
showTopics.value = false
currentTopic.value = topic
}
</script>

View File

@@ -3,7 +3,9 @@
<div class="h-full overflow-auto" id="scrollContainer">
<slot />
</div>
{{ tabs }}
<div
v-if="tabs"
class="grid grid-cols-5 border-t border-gray-300 standalone:pb-4"
:style="{ gridTemplateColumns: `repeat(${tabs.length}, minmax(0, 1fr))` }"
>
@@ -13,27 +15,29 @@
class="flex flex-col items-center justify-center py-3 transition active:scale-95"
@click="handleClick(tab)"
>
<component
:is="tab.icon"
class="h-6 w-6"
:class="[isActive(tab) ? 'text-gray-900' : 'text-gray-600']"
/>
{{ tab.label }}
<component :is="tab.icon" class="h-6 w-6" />
</button>
</div>
</div>
</template>
<script>
import { scrollTo } from '@/utils/scrollContainer'
<script setup>
import { getSidebarLinks } from '../utils'
import { useRouter } from 'vue-router'
import { computed } from 'vue'
const router = useRouter()
const tabs = getSidebarLinks()
let isActive = computed((tab) => {
return router.currentRoute.value.name === tab.to
const tabs = computed(() => {
return getSidebarLinks()
})
console.log(tabs.value)
/* let isActive = computed((tab) => {
console.log(tab);
return router.currentRoute.value.name === tab.to
}) */
const handleClick = (tab) => {
router.push({ name: tab.to })
}