Skip to content

Commit

Permalink
Merge pull request #361 from globe-and-citizen/develop
Browse files Browse the repository at this point in the history
Release 1.9
  • Loading branch information
arnonrdp authored Apr 5, 2024
2 parents 3a8350e + 5db0938 commit f2ba8cd
Show file tree
Hide file tree
Showing 35 changed files with 766 additions and 98 deletions.
3 changes: 1 addition & 2 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
"vue.volar",
"gruntfuggly.todo-tree",
"mhutchie.git-graph",
"vue.vscode-typescript-vue-plugin",
"Vue.volar"
"vue.vscode-typescript-vue-plugin"
],
"unwantedRecommendations": ["octref.vetur", "hookyqr.beautify", "dbaeumer.jshint", "ms-vscode.vscode-typescript-tslint-plugin"]
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"workbox-strategies": "^6.5.4"
},
"engines": {
"node": "^18 || ^16 || ^14.19",
"node": "^20 || ^18 || ^16",
"npm": ">= 6.13.4",
"yarn": ">= 1.21.1"
}
Expand Down
128 changes: 128 additions & 0 deletions src/components/Admin/ManageReports.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<template>
<h5 v-if="!reportStore.getReports" class="text-center">There are no reports yet.</h5>
<q-table
v-else
:columns="columns"
data-test="reports-table"
grid
hide-header
:loading="reportStore.isLoading"
:pagination="pagination"
row-key="created"
:rows="reportStore.getReports"
style="left: 0; right: 0"
title="Manage Reports"
>
<template v-slot:item="props">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3 col-xl-2 q-pa-sm">
<q-card class="report">
<q-card-section class="text-center flex justify-between">
<div class="text-body1 text-left self-center" data-test="user-div">
<span class="text-bold">Author:</span>
{{ props.row.author ? props.row.author.displayName : 'Anonymously' }}
</div>
<div
class="text-body1 text-right text-white q-pa-sm q-btn--rounded"
:class="props.row.status === 'New' ? 'bg-positive' : 'bg-negative'"
>
{{ props.row.status }}
</div>
</q-card-section>
<q-separator />
<q-card-section>
<p data-test="report-message" style="white-space: pre-line">
<span class="text-body2 text-bold">Created:</span>
{{ shortMonthDayTime(props.row.created) }}
</p>
<p data-test="report-message" style="white-space: pre-line">
<span class="text-body2 text-bold">Report message:</span>
{{ props.row.reportMessage }}
</p>
<p data-test="report-message" style="white-space: pre-line">
<span class="text-body2 text-bold">Comment text:</span>
{{ props.row.commentText }}
</p>
</q-card-section>
<q-card-actions align="right">
<q-btn
:disable="props.row.status === 'New'"
label="Delete report"
color="primary"
@click="confirmDelete(props.row, 'report')"
/>
<q-btn
:disable="props.row.status === 'Deleted'"
label="Delete comment"
color="primary"
@click="confirmDelete(props.row, 'comment')"
/>
</q-card-actions>
</q-card>
</div>
</template>
</q-table>

<q-dialog v-model="deleteDialog.show">
<q-card>
<q-card-section class="q-pb-none">
<h6 class="q-my-sm">Delete {{ isComment ? 'comment' : 'report' }}?</h6>
</q-card-section>
<q-card-section>Are you sure you want to delete this {{ isComment ? 'comment' : 'report' }}?</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Cancel" color="primary" v-close-popup />
<q-btn data-test="delete-button" flat label="Delete" color="negative" @click="onDeleteReport()" />
</q-card-actions>
</q-card>
</q-dialog>
</template>

<script setup>
import { useQuasar } from 'quasar'
import { useErrorStore, useReportStore } from 'src/stores'
import { shortMonthDayTime } from 'src/utils/date'
import { onMounted, ref } from 'vue'
const $q = useQuasar()
const errorStore = useErrorStore()
const reportStore = useReportStore()
const columns = [
{ name: 'created', align: 'left', label: 'Created At', field: 'created', sortable: true },
{ name: 'message', align: 'left', label: 'Reports', field: 'message', sortable: true },
{ name: 'action', label: 'Action', field: 'action' }
]
const deleteDialog = ref({})
const isComment = ref(false)
const pagination = { sortBy: 'created', descending: true, rowsPerPage: 0 }
onMounted(async () => {
await reportStore.fetchReports()
})
function confirmDelete(report, value) {
if (value == 'comment') {
isComment.value = true
} else {
isComment.value = false
}
deleteDialog.value = report
deleteDialog.value.show = true
}
function onDeleteReport() {
if (isComment.value) {
reportStore
.deleteComment(deleteDialog.value.collectionName, deleteDialog.value.documentId, deleteDialog.value.commentId, deleteDialog.value.id)
.then(() => $q.notify({ color: 'positive', message: 'Comment deleted successfully' }))
.catch((error) => errorStore.throwError(error, 'Failed to delete comment'))
} else {
reportStore
.deleteReport(deleteDialog.value.id)
.then(() => $q.notify({ color: 'positive', message: 'Report deleted successfully' }))
.catch((error) => errorStore.throwError(error, 'Failed to delete report'))
}
deleteDialog.value.show = false
}
</script>

<style scoped></style>
4 changes: 2 additions & 2 deletions src/components/Admin/TableEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
<template v-slot:body-cell-actions="props">
<td class="text-right">
<q-btn
v-if="userStore.getUser.role !== 'Writer' || userStore.getUser.uid === props.row.author.uid"
color="warning"
:disable="userStore.getUser.role === 'Writer' && userStore.getUser.uid !== props.row.author.uid"
flat
icon="edit"
round
size="sm"
@click="onEditDialog(props.row)"
/>
<q-btn
v-if="userStore.getUser.role !== 'Writer' || userStore.getUser.uid === props.row.author.uid"
color="negative"
:disable="userStore.getUser.role === 'Writer' && userStore.getUser.uid !== props.row.author.uid"
data-test="button-delete-entry"
flat
icon="delete"
Expand Down
85 changes: 80 additions & 5 deletions src/components/Posts/Comments/DisplayComment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,35 @@
<q-item-label>{{ comment.author?.displayName || 'Anonymous' }}</q-item-label>
<q-item-label caption>{{ shortMonthDayTime(comment.created) }}</q-item-label>
</q-item-section>
<q-item-section v-if="(comment.author?.uid || comment.author) === userId" side>
<q-item-section v-if="!comment.isDeleted" side>
<q-btn-dropdown color="secondary" :data-test="comment.text + '-option-button'" dense dropdown-icon="more_vert" flat rounded>
<q-list>
<q-item clickable data-test="comment-select-edit" v-close-popup @click="editInput(comment.id)">
<q-item
v-if="(comment.author?.uid || comment.author) === userId"
clickable
data-test="comment-select-edit"
v-close-popup
@click="editInput(comment.id)"
>
<q-item-section>Edit</q-item-section>
</q-item>
<q-item clickable data-test="comment-select-delete" v-close-popup @click="deleteComment(comment.id)">
<q-item
v-if="(comment.author?.uid || comment.author) === userId"
clickable
data-test="comment-select-delete"
v-close-popup
@click="deleteComment(comment.id)"
>
<q-item-section>Delete</q-item-section>
</q-item>
<q-item clickable data-test="comment-select-delete" v-close-popup @click="reportInput">
<q-item-section>Report</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</q-item-section>
</q-item>

<!-- Parent comment editing -->
<q-form v-if="isEditing && comment.id === inputEdit" greedy @submit.prevent="editComment(comment.id, newComment)">
<q-input
Expand Down Expand Up @@ -60,8 +76,31 @@
<span v-else>{{ comment.text }}</span>
</div>

<!-- Parent comment reporting -->
<q-dialog v-model="isReporting">
<q-card style="width: 400px">
<q-card-section class="row items-center q-pb-none">
<div class="text-h6">Report comment</div>
<q-space />
<q-btn icon="close" flat round dense @click="reportInput" />
</q-card-section>
<q-card-section>
<q-option-group :options="reportOptions" type="radio" v-model="reportOption" />
</q-card-section>
<q-card-actions class="q-mb-sm" align="right">
<q-btn label="Cancel" color="primary" @click="reportInput" />
<q-btn
label="Report"
color="positive"
@click="reportComment(comment.id, comment.text, reportOption)"
:disable="reportOption === null"
/>
</q-card-actions>
</q-card>
</q-dialog>

<!-- Parent Like, Dislike, Reply buttons -->
<div class="row">
<div class="row" v-if="!comment.isDeleted">
<q-btn
:data-test="'like' + comment.text"
flat
Expand Down Expand Up @@ -111,12 +150,13 @@
import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { shortMonthDayTime } from 'src/utils/date'
import { useCommentStore, useErrorStore, useUserStore } from 'src/stores'
import { useCommentStore, useErrorStore, useUserStore, useReportStore } from 'src/stores'
import { useQuasar } from 'quasar'
const router = useRouter()
const userStore = useUserStore()
const commentStore = useCommentStore()
const reportStore = useReportStore()
const errorStore = useErrorStore()
const props = defineProps({
Expand All @@ -128,7 +168,19 @@ const props = defineProps({
const userId = ref('')
const inputEdit = ref('')
const isEditing = ref(false)
const isReporting = ref(false)
const newComment = ref(props.comment.text)
const reportOption = ref(null)
const reportOptions = [
{ label: 'Unwanted commercial content or spam', value: 'Unwanted commercial content or spam' },
{ label: 'Pornography or sexually explicit material', value: 'Pornography or sexually explicit material' },
{ label: 'Child abuse', value: 'Child abuse' },
{ label: 'Hate speech or graphic violence', value: 'Hate speech or graphic violence' },
{ label: 'Promotes terrorism', value: 'Promotes terrorism' },
{ label: 'Harassment or bullying', value: 'Harassment or bullying' },
{ label: 'Suicide or self injury', value: 'Suicide or self injury' },
{ label: 'Misinformation', value: 'Misinformation' }
]
const $q = useQuasar()
onMounted(async () => {
Expand All @@ -146,6 +198,24 @@ async function editComment(commentId, editedComment) {
.finally(() => (isEditing.value = false))
}
async function reportComment(commentId, commentText, reportMessage) {
const report = {
collectionName: props.collectionName,
documentId: props.documentId,
commentId: commentId,
commentText: commentText,
reportMessage: reportMessage
}
await reportStore
.addReports(report)
.then(() => $q.notify({ type: 'info', message: 'Comment successfully reported!' }))
.catch((error) => errorStore.throwError(error, 'Failed to report comment'))
.finally(() => (isReporting.value = false))
reportOption.value = null
}
async function deleteComment(commentId) {
await commentStore
.deleteComment(props.collectionName, props.documentId, commentId)
Expand Down Expand Up @@ -188,4 +258,9 @@ function editInput(commentId) {
isEditing.value = !isEditing.value
inputEdit.value = commentId
}
function reportInput() {
isReporting.value = !isReporting.value
reportOption.value = null
}
</script>
26 changes: 23 additions & 3 deletions src/components/Posts/Graphs/LikesBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,23 @@ function groupLikesAndDislikesByDate(data) {
return new Date(dateA) - new Date(dateB)
})
// Generate missing dates between the first provided date and today's date
const uniqueDates = new Set(mergedResult.map((item) => Object.keys(item)[0]))
const today = new Date()
const firstDate = mergedResult[0] ? Object.keys(mergedResult[0])[0] : null
const startDate = firstDate ? new Date(firstDate) : null
for (let currentDate = startDate; currentDate <= today; currentDate.setDate(currentDate.getDate() + 1)) {
const formattedDate = currentDate.toLocaleDateString('en-US')
if (!uniqueDates.has(formattedDate)) {
mergedResult.push({ [formattedDate]: {} })
uniqueDates.add(formattedDate)
}
}
// Sort the array by date
mergedResult.sort((a, b) => new Date(Object.keys(a)[0]) - new Date(Object.keys(b)[0]))
return mergedResult
}
Expand All @@ -121,11 +138,14 @@ watchEffect(() => {
}
dates.value = info.map((obj) => Object.keys(obj)[0])
likes.value = info.map((obj) => Object.values(obj)[0].likes)
dislikes.value = info.map((obj) => Object.values(obj)[0].dislikes)
likes.value = info.map((obj) => Object.values(obj)[0].likes || 0)
dislikes.value = info.map((obj) => {
const dislikesValue = Object.values(obj)[0].dislikes || 0
return dislikesValue < 0 ? -dislikesValue : dislikesValue
})
countLikes.value = likes.value.reduce((acc, item) => acc + item, 0)
countDislikes.value = dislikes.value.reduce((acc, item) => acc - item, 0)
countDislikes.value = dislikes.value.reduce((acc, item) => acc + item, 0)
compute()
})
Expand Down
Loading

0 comments on commit f2ba8cd

Please sign in to comment.