Skip to content

Commit

Permalink
Feature: Adjust display for share grant events (#3569)
Browse files Browse the repository at this point in the history
* feat: add API to retrieve grant notes for specific user via Finder API

- Implemented `getOrganizationNotesForGrantByUser()` function in `grantsCollaboration/notes.js`
- Renamed `getOrganizationNotesForGrant()` to `getCurrentNoteRevisions()` and updated signature
- Made conditional `WHERE` clause for `grantId` and `userId` in `getCurrentNoteRevisions()`
- Created API route `GET /:grantId/notes/user/:userId` in `grants.js` for fetching user-specific grant notes
- Added validation for `afterRevision` and `limit` query parameters in the new API
- Ensured results are paginated and ordered by `created_at` in descending order
- Added necessary imports/exports in `grantsCollaboration/index.js`

* ES Lint fixes

* ES Lint fixes

* ES Lint fixes

* ES Lint fixes

* ES Lint fixes

* first pass

* add notes for user

* adjust styling

* refine grant notes

* add FE tests

* adjust grant notes

* adjust styling

* adjust notes display

* adjust styling

* fix test

* extract header text to component

* adjust styling

* correct limit

* update share grant

* include assigned by agency

* sync styling on sidepanel cards

* remove unused

* remove old component

* incl revised state

---------

Co-authored-by: Sushil Rajeeva Bhandary <[email protected]>
  • Loading branch information
greg-adams and sushilrajeeva authored Oct 15, 2024
1 parent 560c13e commit 5f2dbd8
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 159 deletions.
5 changes: 3 additions & 2 deletions packages/client/src/components/GrantActivity.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
<div>
<b-card
header-bg-variant="white"
header-class="px-3 py-4"
footer-bg-variant="white"
footer-class="p-0"
>
<template #header>
<h3 class="my-2">
<h3 class="m-0">
Grant Activity
</h3>
</template>
Expand Down Expand Up @@ -135,7 +136,7 @@ export default {
return `${textCount} ${this.notes.length === 1 ? 'note' : 'notes'}`;
},
},
async beforeMount() {
beforeMount() {
this.fetchFollowAndNotes();
},
methods: {
Expand Down
37 changes: 0 additions & 37 deletions packages/client/src/components/GrantNote.spec.js

This file was deleted.

11 changes: 6 additions & 5 deletions packages/client/src/components/GrantNotes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,16 @@ describe('GrantNotes component', () => {
const inputCmp = wrapper.findComponent('[data-test-note-input]');
expect(inputCmp.exists()).toEqual(false);

const userNoteCmp = wrapper.findComponent('[data-test-user-note]');
const userNoteCmp = wrapper.findComponent('[data-test-user-note-id]');
expect(userNoteCmp.exists()).equal(true);

const otherNoteCmps = wrapper.findAllComponents('[data-test-other-note]');
const otherNoteCmps = wrapper.findAllComponents('[data-test-other-note-id]');
expect(otherNoteCmps).toHaveLength(5);

// excludes user note
otherNoteCmps.forEach((cmp) => {
expect(cmp.props('note').user.id).not.toEqual(CURRENT_USER_ID);
expect(cmp.attributes('data-test-other-note-id')).toBeTruthy();
expect(cmp.attributes('data-test-other-note-id')).not.toEqual(CURRENT_USER_ID);
});
});

Expand Down Expand Up @@ -133,7 +134,7 @@ describe('GrantNotes component', () => {
await flushPromises();

expect(mockStore.actions['grants/saveNoteForGrant'].mock.lastCall[1].text).toEqual('My Note');
const userNoteCmp = wrapper.findComponent('[data-test-user-note]');
const userNoteCmp = wrapper.findComponent('[data-test-user-note-id]');
expect(userNoteCmp.exists()).equal(true);
});

Expand Down Expand Up @@ -162,7 +163,7 @@ describe('GrantNotes component', () => {
await flushPromises();

expect(mockStore.actions['grants/getNotesForGrant'].mock.lastCall[1].cursor).toEqual(NEXT_ID);
const otherNoteCmps = wrapper.findAllComponents('[data-test-other-note]');
const otherNoteCmps = wrapper.findAllComponents('[data-test-other-note-id]');
expect(otherNoteCmps).toHaveLength(4);
expect(wrapper.findComponent('[data-test-show-more-btn]').exists()).toEqual(false);
});
Expand Down
48 changes: 34 additions & 14 deletions packages/client/src/components/GrantNotes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
class="d-flex note-edit-container"
>
<UserAvatar
:user-name="loggedInUser.name"
size="2.5rem"
:user-name="loggedInUser.name"
:color="loggedInUser.avatar_color"
/>
<b-form-group class="ml-2 mb-2 flex-grow-1 position-relative">
<b-form-group class="mx-3 mb-2 flex-grow-1 position-relative">
<b-form-textarea
ref="noteTextarea"
v-model="noteText"
Expand Down Expand Up @@ -51,12 +51,19 @@
</div>

<!-- Current User's Note -->
<GrantNote
<UserActivityItem
v-if="userNote && !editingNote"
data-test-user-note
:class="userNoteClass"
:note="userNote"
:user-name="userNote.user.name"
:user-email="userNote.user.email"
:team-name="userNote.user.team.name"
:avatar-color="userNote.user.avatarColor"
:created-at="userNote.createdAt"
:is-edited="userNote.isRevised"
copy-email-enabled
data-test-user-note-id="userNote.id"
>
{{ userNote.text }}
<template #actions>
<b-button
class="note-edit-btn p-0"
Expand All @@ -70,18 +77,27 @@
<span>EDIT</span>
</b-button>
</template>
</GrantNote>
</UserActivityItem>

<!-- Other Notes -->
<ul class="list-unstyled mb-0">
<li
v-for="note of otherNotes"
:key="note.id"
>
<GrantNote
:note="note"
data-test-other-note
/>
<UserActivityItem
class="activity-container"
:user-name="note.user.name"
:user-email="note.user.email"
:team-name="note.user.team.name"
:avatar-color="note.user.avatarColor"
:created-at="note.createdAt"
:is-edited="note.isRevised"
copy-email-enabled
:data-test-other-note-id="note.id"
>
{{ note.text }}
</UserActivityItem>
</li>
</ul>

Expand All @@ -107,13 +123,13 @@
import { nextTick } from 'vue';
import { mapActions, mapGetters } from 'vuex';
import UserAvatar from '@/components/UserAvatar.vue';
import GrantNote from '@/components/GrantNote.vue';
import UserActivityItem from '@/components/UserActivityItem.vue';
import { grantNotesLimit } from '@/helpers/featureFlags';
export default {
components: {
UserAvatar,
GrantNote,
UserActivityItem,
},
emits: ['noteSaved'],
data() {
Expand Down Expand Up @@ -149,9 +165,9 @@ export default {
return `ml-auto ${errColor}`;
},
userNoteClass() {
const corners = this.emptyNoteText ? 'rounded-bottom-corners' : '';
const corners = this.otherNotes.length === 0 ? 'rounded-bottom-corners' : '';
return `user-note ${corners}`;
return `user-note activity-container ${corners}`;
},
},
async beforeMount() {
Expand Down Expand Up @@ -241,6 +257,10 @@ textarea.note-textarea {
padding-right: 2.25rem;
}
.activity-container {
padding: 1.25rem;
}
textarea.note-textarea::placeholder {
font-size: 0.875rem
}
Expand Down
54 changes: 13 additions & 41 deletions packages/client/src/components/Modals/GrantFollowers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,25 @@

<ul class="list-unstyled mt-3">
<li
v-for="follower in formattedFollowers"
v-for="follower in followers"
:key="follower.id"
data-test-follower
class="mb-3"
>
<div class="d-flex">
<UserAvatar
:user-name="follower.name"
size="2.5rem"
:color="follower.avatarColor"
class="mr-2"
<UserActivityItem
class="mr-3"
:user-name="follower.user.name"
:user-email="follower.user.email"
:team-name="follower.user.team.name"
:avatar-color="follower.user.avatarColor"
:created-at="follower.createdAt"
hide-avatar-vertical
/>
<div class="mt-1 d-flex flex-column has-flexi-truncate mr-2">
<UserHeaderText
:name="follower.name"
:team="follower.team"
/>
<div class="follower-email text-muted">
{{ follower.email }}
</div>
<div class="follower-date text-muted">
{{ follower.dateFollowedText }}
</div>
</div>

<CopyButton
:copy-text="follower.email"
:copy-text="follower.user.email"
hide-icon
class="ms-auto flex-shrink-0 mt-1"
class="ml-auto flex-shrink-0 mt-1"
>
<b-button
variant="outline-primary"
Expand Down Expand Up @@ -90,16 +80,13 @@
<script>
import { mapActions, mapGetters } from 'vuex';
import UserAvatar from '@/components/UserAvatar.vue';
import CopyButton from '@/components/CopyButton.vue';
import UserHeaderText from '@/components/UserHeaderText.vue';
import { formatActivityDate } from '@/components/GrantNote.vue';
import UserActivityItem from '@/components/UserActivityItem.vue';
export default {
components: {
UserAvatar,
CopyButton,
UserHeaderText,
UserActivityItem,
},
props: {
modalId: {
Expand All @@ -123,21 +110,6 @@ export default {
loadMoreVisible() {
return this.followersNextCursor !== null;
},
formattedFollowers() {
return this.followers
.map((follower) => {
const { user, id, createdAt } = follower;
return {
id,
name: user.name,
email: user.email,
team: user.team.name,
dateFollowedText: formatActivityDate(createdAt),
avatarColor: user.avatarColor,
};
});
},
followersEmailText() {
return this.followers
.map((follower) => follower.user.email)
Expand Down
48 changes: 26 additions & 22 deletions packages/client/src/components/ShareGrant.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
<!-- eslint-disable max-len -->
<template>
<b-card>
<!-- Assign grant section -->
<div class="mb-5">
<h3 class="mb-3">
<b-card
header-bg-variant="white"
header-class="px-3 py-4"
>
<template #header>
<h3 class="m-0">
{{ shareTerminologyEnabled ? 'Share Grant' : 'Assign Grant' }}
</h3>
</template>
<div class="mt-1 mb-4">
<div class="d-flex print-d-none">
<v-select
v-model="selectedAgencyToAssign"
class="flex-grow-1 mr-3"
:options="unassignedAgencies"
label="name"
track-by="id"
:placeholder="`Choose ${newTerminologyEnabled ? 'team': 'agency'}`"
:placeholder="`Choose ${newTerminologyEnabled ? 'team' : 'agency'}`"
:clearable="false"
data-dd-action-name="select team for grant assignment"
@close="$refs.assignSubmitButton.focus()"
Expand Down Expand Up @@ -53,21 +57,20 @@
<div
v-for="agency in assignedAgencies"
:key="agency.id"
class="d-flex justify-content-start align-items-start my-3"
class="my-4"
>
<UserAvatar
<UserActivityItem
:user-name="agency.assigned_by_name"
:color="agency.assigned_by_avatar_color"
size="2.5rem"
/>
<div class="mx-3">
<p class="m-0">
<strong>{{ agency.assigned_by_name }}</strong> shared to <strong>{{ agency.name }}</strong>
</p>
<p class="m-0 text-muted">
<small>{{ formatDateTime(agency.created_at) }}</small>
</p>
</div>
:user-email="agency.assigned_by_email"
:team-name="agency.assigned_by_agency_name"
:avatar-color="agency.assigned_by_avatar_color"
:created-at="agency.created_at"
copy-email-enabled
>
<div>
Shared grant with <strong>{{ agency.name }}</strong>
</div>
</UserActivityItem>
</div>
</template>
</div>
Expand All @@ -80,10 +83,12 @@ import { datadogRum } from '@datadog/browser-rum';
import { newTerminologyEnabled, shareTerminologyEnabled } from '@/helpers/featureFlags';
import { formatDate, formatDateTime } from '@/helpers/dates';
import { gtagEvent } from '@/helpers/gtag';
import UserAvatar from '@/components/UserAvatar.vue';
import UserActivityItem from '@/components/UserActivityItem.vue';
export default {
components: { UserAvatar },
components: {
UserActivityItem,
},
data() {
return {
selectedAgencyToAssign: null,
Expand Down Expand Up @@ -150,5 +155,4 @@ export default {
};
</script>

<style scoped>
</style>
<style scoped></style>
Loading

0 comments on commit 5f2dbd8

Please sign in to comment.