From 81839f70a5f13af3d7ef546931dd143a9d5ae256 Mon Sep 17 00:00:00 2001 From: taslangraham Date: Wed, 9 Jul 2025 00:25:52 -0500 Subject: [PATCH 1/5] WIP: pkp/pkp-lib#11327 Implement public facing UI for user comments --- src/components/UserComment/UserComment.vue | 274 +++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 src/components/UserComment/UserComment.vue diff --git a/src/components/UserComment/UserComment.vue b/src/components/UserComment/UserComment.vue new file mode 100644 index 000000000..3e36a9226 --- /dev/null +++ b/src/components/UserComment/UserComment.vue @@ -0,0 +1,274 @@ + + + + + From 3efa589742b8e06c7442fe3d8b1c5674bc0d4f70 Mon Sep 17 00:00:00 2001 From: taslangraham Date: Wed, 13 Aug 2025 12:02:50 -0500 Subject: [PATCH 2/5] pkp/pkp-lib#11698 Add additional icons and components to frontend UI --- .../PkpAvatarInitials/pkpAvatarInitials.vue | 101 ++++++++++++++++++ .../components/PkpButton/PkpButton.vue | 19 +++- src/frontend/components/PkpIcon/PkpIcon.vue | 7 +- .../components/PkpIcon/icons/Error.vue | 8 ++ .../components/PkpIcon/icons/Help.vue | 27 +++++ .../components/PkpIcon/icons/User.vue | 10 ++ src/frontend/composables/load_frontend.js | 3 +- src/frontend/composables/usePkpDate.js | 17 +++ 8 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 src/frontend/components/PkpAvatarInitials/pkpAvatarInitials.vue create mode 100644 src/frontend/components/PkpIcon/icons/Error.vue create mode 100644 src/frontend/components/PkpIcon/icons/Help.vue create mode 100644 src/frontend/components/PkpIcon/icons/User.vue create mode 100644 src/frontend/composables/usePkpDate.js diff --git a/src/frontend/components/PkpAvatarInitials/pkpAvatarInitials.vue b/src/frontend/components/PkpAvatarInitials/pkpAvatarInitials.vue new file mode 100644 index 000000000..f146d68c9 --- /dev/null +++ b/src/frontend/components/PkpAvatarInitials/pkpAvatarInitials.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/src/frontend/components/PkpButton/PkpButton.vue b/src/frontend/components/PkpButton/PkpButton.vue index f0d58f61c..32ce02b84 100644 --- a/src/frontend/components/PkpButton/PkpButton.vue +++ b/src/frontend/components/PkpButton/PkpButton.vue @@ -1,8 +1,14 @@ - - diff --git a/src/frontend/components/PkpAccordion/PkpAccordion.vue b/src/frontend/components/PkpAccordion/PkpAccordion.vue index 7d15fcbad..db50b5fd5 100644 --- a/src/frontend/components/PkpAccordion/PkpAccordion.vue +++ b/src/frontend/components/PkpAccordion/PkpAccordion.vue @@ -63,7 +63,7 @@ const accordionClass = computed(() => ['pkpAccordion']); width: 100%; padding: var(--pkp-spacing-2) var(--pkp-spacing-3); background-color: var(--pkp-background-color-tertiary); - color: var(--pkp-text-color-secondary); + color: var(--pkp-text-color-heading); font: var(--pkp-font-base-bold); border: none; cursor: pointer; diff --git a/src/frontend/components/PkpButton/PkpButton.vue b/src/frontend/components/PkpButton/PkpButton.vue index 32ce02b84..f0d58f61c 100644 --- a/src/frontend/components/PkpButton/PkpButton.vue +++ b/src/frontend/components/PkpButton/PkpButton.vue @@ -1,14 +1,8 @@ + + diff --git a/src/frontend/components/PkpUserComment/PkpUserCommentReportModal.vue b/src/frontend/components/PkpUserComment/PkpUserCommentReportModal.vue new file mode 100644 index 000000000..aa4f72f60 --- /dev/null +++ b/src/frontend/components/PkpUserComment/PkpUserCommentReportModal.vue @@ -0,0 +1,83 @@ + + + + diff --git a/src/frontend/components/PkpUserComment/PkpUserCommentsList.vue b/src/frontend/components/PkpUserComment/PkpUserCommentsList.vue new file mode 100644 index 000000000..e6d8bc757 --- /dev/null +++ b/src/frontend/components/PkpUserComment/PkpUserCommentsList.vue @@ -0,0 +1,363 @@ + + + + + From 234a0ef6d63c3bdd67ac60175d2d291fff3dca06 Mon Sep 17 00:00:00 2001 From: taslangraham Date: Wed, 13 Aug 2025 22:37:28 -0500 Subject: [PATCH 4/5] pkp/pkp-lib#11327 Allow user to delete comment --- .../components/PkpButton/PkpButton.vue | 16 +++++++ .../components/PkpModal/PkpDialog.vue | 3 ++ .../PkpUserComment/PkpUserComment.vue | 4 -- .../PkpUserComment/PkpUserCommentsList.vue | 45 ++++++++++++++++++- 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/frontend/components/PkpButton/PkpButton.vue b/src/frontend/components/PkpButton/PkpButton.vue index f0d58f61c..eafd119ba 100644 --- a/src/frontend/components/PkpButton/PkpButton.vue +++ b/src/frontend/components/PkpButton/PkpButton.vue @@ -13,12 +13,15 @@ const props = defineProps({ type: Boolean, default: false, }, + /** Use when this button represents an action such as delete, go back, revert or cancel. */ + isWarnable: Boolean, }); const buttonClass = computed(() => { return [ 'pkpButton', props.isSecondary ? 'pkpButton--secondary' : 'pkpButton--primary', + props.isWarnable ? 'pkpButton--warnable' : '', ]; }); @@ -73,4 +76,17 @@ const buttonClass = computed(() => { border-color: var(--pkp-text-color-disabled); color: var(--pkp-text-color-disabled); } + +.pkpButton--warnable { + color: var(--pkp-color-negative); + border-color: var(--pkp-border-color-light); + background-color: var(--pkp-background-color-secondary); +} + +.pkpButton--warnable:disabled { + color: var(--pkp-text-color-disabled); +} +.pkpButton--warnable:hover { + background-color: var(--pkp-background-color-secondary); +} diff --git a/src/frontend/components/PkpModal/PkpDialog.vue b/src/frontend/components/PkpModal/PkpDialog.vue index 19bf0dab6..5aeded165 100644 --- a/src/frontend/components/PkpModal/PkpDialog.vue +++ b/src/frontend/components/PkpModal/PkpDialog.vue @@ -68,6 +68,9 @@ import { } from 'reka-ui'; import PkpIcon from '@/frontend/components/PkpIcon/PkpIcon.vue'; +import {useLocalize} from '@/composables/useLocalize'; +const {t} = useLocalize(); + const props = defineProps({ /** Used only internally, don't pass this prop via openDialog */ opened: {type: Boolean, default: false}, diff --git a/src/frontend/components/PkpUserComment/PkpUserComment.vue b/src/frontend/components/PkpUserComment/PkpUserComment.vue index 5340b8a4c..ed807ee84 100644 --- a/src/frontend/components/PkpUserComment/PkpUserComment.vue +++ b/src/frontend/components/PkpUserComment/PkpUserComment.vue @@ -38,10 +38,6 @@ const props = defineProps({ type: Number, required: true, }, - publicationId: { - type: Number, - required: true, - }, publicationIds: { type: Array, required: false, diff --git a/src/frontend/components/PkpUserComment/PkpUserCommentsList.vue b/src/frontend/components/PkpUserComment/PkpUserCommentsList.vue index e6d8bc757..4ce18ac54 100644 --- a/src/frontend/components/PkpUserComment/PkpUserCommentsList.vue +++ b/src/frontend/components/PkpUserComment/PkpUserCommentsList.vue @@ -107,7 +107,9 @@ import {usePkpModal} from '@/frontend/composables/usePkpModal'; import {usePkpFetch} from '@/frontend/composables/usePkpFetch'; import {useUrl} from '@/frontend/composables/usePkpUrl'; import {usePkpDate} from '@/frontend/composables/usePkpDate'; -import {t} from '@/utils/i18n'; +import {useLocalize} from '@/composables/useLocalize'; + +const {t} = useLocalize(); const {openModal, closeModal, openDialogNetworkError} = usePkpModal(); const {formatShortDateTime} = usePkpDate(); @@ -145,8 +147,49 @@ const commentText = ref(''); const commentActionMethods = { commentReport, + deleteComment, }; +function deleteComment(comment) { + if (!currentUser || currentUser.id !== comment.userId) { + throw new Error('Only the comment author can delete the comment'); + } + + const {openDialog} = usePkpModal(); + openDialog({ + title: 'Delete Comment', + message: t('userComment.deleteCommentConfirm', { + comment: comment.commentText, + }), + actions: [ + { + label: t('common.delete'), + isWarnable: true, + callback: async (close) => { + const {apiUrl} = useUrl(`comments/${comment.id}`); + const {fetch: deleteComment, isSuccess} = usePkpFetch(apiUrl, { + method: 'DELETE', + }); + await deleteComment(); + if (isSuccess.value) { + comments.value = comments.value.filter((c) => c.id !== comment.id); + } else { + openDialogNetworkError(); + } + + close(); + }, + }, + { + label: t('common.cancel'), + callback: (close) => { + close(); + }, + }, + ], + }); +} + function canReportComment(comment) { return currentUser && currentUser.id !== comment.userId && comment.isApproved; } From 39adda2c1745788079b1811b34ac3c905e1e9336 Mon Sep 17 00:00:00 2001 From: taslangraham Date: Thu, 14 Aug 2025 14:27:58 -0500 Subject: [PATCH 5/5] pkp/pkp-lib#11327 Code cleanup and refactoring --- .../PkpAvatarInitials/pkpAvatarInitials.vue | 101 ------- src/frontend/components/PkpIcon/PkpIcon.vue | 4 + .../components/PkpIcon/icons/Orcid.vue | 19 ++ .../PkpIcon/icons/OrcidUnauthenticated.vue | 27 ++ .../components/PkpModal/PkpDialog.vue | 4 +- .../components/PkpModal/PkpModalBody.vue | 7 +- .../components/PkpTextarea/PkpTextarea.vue | 11 +- .../PkpUserComment/PkpScrollToComments.vue | 70 +++++ .../PkpUserComment/PkpUserComment.vue | 38 ++- .../PkpUserCommentReportModal.vue | 55 +++- .../PkpUserComment/PkpUserCommentsList.vue | 262 +++++++++++------- src/frontend/composables/load_frontend.js | 2 - src/frontend/composables/usePkpLocalize.js | 43 +++ src/frontend/styles/style.css | 31 +++ 14 files changed, 436 insertions(+), 238 deletions(-) delete mode 100644 src/frontend/components/PkpAvatarInitials/pkpAvatarInitials.vue create mode 100644 src/frontend/components/PkpIcon/icons/Orcid.vue create mode 100644 src/frontend/components/PkpIcon/icons/OrcidUnauthenticated.vue create mode 100644 src/frontend/components/PkpUserComment/PkpScrollToComments.vue create mode 100644 src/frontend/composables/usePkpLocalize.js create mode 100644 src/frontend/styles/style.css diff --git a/src/frontend/components/PkpAvatarInitials/pkpAvatarInitials.vue b/src/frontend/components/PkpAvatarInitials/pkpAvatarInitials.vue deleted file mode 100644 index f146d68c9..000000000 --- a/src/frontend/components/PkpAvatarInitials/pkpAvatarInitials.vue +++ /dev/null @@ -1,101 +0,0 @@ - - - - - diff --git a/src/frontend/components/PkpIcon/PkpIcon.vue b/src/frontend/components/PkpIcon/PkpIcon.vue index 03b7ad19b..7b15a3bfa 100644 --- a/src/frontend/components/PkpIcon/PkpIcon.vue +++ b/src/frontend/components/PkpIcon/PkpIcon.vue @@ -5,12 +5,16 @@ import MoreOptions from './icons/MoreOptions.vue'; import Error from './icons/Error.vue'; import Help from './icons/Help.vue'; import User from './icons/User.vue'; +import Orcid from '@/frontend/components/PkpIcon/icons/Orcid.vue'; +import OrcidUnauthenticated from '@/frontend/components/PkpIcon/icons/OrcidUnauthenticated.vue'; const svgIcons = { Cancel, MoreOptions, Error, Help, User, + Orcid, + OrcidUnauthenticated, }; const props = defineProps({ diff --git a/src/frontend/components/PkpIcon/icons/Orcid.vue b/src/frontend/components/PkpIcon/icons/Orcid.vue new file mode 100644 index 000000000..01fe1647c --- /dev/null +++ b/src/frontend/components/PkpIcon/icons/Orcid.vue @@ -0,0 +1,19 @@ + diff --git a/src/frontend/components/PkpIcon/icons/OrcidUnauthenticated.vue b/src/frontend/components/PkpIcon/icons/OrcidUnauthenticated.vue new file mode 100644 index 000000000..8bfb03fd6 --- /dev/null +++ b/src/frontend/components/PkpIcon/icons/OrcidUnauthenticated.vue @@ -0,0 +1,27 @@ + diff --git a/src/frontend/components/PkpModal/PkpDialog.vue b/src/frontend/components/PkpModal/PkpDialog.vue index 5aeded165..4bac55330 100644 --- a/src/frontend/components/PkpModal/PkpDialog.vue +++ b/src/frontend/components/PkpModal/PkpDialog.vue @@ -67,9 +67,9 @@ import { DialogTitle, } from 'reka-ui'; import PkpIcon from '@/frontend/components/PkpIcon/PkpIcon.vue'; +import {usePkpLocalize} from '@/frontend/composables/usePkpLocalize'; -import {useLocalize} from '@/composables/useLocalize'; -const {t} = useLocalize(); +const {t} = usePkpLocalize(); const props = defineProps({ /** Used only internally, don't pass this prop via openDialog */ diff --git a/src/frontend/components/PkpModal/PkpModalBody.vue b/src/frontend/components/PkpModal/PkpModalBody.vue index 71fe0edb4..abcc87235 100644 --- a/src/frontend/components/PkpModal/PkpModalBody.vue +++ b/src/frontend/components/PkpModal/PkpModalBody.vue @@ -41,7 +41,7 @@
@@ -78,8 +78,8 @@ import PkpIcon from '@/frontend/components/PkpIcon/PkpIcon.vue'; import {focusFirstHeading} from '@/components/Modal/modalHelpers'; const containerId = useId(); -import {useLocalize} from '@/composables/useLocalize'; -const {t} = useLocalize(); +import {usePkpLocalize} from '@/frontend/composables/usePkpLocalize'; +const {t} = usePkpLocalize(); const closeModal = inject('closeModal'); const closeModalButton = inject('closeModalButton'); /* Initial focus */ @@ -230,6 +230,7 @@ function handleAutoFocus(event) { position: relative; outline: none; background-color: var(--pkp-background-color-secondary); + cursor: pointer; } .pkpModalBody__close:hover { diff --git a/src/frontend/components/PkpTextarea/PkpTextarea.vue b/src/frontend/components/PkpTextarea/PkpTextarea.vue index 11a1889e5..8667f0e9e 100644 --- a/src/frontend/components/PkpTextarea/PkpTextarea.vue +++ b/src/frontend/components/PkpTextarea/PkpTextarea.vue @@ -1,6 +1,10 @@