hotfix (dashboard): display allow email contact in card box client

- Added the `allowEmailContact` property to the `CardBoxClient` component to display the email contact status.
- Added the `allowEmailContact` computed property to the `Person` model to determine if email contact is allowed based on the related datasets.
- Preloaded the datasets relation in the `AuthorsController` to access the pivot attributes.
- Updated the `Dashboard.vue` to pass the `allowEmailContact` prop to the `CardBoxClient` component.
- Updated the `array_contains_types` validation rule to correct the error message for descriptions.
- Updated the `FormCheckRadio.vue` to correctly handle the radio button and checkbox components.
This commit is contained in:
Kaimbacher 2025-03-31 15:14:34 +02:00
parent 09f65359f9
commit f89b119b18
6 changed files with 31 additions and 49 deletions

View file

@ -9,6 +9,7 @@ export default class AuthorsController {
// where exists (select * from gba.documents inner join gba.link_documents_persons on "documents"."id" = "link_documents_persons"."document_id" // where exists (select * from gba.documents inner join gba.link_documents_persons on "documents"."id" = "link_documents_persons"."document_id"
// where ("link_documents_persons"."role" = 'author') and ("persons"."id" = "link_documents_persons"."person_id")); // where ("link_documents_persons"."role" = 'author') and ("persons"."id" = "link_documents_persons"."person_id"));
const authors = await Person.query() const authors = await Person.query()
.preload('datasets')
.where('name_type', 'Personal') .where('name_type', 'Personal')
.whereHas('datasets', (dQuery) => { .whereHas('datasets', (dQuery) => {
dQuery.wherePivot('role', 'author'); dQuery.wherePivot('role', 'author');

View file

@ -3,7 +3,7 @@ import { DateTime } from 'luxon';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import Dataset from './dataset.js'; import Dataset from './dataset.js';
import BaseModel from './base_model.js'; import BaseModel from './base_model.js';
import type { ManyToMany } from "@adonisjs/lucid/types/relations"; import type { ManyToMany } from '@adonisjs/lucid/types/relations';
export default class Person extends BaseModel { export default class Person extends BaseModel {
public static namingStrategy = new SnakeCaseNamingStrategy(); public static namingStrategy = new SnakeCaseNamingStrategy();
@ -64,9 +64,8 @@ export default class Person extends BaseModel {
// return '2023-03-21 08:45:00'; // return '2023-03-21 08:45:00';
// } // }
@computed({ @computed({
serializeAs: 'dataset_count', serializeAs: 'dataset_count',
}) })
public get datasetCount() { public get datasetCount() {
const stock = this.$extras.datasets_count; //my pivot column name was "stock" const stock = this.$extras.datasets_count; //my pivot column name was "stock"
@ -79,6 +78,16 @@ export default class Person extends BaseModel {
return contributor_type; return contributor_type;
} }
@computed({ serializeAs: 'allow_email_contact' })
public get allowEmailContact() {
// If the datasets relation is missing or empty, return false instead of null.
if (!this.datasets || this.datasets.length === 0) {
return false;
}
// Otherwise return the pivot attribute from the first related dataset.
return this.datasets[0].$extras?.pivot_allow_email_contact;
}
@manyToMany(() => Dataset, { @manyToMany(() => Dataset, {
pivotForeignKey: 'person_id', pivotForeignKey: 'person_id',
pivotRelatedForeignKey: 'document_id', pivotRelatedForeignKey: 'document_id',

View file

@ -39,6 +39,10 @@ const props = defineProps({
type: String, type: String,
default: null, default: null,
}, },
allowEmailContact: {
type: Boolean,
default: false,
}
}); });
const pillType = computed(() => { const pillType = computed(() => {
@ -81,9 +85,8 @@ const pillType = computed(() => {
<h4 class="text-xl text-ellipsis"> <h4 class="text-xl text-ellipsis">
{{ name }} {{ name }}
</h4> </h4>
<p class="text-gray-500 dark:text-slate-400"> <p class="text-gray-500 dark:text-slate-400">
<!-- {{ date }} @ {{ login }} --> <div v-if="props.allowEmailContact"> {{ email }}</div>
{{ email }}
</p> </p>
</div> </div>
</BaseLevel> </BaseLevel>

View file

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
interface Props { interface Props {
name: string; name: string;
type?: 'checkbox' | 'radio' | 'switch'; type?: 'checkbox' | 'radio' | 'switch';
@ -10,58 +9,27 @@ interface Props {
inputValue: string | number | boolean; inputValue: string | number | boolean;
} }
const props = defineProps({ const props = defineProps<Props>();
name: {
type: String,
required: true,
},
type: {
type: String,
default: 'checkbox',
validator: (value: string) => ['checkbox', 'radio', 'switch'].includes(value),
},
label: {
type: String,
default: null,
},
modelValue: {
type: [Array, String, Number, Boolean],
default: null,
},
inputValue: {
type: [String, Number, Boolean],
required: true,
},
});// const props = defineProps<Props>();
// const emit = defineEmits(['update:modelValue']);
const emit = defineEmits<{ (e: 'update:modelValue', value: Props['modelValue']): void }>(); const emit = defineEmits<{ (e: 'update:modelValue', value: Props['modelValue']): void }>();
const computedValue = computed({ const computedValue = computed({
get: () => props.modelValue, get: () => props.modelValue,
set: (value) => { set: (value) => {
// If type is radio, wrap the new value inside an array. emit('update:modelValue', props.type === 'radio' ? [value] : value);
if (props.type === 'radio') {
emit('update:modelValue', [value]);
} else {
emit('update:modelValue', value);
}
}, },
}); });
const inputType = computed(() => (props.type === 'radio' ? 'radio' : 'checkbox')); const inputType = computed(() => (props.type === 'radio' ? 'radio' : 'checkbox'));
// Define isChecked for radio inputs: it's true when the current modelValue equals the inputValue // Define isChecked for radio inputs: it's true when the current modelValue equals the inputValue
const isChecked = computed(() => { const isChecked = computed(() => {
if (props.type === 'radio') { if (Array.isArray(computedValue.value) && computedValue.value.length > 0) {
return Array.isArray(computedValue.value) && return props.type === 'radio'
computedValue.value.length > 0 && ? computedValue.value[0] === props.inputValue
computedValue.value[0] === props.inputValue; : computedValue.value.includes(props.inputValue);
} else if (props.type === 'checkbox') { }
return Array.isArray(computedValue.value) && return computedValue.value === props.inputValue;
computedValue.value.length > 0 &&
computedValue.value.includes(props.inputValue);
}
return computedValue.value === props.inputValue;
}); });
</script> </script>

View file

@ -113,6 +113,7 @@ const userHasRoles = (roleNames: Array<string>): boolean => {
:date="client.created_at" :date="client.created_at"
:text="client.identifier_orcid" :text="client.identifier_orcid"
:count="client.dataset_count" :count="client.dataset_count"
/> />
</div> </div>
<div class="flex flex-col justify-between"> <div class="flex flex-col justify-between">

View file

@ -53,9 +53,9 @@ async function arrayContainsTypes(value: unknown, options: Options, field: Field
} else if (field.getFieldPath() === 'descriptions') { } else if (field.getFieldPath() === 'descriptions') {
// For descriptions we expect one abstracts description and minimum one translated description. // For descriptions we expect one abstracts description and minimum one translated description.
if (!hasTypeA && !hasTypeB) { if (!hasTypeA && !hasTypeB) {
errorMessage = 'For descriptions, define one abstracts description and minimum one translated description.'; errorMessage = 'For descriptions, define one abstract description and minimum one translated description.';
} else if (!hasTypeA) { } else if (!hasTypeA) {
errorMessage = 'For descriptions, define one abstracts description.'; errorMessage = 'For descriptions, define one abstract description.';
} else if (!hasTypeB) { } else if (!hasTypeB) {
errorMessage = 'For descriptions, define minimum one translated description.'; errorMessage = 'For descriptions, define minimum one translated description.';
} }