Skip to content

Commit b3aace0

Browse files
authored
Merge pull request #552 from citation-file-format/highlight-duplicate-author
Highlight duplicate author, identifier, keyword
2 parents 07ee3d6 + 50c5d1c commit b3aace0

6 files changed

Lines changed: 85 additions & 27 deletions

File tree

src/components/AuthorCardViewing.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
<script lang="ts">
4646
import { PropType, computed, defineComponent, onUpdated } from 'vue'
47-
import { byError, emailQueries, orcidQueries } from 'src/error-filtering'
47+
import { byError, duplicateAuthorQueries, duplicateMatcher, emailQueries, orcidQueries } from 'src/error-filtering'
4848
import { AuthorType } from 'src/types'
4949
import { useStepperErrors } from 'src/store/stepper-errors'
5050
import { useValidation } from 'src/store/validation'
@@ -81,7 +81,13 @@ export default defineComponent({
8181
.filter(byError(errors.value))
8282
.map(query => query.replace.message)
8383
})
84-
const authorErrors = [...emailErrors.value, ...orcidErrors.value]
84+
const duplicateErrors = computed(() => {
85+
return duplicateAuthorQueries
86+
.filter(byError(errors.value))
87+
.filter(byError(errors.value, duplicateMatcher(props.index)))
88+
.map(query => query.replace.message)
89+
})
90+
const authorErrors = computed(() => [...emailErrors.value, ...orcidErrors.value, ...duplicateErrors.value])
8591
return {
8692
authorErrors
8793
}

src/components/IdentifierCardViewing.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<q-card
33
bordered
4-
v-bind:class="['bg-formcard', identifierValueErrors.length > 0 ? 'red-border has-error' : '']"
4+
v-bind:class="['bg-formcard', identifierErrors.length > 0 ? 'red-border has-error' : '']"
55
flat
66
style="display: flex; flex-direction: row"
77
>
@@ -43,7 +43,7 @@
4343

4444
<script lang="ts">
4545
import { PropType, computed, defineComponent } from 'vue'
46-
import { byError, identifierValueQueries } from 'src/error-filtering'
46+
import { byError, duplicateIdentifierQueries, duplicateMatcher, identifierValueQueries } from 'src/error-filtering'
4747
import { IdentifierType } from 'src/types'
4848
import { useValidation } from 'src/store/validation'
4949
@@ -70,8 +70,15 @@ export default defineComponent({
7070
.filter(byError(errors.value))
7171
.map(query => query.replace.message)
7272
})
73+
const duplicateErrors = computed(() => {
74+
return duplicateIdentifierQueries
75+
.filter(byError(errors.value))
76+
.filter(byError(errors.value, duplicateMatcher(props.index)))
77+
.map(query => query.replace.message)
78+
})
79+
const identifierErrors = computed(() => [...identifierValueErrors.value, ...duplicateErrors.value])
7380
return {
74-
identifierValueErrors
81+
identifierErrors
7582
}
7683
},
7784
emits: ['editPressed', 'moveDown', 'moveUp']

src/components/Keyword.vue

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
dense
88
outlined
99
placeholder="Type a keyword"
10-
v-bind:class="keywordErrors.length > 0 ? 'has-error' : ''"
10+
v-bind:class="validationErrors.length > 0 ? 'has-error' : ''"
1111
v-bind:model-value="keyword"
12-
v-bind:error="keywordErrors.length > 0"
13-
v-bind:error-message="keywordErrors.join(', ')"
12+
v-bind:error="validationErrors.length > 0"
13+
v-bind:error-message="validationErrors.join(', ')"
1414
v-on:update:modelValue="$emit('update', $event)"
1515
ref="keywordRef"
1616
/>
@@ -44,7 +44,7 @@
4444
</template>
4545

4646
<script lang="ts">
47-
import { byError, keywordQueries } from 'src/error-filtering'
47+
import { byError, duplicateKeywordQueries, duplicateMatcher, keywordQueries } from 'src/error-filtering'
4848
import { computed, defineComponent } from 'vue'
4949
import { useValidation } from 'src/store/validation'
5050
@@ -71,8 +71,15 @@ export default defineComponent({
7171
.filter(byError(errors.value))
7272
.map(query => query.replace.message)
7373
})
74+
const duplicateErrors = computed(() => {
75+
return duplicateKeywordQueries
76+
.filter(byError(errors.value))
77+
.filter(byError(errors.value, duplicateMatcher(props.index)))
78+
.map(query => query.replace.message)
79+
})
80+
const validationErrors = computed(() => [...keywordErrors.value, ...duplicateErrors.value])
7481
return {
75-
keywordErrors
82+
validationErrors
7683
}
7784
},
7885
emits: ['moveDown', 'moveUp', 'removePressed', 'update']

src/components/ScreenAuthors.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export default defineComponent({
102102
})
103103
const { authors, setAuthors } = useCff()
104104
const { errors } = useValidation()
105-
const editingId = ref(0)
105+
const editingId = ref(-1)
106106
const addAuthor = async () => {
107107
const newAuthor: AuthorType = {}
108108
const newAuthors = [...authors.value, newAuthor]

src/error-filtering.ts

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,29 @@ export type ErrorQuery = {
1414
replace: ErrorQueryReplace
1515
}
1616

17-
export const byError = (errors: ErrorObject[]) => {
17+
export interface Comparator { (error: ErrorObject, query: ErrorQuery): boolean }
18+
19+
const defaultMatcher: Comparator = (error, query) => {
20+
const keys = Object.keys(query.find)
21+
if (keys.includes('instancePath') && query.find.instancePath !== error.instancePath) {
22+
return false
23+
}
24+
if (keys.includes('schemaPath') && query.find.schemaPath !== error.schemaPath) {
25+
return false
26+
}
27+
if (keys.includes('message') && query.find.message !== error.message) {
28+
return false
29+
}
30+
return true
31+
}
32+
33+
export const duplicateMatcher = (index: number) => {
34+
return (error: ErrorObject) => error.params.i === index || error.params.j === index
35+
}
36+
37+
export const byError = (errors: ErrorObject[], matcher: Comparator = defaultMatcher) => {
1838
return (query: ErrorQuery) => {
19-
const matches = (error: ErrorObject) => {
20-
const keys = Object.keys(query.find)
21-
if (keys.includes('instancePath') && query.find.instancePath !== error.instancePath) {
22-
return false
23-
}
24-
if (keys.includes('schemaPath') && query.find.schemaPath !== error.schemaPath) {
25-
return false
26-
}
27-
if (keys.includes('message') && query.find.message !== error.message) {
28-
return false
29-
}
30-
return true
31-
}
32-
return errors.some(matches)
39+
return errors.some((error: ErrorObject) => matcher(error, query))
3340
}
3441
}
3542

@@ -62,6 +69,36 @@ export const dateReleasedQueries: ErrorQuery[] = [{
6269
}
6370
}]
6471

72+
export const duplicateAuthorQueries: ErrorQuery[] = [{
73+
find: {
74+
instancePath: '/authors',
75+
schemaPath: '#/properties/authors/uniqueItems'
76+
},
77+
replace: {
78+
message: 'This author is a duplicate.'
79+
}
80+
}]
81+
82+
export const duplicateIdentifierQueries: ErrorQuery[] = [{
83+
find: {
84+
instancePath: '/identifiers',
85+
schemaPath: '#/properties/identifiers/uniqueItems'
86+
},
87+
replace: {
88+
message: 'This identifier is a duplicate.'
89+
}
90+
}]
91+
92+
export const duplicateKeywordQueries: ErrorQuery[] = [{
93+
find: {
94+
instancePath: '/keywords',
95+
schemaPath: '#/properties/keywords/uniqueItems'
96+
},
97+
replace: {
98+
message: 'This keyword is a duplicate.'
99+
}
100+
}]
101+
65102
export const emailQueries = (index: number) => {
66103
return [{
67104
find: {

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"extends": "@quasar/app/tsconfig-preset",
33
"compilerOptions": {
4-
"baseUrl": "."
4+
"baseUrl": ".",
5+
"jsx": "react-jsx"
56
}
67
}

0 commit comments

Comments
 (0)