Skip to content

Commit

Permalink
feat: multiselect for search hpo-term and category + list view for pa…
Browse files Browse the repository at this point in the history
…tient matches
  • Loading branch information
tada5hi committed Nov 2, 2023
1 parent 93fdcfb commit 0a4af8a
Show file tree
Hide file tree
Showing 12 changed files with 295 additions and 192 deletions.
4 changes: 4 additions & 0 deletions packages/portal/assets/css/bootstrap-override.css
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,7 @@ a:focus{
padding: .5rem;
}


.b-form-tag {
margin-bottom: 0.25rem;
}
36 changes: 36 additions & 0 deletions packages/portal/assets/css/list.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.list {
display: flex;
flex-direction: column;
}

.list .list-items {
padding: 0;
margin: 0;
}

.list-header,
.list-search {
margin-bottom: 0.5rem;
}

.list .list-item {
padding: 0.25rem 0.25rem;
margin-bottom: 0.5rem;
vertical-align: middle;
display: flex;
flex-direction: column;
align-items: center;
}

.list .list-card {
background-color: #ececec;
border: 1px solid #dedede;

box-shadow: 0 4px 25px 0 rgba(0,0,0,.1);
transition: all .3s ease-in-out;

border-radius: 4px;

padding: 0.5rem 1rem;
width: 100%;
}
2 changes: 2 additions & 0 deletions packages/portal/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineNuxtConfig({
css: [
'@fortawesome/fontawesome-free/css/all.css',
'bootstrap/dist/css/bootstrap.css',
'bootstrap-vue-next/dist/bootstrap-vue-next.css',
'@/assets/css/bootstrap-override.css',
'@/assets/css/layout/body.css',
'@/assets/css/layout/footer.css',
Expand All @@ -18,6 +19,7 @@ export default defineNuxtConfig({
'@/assets/css/layout/shared.css',
'@/assets/css/layout/sidebar.css',
'@/assets/css/vue-layout-navigation.css',
'@/assets/css/list.css',
],
alias: {
'@dnpm-dip/core': path.join(__dirname, '..', 'core', 'src'),
Expand Down
1 change: 1 addition & 0 deletions packages/portal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@vue-layout/preset-font-awesome": "^3.0.0",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
"bootstrap-vue-next": "^0.14.10",
"bootstrap": "^5.3.2",
"nuxt": "^3.8.0",
"vue": "^3.3.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default defineComponent({
default: 6,
},
},
emits: ['update:modelValue'],
emits: ['update:modelValue', 'changed'],
async setup(props, { emit }) {
const q = ref('');
q.value = props.modelValue;
Expand Down Expand Up @@ -100,6 +100,7 @@ export default defineComponent({
q.value = option.value;
emit('update:modelValue', option.id);
emit('changed', option);
};
const display = () => {
Expand Down
132 changes: 72 additions & 60 deletions packages/rare-diseases/src/runtime/components/SearchForm.vue
Original file line number Diff line number Diff line change
@@ -1,94 +1,90 @@
<script lang="ts">
import { FormInput, FormInputCheckbox, FormSelect } from '@vue-layout/form-controls';
import { defineComponent, reactive } from 'vue';
import type { FormSelectOption } from '@vue-layout/form-controls';
import { FormInput } from '@vue-layout/form-controls';
import { defineComponent, reactive, ref } from 'vue';
import type { QueryRequestMode, ValueSetCoding } from '@dnpm-dip/core';
import { ValueSetEntity } from '@dnpm-dip/core';
import { useRDAPIClient } from '#imports';
import type { RDQueryCriteriaScopeValue, RDQueryCriteriaScopes } from '../domains';
import FormSelectSearch from './FormSelectSearch.vue';
import Tags from './Tags.vue';
import ValueSetCodings from './ValueSetCodings.vue';
export default defineComponent({
components: {
Tags,
ValueSetCodings,
FormInput,
FormInputCheckbox,
FormSelect,
FormSelectSearch,
ValueSetEntity,
},
emits: ['failed', 'created'],
async setup(props, { emit }) {
// todo: how to know the key of the query request body?
const form = reactive({
// hpoTerms
hpo_term: '',
const categories = ref<FormSelectOption[]>([]);
const hpoTerms = ref<FormSelectOption[]>([]);
// diagnosis
category: '',
const selectCategory = (item: FormSelectOption) => {
const index = categories.value.findIndex((el) => el.id === item.id);
if (index === -1) {
categories.value.push(item);
}
};
const selectHPOTerm = (item: FormSelectOption) => {
const index = hpoTerms.value.findIndex((el) => el.id === item.id);
if (index === -1) {
hpoTerms.value.push(item);
}
};
// variants
const variants = reactive({
gene: '',
cDNAChange: '',
gDNAChange: '',
proteinChange: '',
// todo: what scope?!
vital_status: '',
});
const apiClient = useRDAPIClient();
const submit = async (mode: `${QueryRequestMode}`) => {
let diagnoses : Record<string, RDQueryCriteriaScopeValue> | undefined;
let hpoTerms : RDQueryCriteriaScopeValue | undefined;
let variants : Record<string, RDQueryCriteriaScopeValue> | undefined;
const keys = Object.keys(form);
for (let i = 0; i < keys.length; i++) {
if (form[keys[i]].length === 0) {
continue;
}
switch (keys[i]) {
case 'hpo_term': {
hpoTerms = {
code: form.hpo_term,
};
break;
}
case 'category': {
diagnoses = {
category: {
code: form.category,
},
};
break;
}
case 'vital_status': {
break;
}
default: {
if (typeof variants === 'undefined') {
variants = {};
}
variants[keys[i]] = {
code: form[keys[i]] as string,
const criteria : RDQueryCriteriaScopes = {};
const keys = Object.keys(variants);
if (keys.length > 0) {
let isValid = false;
const group : Record<string, RDQueryCriteriaScopeValue> = {};
for (let i = 0; i < keys.length; i++) {
const code = variants[keys[i] as keyof typeof variants];
if (code.length > 0) {
group[keys[i]] = {
code,
};
break;
isValid = true;
}
}
}
const criteria : RDQueryCriteriaScopes = {};
if (diagnoses) {
criteria.diagnoses = [diagnoses];
if (isValid) {
criteria.variants = [group];
}
}
if (hpoTerms) {
criteria.hpoTerms = [hpoTerms];
if (hpoTerms.value.length > 0) {
criteria.hpoTerms = [];
for (let i = 0; i < hpoTerms.value.length; i++) {
criteria.hpoTerms.push({
code: `${hpoTerms.value[i].id}`,
});
}
}
if (variants) {
criteria.variants = [variants];
if (categories.value.length > 0) {
criteria.diagnoses = [];
for (let i = 0; i < categories.value.length; i++) {
criteria.diagnoses.push({
code: `${categories.value[i].id}`,
});
}
}
try {
Expand All @@ -111,7 +107,13 @@ export default defineComponent({
});
return {
form,
hpoTerms,
categories,
selectCategory,
selectHPOTerm,
form: variants,
submit,
transformCodingsForSelect,
};
Expand All @@ -137,8 +139,8 @@ export default defineComponent({
>
<template #default="options">
<FormSelectSearch
v-model="form.category"
:options="options"
@changed="selectCategory"
/>
</template>
</ValueSetCodings>
Expand All @@ -153,6 +155,11 @@ export default defineComponent({
</div>
</template>
</ValueSetEntity>

<Tags
v-model="categories"
tag-variant="dark"
/>
</div>
<div class="col">
<h6>HPO</h6>
Expand All @@ -170,8 +177,8 @@ export default defineComponent({
>
<template #default="options">
<FormSelectSearch
v-model="form.hpo_term"
:options="options"
@changed="selectHPOTerm"
/>
</template>
</ValueSetCodings>
Expand All @@ -186,6 +193,11 @@ export default defineComponent({
</div>
</template>
</ValueSetEntity>

<Tags
v-model="hpoTerms"
tag-variant="dark"
/>
</div>
</div>
<div class="row mb-2">
Expand Down
83 changes: 83 additions & 0 deletions packages/rare-diseases/src/runtime/components/Tags.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<script lang="ts">
import type { ColorVariant } from 'bootstrap-vue-next';
import { BFormTag } from 'bootstrap-vue-next';
import type { PropType } from 'vue';
import { defineComponent, ref } from 'vue';
type Tag = {
id: string | number,
value: string
};
export default defineComponent({
components: {
BFormTag,
},
props: {
modelValue: {
type: Array as PropType<Tag[]>,
required: true,
},
tagClass: {
type: String as PropType<any>,
},
tagPills: {
type: Boolean,
},
tagValidator: {
type: Function as PropType<(t: string) => boolean>,
},
tagVariant: {
type: String as PropType<ColorVariant>,
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const items = ref<Tag[]>([]);
items.value = props.modelValue;
const drop = (value: string) => {
const index = items.value.findIndex((el) => el.value === value);
if (index !== -1) {
items.value.splice(index, 1);
}
emit('update:modelValue', items);
};
return {
items,
drop,
};
},
});
</script>
<template>
<slot>
<ul class="list-unstyled mb-0 d-flex flex-wrap align-items-center">
<template
v-for="(item, index) in items"
:key="index"
>
<slot
name="tag"
:tag="item.id"
:tag-class="tagClass"
:tag-variant="tagVariant"
:tag-pills="tagPills"
:remove-tag="drop"
>
<BFormTag
:key="item.id"
:class="tagClass"
tag="li"
:variant="tagVariant"
:pill="tagPills"
@remove="drop"
>
{{ item.value }}
</BFormTag>
</slot>
</template>
</ul>
</slot>
</template>
2 changes: 1 addition & 1 deletion packages/rare-diseases/src/runtime/domains/query/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type RDQueryCriteriaScopeValue = {
};

export type RDQueryCriteriaScopes = {
diagnoses?: Record<string, RDQueryCriteriaScopeValue>[],
diagnoses?: RDQueryCriteriaScopeValue[],
hpoTerms?: RDQueryCriteriaScopeValue[],
variants?: Record<string, RDQueryCriteriaScopeValue>[]
};
Expand Down
Loading

0 comments on commit 0a4af8a

Please sign in to comment.