Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into jmo-grant-details-avatar
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffsmohan committed Feb 29, 2024
2 parents 4522fd7 + 127d6af commit 7ab545a
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 621 deletions.
53 changes: 53 additions & 0 deletions docs/decisions/0007-adopt-vue-recommended-linting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 0007. Adopt vue/recommended linting

Date: 2024-02-26
Status: Proposed

## Context and Problem Statement

Our frontend code currently enforces the [essential](https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention)
set of linting rules from [`eslint-plugin-vue`](https://eslint.vuejs.org/). This minimal set of
rules leaves a lot of code style up to individual authors and avoids enforcing a number of
community best practices. For this codebase to better support new volunteers frequently
onboarding while maintaining consistency and readability, we would benefit from adopting the full
set of Vue community recommended linting rules
([strongly recommended](https://eslint.vuejs.org/rules/#priority-b-strongly-recommended-improving-readability)
and [recommended](https://eslint.vuejs.org/rules/#priority-c-recommended-potentially-dangerous-patterns)).

## Decision Drivers <!-- optional -->

- **Consistency and readability**: Enforcing style choices keeps code more readable and maintainable.
- **Easy Onboarding**: By adopting and automating community standards, we make it easier for new
engineers to spin up and be productive quickly.
- **Security**: Some rules help us enforce security best practices (e.g.,
[`vue/no-v-html`](https://eslint.vuejs.org/rules/no-v-html.html)).
- **Bug prevention**: Some rules help us avoid bugs before they happen (e.g.,
[`vue/no-template-shadow`](https://eslint.vuejs.org/rules/no-template-shadow.html)).

## Considered Options

The relevant option space is really just which rules to adopt. The `eslint-plugin-vue` package
organizes its rules into three groups (each being a superset of the previous):

- [Priority A: Essential (Error Prevention)](https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention)
— *our current ruleset*
- [Priority B: Strongly Recommended (Improving Readability)](https://eslint.vuejs.org/rules/#priority-b-strongly-recommended-improving-readability)
- [Priority C: Recommended (Potentially Dangerous Patterns)](https://eslint.vuejs.org/rules/#priority-c-recommended-potentially-dangerous-patterns)
— *proposed ruleset*

We could also override individual rules from these rulesets if we disagreed with them or they
caused undue burden in development.

## Decision Outcome

This proposal is to adopt the full [recommended](https://eslint.vuejs.org/rules/#priority-c-recommended-potentially-dangerous-patterns) ruleset for linting across our frontend code.

## Code Examples

In order to avoid a "pause the world" style single giant PR, we would introduce the linting rules
incrementally. Examples of how this would work can be seen in:

1. Code change to introduce eslint rule change – https://github.com/usdigitalresponse/usdr-gost/pull/2654
2. Example of burning down one file incrementally — https://github.com/usdigitalresponse/usdr-gost/pull/2655

The burndown work could easily be split into a number of "easy first issue" tickets and worked through over a couple weeks.
11 changes: 4 additions & 7 deletions packages/client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,10 @@ module.exports = {
parser: 'babel-eslint',
},
rules: {
'max-len': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// ['warn', {
// // eslint-disable-next-line max-len
// code: 120, comments: 120, tabWidth: 4, ignoreStrings: true, ignoreTemplateLiterals: true, ignoreRegExpLiterals: true,
// }],
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// TODO: enable these lint rules over time
'max-len': 'off',
'no-console': 'off',
'no-debugger': 'off',
'func-names': 'off',

// Modern browsers have much greater support for ES6+ features than they did
Expand Down
295 changes: 158 additions & 137 deletions packages/client/src/views/GrantDetails.vue
Original file line number Diff line number Diff line change
@@ -1,149 +1,149 @@
<template>
<section class="grants-details-container m-5">
<div v-if="loading">
Loading...
</div>
<div v-if="!selectedGrant && !loading">
No grant found
</div>
<b-container fluid v-if="selectedGrant && !loading">
<b-row>
<section>
<div v-if="loading">
Loading...
</div>
<div v-if="!selectedGrant && !loading">
No grant found
</div>
<b-container fluid v-if="selectedGrant && !loading">
<div class="grant-details-container">

<!-- Left page column: title, table data, and grant description -->
<b-col>
<h2 class="mb-5">{{ selectedGrant.title }}</h2>
<b-table
class="grant-details-table mb-5"
:items="tableData"
:fields="[
{key: 'name', class: 'color-gray grants-details-table-fit-content'},
{key: 'value', class: 'font-weight-bold'},
]"
thead-class="d-none"
borderless
hover
>
<template #cell()="data">
<span :class="{'text-muted font-weight-normal': data.item.displayMuted}">
{{ data.value }}
</span>
</template>
</b-table>
<h3 class="mb-3">Description</h3>
<!-- TODO: spike on removing v-html usage https://github.com/usdigitalresponse/usdr-gost/issues/2572 -->
<div style="white-space: pre-line" v-html="selectedGrant.description"></div>
</b-col>
<!-- Left page column: headline -->
<h2 class="grant-details-headline m-0">{{ selectedGrant.title }}</h2>

<!-- Right page column: apply, assign, and status actions -->
<b-col class="grants-details-sidebar">
<!-- Left page column: table data, and grant description -->
<div class="grant-details-content">
<b-table
class="grant-details-table mb-5"
:items="tableData"
:fields="[
{key: 'name', class: 'color-gray grants-details-table-fit-content'},
{key: 'value', class: 'font-weight-bold'},
]"
thead-class="d-none"
borderless
hover
>
<template #cell()="data">
<span :class="{'text-muted font-weight-normal': data.item.displayMuted}">
{{ data.value }}
</span>
</template>
</b-table>
<h3 class="mb-3">Description</h3>
<!-- TODO: spike on removing v-html usage https://github.com/usdigitalresponse/usdr-gost/issues/2572 -->
<div style="white-space: pre-line" v-html="selectedGrant.description"></div>
</div>

<!-- Right page column: main print/copy/grants.gov buttons -->
<div class="grant-details-main-actions print-d-none">
<b-button
variant="primary"
block
class="mb-3"
:href="`https://www.grants.gov/search-results-detail/${selectedGrant.grant_id}`"
target="_blank"
rel="noopener noreferrer"
data-dd-action-name="view on grants.gov"
>
<b-icon icon="box-arrow-up-right" aria-hidden="true" class="mr-2" />
Apply on Grants.gov
</b-button>
<div class="d-flex">
<b-button
class="w-50 flex-shrink-1 mr-3"
variant="outline-primary"
@click="printPage"
>
<b-icon icon="printer-fill" aria-hidden="true" class="mr-2" />
Print
</b-button>
<b-button
class="w-50 flex-shrink-1"
:variant="copyUrlSuccessTimeout === null ? 'outline-primary' : 'outline-success'"
@click="copyUrl"
>
<b-icon :icon="copyUrlSuccessTimeout === null ? 'files' : 'check2'" aria-hidden="true" class="mr-2" />
<span v-if="copyUrlSuccessTimeout === null">Copy Link</span>
<span v-else>Link Copied</span>
</b-button>
</div>
</div>

<!-- Main action buttons section -->
<div class="mb-5 print-d-none">
<b-button
variant="primary"
block
class="mb-3"
:href="`https://www.grants.gov/search-results-detail/${selectedGrant.grant_id}`"
target="_blank"
rel="noopener noreferrer"
data-dd-action-name="view on grants.gov"
>
<b-icon icon="box-arrow-up-right" aria-hidden="true" class="mr-2" />
Apply on Grants.gov
<!-- Right page column: secondary assign grant section -->
<div class="grant-details-secondary-actions">
<!-- Assign grant section -->
<div class="mb-5">
<h3 class="mb-3">Assign Grant</h3>
<div class="d-flex print-d-none">
<v-select
class="flex-grow-1 mr-3"
v-model="selectedAgencyToAssign"
:options="unassignedAgencies"
label="name"
track-by="id"
:placeholder="`Choose ${newTerminologyEnabled ? 'team': 'agency'}`"
:clearable="false"
data-dd-action-name="select team for grant assignment"
/>
<b-button variant="outline-primary" @click="assignAgenciesToGrant" :disabled="!selectedAgencyToAssign" data-dd-action-name="assign team">
Submit
</b-button>
<div class="d-flex">
<b-button
class="w-50 flex-shrink-1 mr-3"
variant="outline-primary"
@click="printPage"
>
<b-icon icon="printer-fill" aria-hidden="true" class="mr-2" />
Print
</b-button>
<b-button
class="w-50 flex-shrink-1"
:variant="copyUrlSuccessTimeout === null ? 'outline-primary' : 'outline-success'"
@click="copyUrl"
>
<b-icon :icon="copyUrlSuccessTimeout === null ? 'files' : 'check2'" aria-hidden="true" class="mr-2" />
<span v-if="copyUrlSuccessTimeout === null">Copy Link</span>
<span v-else>Link Copied</span>
</b-button>
</div>
</div>

<!-- Assign grant section -->
<div class="mb-5">
<h3 class="mb-3">Assign Grant</h3>
<div class="d-flex print-d-none">
<v-select
class="flex-grow-1 mr-3"
v-model="selectedAgencyToAssign"
:options="unassignedAgencies"
label="name"
track-by="id"
:placeholder="`Choose ${newTerminologyEnabled ? 'team': 'agency'}`"
:clearable="false"
data-dd-action-name="select team for grant assignment"
/>
<b-button variant="outline-primary" @click="assignAgenciesToGrant" :disabled="!selectedAgencyToAssign" data-dd-action-name="assign team">
Submit
</b-button>
</div>
<div v-for="agency in assignedAgencies" :key="agency.id" class="d-flex justify-content-between align-items-start my-3">
<div class="mr-3">
<p class="m-0">{{ agency.name }}</p>
<p class="m-0 text-muted"><small>{{ formatDateTime(agency.created_at) }}</small></p>
</div>
<b-button-close
@click="unassignAgenciesToGrant(agency)"
data-dd-action-name="remove team assignment"
class="print-d-none"
/>
<div v-for="agency in assignedAgencies" :key="agency.id" class="d-flex justify-content-between align-items-start my-3">
<div class="mr-3">
<p class="m-0">{{ agency.name }}</p>
<p class="m-0 text-muted"><small>{{ formatDateTime(agency.created_at) }}</small></p>
</div>
<b-button-close
@click="unassignAgenciesToGrant(agency)"
data-dd-action-name="remove team assignment"
class="print-d-none"
/>
</div>
</div>

<!-- Team status section -->
<div class="mb-5">
<h3 class="mb-3">{{newTerminologyEnabled ? 'Team': 'Agency'}} Status</h3>
<div class="d-flex print-d-none">
<v-select
class="flex-grow-1 mr-3"
v-model="selectedInterestedCode"
:reduce="(option) => option.id"
:options="interestedOptions"
label="name"
track-by="id"
placeholder="Choose status"
:selectable="selectableOption"
:clearable="false"
data-dd-action-name="select team status"
/>
<b-button variant="outline-primary" @click="markGrantAsInterested" :disabled="!selectedInterestedCode" data-dd-action-name="submit team status">
Submit
</b-button>
</div>
<div v-for="agency in visibleInterestedAgencies" :key="agency.id" class="d-flex justify-content-between align-items-start my-3">
<UserAvatar :user-name="agency.user_name" :color="agency.user_avatar_color" size="2.5rem" />
<div class="mx-3">
<p class="m-0">
<strong>{{ agency.user_name }}</strong> updated
<strong>{{ agency.agency_name }}</strong> team status to
<strong>{{ agency.interested_code_name }}</strong>
</p>
</div>
<b-button-close
@click="unmarkGrantAsInterested(agency)"
data-dd-action-name="remove team status"
class="print-d-none"
/>
<!-- Team status section -->
<div class="mb-5">
<h3 class="mb-3">{{newTerminologyEnabled ? 'Team': 'Agency'}} Status</h3>
<div class="d-flex print-d-none">
<v-select
class="flex-grow-1 mr-3"
v-model="selectedInterestedCode"
:reduce="(option) => option.id"
:options="interestedOptions"
label="name"
track-by="id"
placeholder="Choose status"
:selectable="selectableOption"
:clearable="false"
data-dd-action-name="select team status"
/>
<b-button variant="outline-primary" @click="markGrantAsInterested" :disabled="!selectedInterestedCode" data-dd-action-name="submit team status">
Submit
</b-button>
</div>
<div v-for="agency in visibleInterestedAgencies" :key="agency.id" class="d-flex justify-content-between align-items-start my-3">
<UserAvatar :user-name="agency.user_name" :color="agency.user_avatar_color" size="2.5rem" />
<div class="mx-3">
<p class="m-0">
<strong>{{ agency.user_name }}</strong> updated
<strong>{{ agency.agency_name }}</strong> team status to
<strong>{{ agency.interested_code_name }}</strong>
</p>
</div>
<b-button-close
@click="unmarkGrantAsInterested(agency)"
data-dd-action-name="remove team status"
class="print-d-none"
/>
</div>
</div>
</div>

</b-col>

</b-row>
</b-container>
</div>
</b-container>
</section>
</template>

Expand Down Expand Up @@ -434,10 +434,31 @@ export default {
</script>
<style lang="css">
.grants-details-sidebar {
flex-basis: 437px;
flex-grow: 0;
.grant-details-container {
margin: 80px;
display: grid;
grid-template-columns: 1fr 437px;
grid-template-rows: auto;
grid-template-areas:
"headline main-actions"
"content secondary-actions";
column-gap: 90px;
row-gap: 48px;
}
.grant-details-headline {
grid-area: headline;
align-self: end;
}
.grant-details-content {
grid-area: content;
}
.grant-details-main-actions {
grid-area: main-actions;
}
.grant-details-secondary-actions {
grid-area: secondary-actions;
}
.grant-details-table tr:nth-of-type(odd) {
/* Design color differs from default bootstrap, so making our own striped background here */
background-color: #F9F9F9;
Expand Down
Loading

0 comments on commit 7ab545a

Please sign in to comment.