Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
<template>
<v-card>
<v-card-title class="bg-grey-lighten-4 d-flex align-center ga-3 px-6 py-4">
<v-icon color="accent">mdi-clipboard-text-outline</v-icon>
<span>Additional Details</span>
</v-card-title>
<v-divider />
<v-card-text class="pa-6 pa-md-8">
<div>
<div class="text-overline text-grey-darken-1 mb-2">
2. Level of detail appropriate for the Traditional Knowledge (TK)
</div>
<ul
v-if="!isEmpty(detailLevelLabels)"
class="ms-4"
>
<li
v-for="label of detailLevelLabels"
:key="label"
>
{{ label }}
</li>
</ul>
<div
v-else
class="text-grey-darken-3"
>
Not specified
</div>
<div
v-if="!isEmpty(detailNotes)"
class="mt-2"
>
<div class="text-body-2 text-grey-darken-1">Additional requirements</div>
<div class="text-grey-darken-3 whitespace-pre-wrap">{{ detailNotes }}</div>
</div>
</div>

<v-divider class="my-6" />

<div>
<div class="text-overline text-grey-darken-1 mb-2">
3. Format(s) the Traditional Knowledge (TK) is shared in
</div>
<ul
v-if="!isEmpty(formatLabels)"
class="ms-4"
>
<li
v-for="label of formatLabels"
:key="label"
>
{{ label }}
</li>
</ul>
<div
v-else
class="text-grey-darken-3"
>
Not specified
</div>
</div>

<v-divider class="my-6" />

<div>
<div class="text-overline text-grey-darken-1 mb-2">
7. Any reference to the Traditional Knowledge (TK) is credited to
</div>
<ul
v-if="!isEmpty(creditLineLabels)"
class="ms-4"
>
<li
v-for="label of creditLineLabels"
:key="label"
>
{{ label }}
</li>
</ul>
<div
v-else
class="text-grey-darken-3"
>
Not specified
</div>
<div
v-if="!isEmpty(creditNotes)"
class="mt-2"
>
<div class="text-body-2 text-grey-darken-1">Credit details</div>
<div class="text-grey-darken-3 whitespace-pre-wrap">{{ creditNotes }}</div>
</div>
</div>

<v-divider class="my-6" />

<div>
<div class="text-overline text-grey-darken-1 mb-2">8. Expiration notifications</div>
<ul
v-if="!isEmpty(expirationActionLabels)"
class="ms-4"
>
<li
v-for="label of expirationActionLabels"
:key="label"
>
{{ label }}
</li>
</ul>
<div
v-else
class="text-grey-darken-3"
>
Not specified
</div>
<div
v-if="!isEmpty(expirationNotes)"
class="mt-2"
>
<div class="text-body-2 text-grey-darken-1">Additional expiration requirements</div>
<div class="text-grey-darken-3 whitespace-pre-wrap">{{ expirationNotes }}</div>
</div>
</div>

<v-divider class="my-6" />

<div>
<div class="text-overline text-grey-darken-1 mb-2">
9. In the event of a breach or violation of this agreement
</div>
<ul
v-if="!isEmpty(breachActionLabels)"
class="ms-4"
>
<li
v-for="label of breachActionLabels"
:key="label"
>
{{ label }}
</li>
</ul>
<div
v-else
class="text-grey-darken-3"
>
Not specified
</div>
<div
v-if="!isEmpty(breachNotes)"
class="mt-2"
>
<div class="text-body-2 text-grey-darken-1">Additional breach measures</div>
<div class="text-grey-darken-3 whitespace-pre-wrap">{{ breachNotes }}</div>
</div>
</div>

<v-divider class="my-6" />

<div>
<div class="text-overline text-grey-darken-1 mb-2">10. Compelled disclosure</div>
<div
v-if="!isEmpty(disclosureNotes)"
class="text-grey-darken-3 whitespace-pre-wrap"
>
{{ disclosureNotes }}
</div>
<div
v-else
class="text-grey-darken-3"
>
Not specified
</div>
</div>
</v-card-text>
</v-card>
</template>

<script setup lang="ts">
import { computed } from "vue"
import { useI18n } from "vue-i18n"
import { isEmpty, isNil } from "lodash"

import {
InformationSharingAgreementBreachActions,
InformationSharingAgreementCreditLines,
InformationSharingAgreementDetailLevels,
InformationSharingAgreementExpirationActions,
InformationSharingAgreementFormats,
} from "@/api/information-sharing-agreement-options"

const props = defineProps<{
detailLevel: string | null | undefined
detailNotes: string | null | undefined
formats: string | null | undefined
creditLines: string | null | undefined
creditNotes: string | null | undefined
expirationActions: string | null | undefined
expirationNotes: string | null | undefined
breachActions: string | null | undefined
breachNotes: string | null | undefined
disclosureNotes: string | null | undefined
}>()

const { t } = useI18n()

/** Maps a comma-joined column to its translated option labels. See TK-44. */
function selectedLabels(
commaJoinedValues: string | null | undefined,
translationKey: string,
allowedValues: string[]
): string[] {
if (isNil(commaJoinedValues) || isEmpty(commaJoinedValues)) return []

return commaJoinedValues
.split(",")
.filter((value) => allowedValues.includes(value))
.map((value) => t(`informationSharingAgreement.${translationKey}.${value}`))
}

const detailLevelLabels = computed(() =>
selectedLabels(
props.detailLevel,
"detailLevels",
Object.values(InformationSharingAgreementDetailLevels)
)
)
const formatLabels = computed(() =>
selectedLabels(props.formats, "formats", Object.values(InformationSharingAgreementFormats))
)
const creditLineLabels = computed(() =>
selectedLabels(
props.creditLines,
"creditLines",
Object.values(InformationSharingAgreementCreditLines)
)
)
const expirationActionLabels = computed(() =>
selectedLabels(
props.expirationActions,
"expirationActions",
Object.values(InformationSharingAgreementExpirationActions)
)
)
const breachActionLabels = computed(() =>
selectedLabels(
props.breachActions,
"breachActions",
Object.values(InformationSharingAgreementBreachActions)
)
)
</script>

<style scoped>
.whitespace-pre-wrap {
white-space: pre-wrap; /* preserves line breaks and wraps long text */
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@
:authorized-application="informationSharingAgreement.authorizedApplication"
/>

<InformationSharingAgreementAdditionalDetailsCard
class="mt-6 rounded-lg"
:detail-level="informationSharingAgreement.detailLevel"
:detail-notes="informationSharingAgreement.detailNotes"
:formats="informationSharingAgreement.formats"
:credit-lines="informationSharingAgreement.creditLines"
:credit-notes="informationSharingAgreement.creditNotes"
:expiration-actions="informationSharingAgreement.expirationActions"
:expiration-notes="informationSharingAgreement.expirationNotes"
:breach-actions="informationSharingAgreement.breachActions"
:breach-notes="informationSharingAgreement.breachNotes"
:disclosure-notes="informationSharingAgreement.disclosureNotes"
/>

<div class="mt-4 d-flex flex-column flex-md-row justify-space-between ga-3 px-6 py-4">
<InformationSharingAgreementActionsMenu
:information-sharing-agreement-id="informationSharingAgreementIdAsNumber"
Expand Down Expand Up @@ -83,6 +97,7 @@ import useInformationSharingAgreement from "@/use/use-information-sharing-agreem

import InformationSharingAgreementAccessCard from "@/components/information-sharing-agreements/InformationSharingAgreementAccessCard.vue"
import InformationSharingAgreementActionsMenu from "@/components/information-sharing-agreements/InformationSharingAgreementActionsMenu.vue"
import InformationSharingAgreementAdditionalDetailsCard from "@/components/information-sharing-agreements/InformationSharingAgreementAdditionalDetailsCard.vue"
import InformationSharingAgreementBasicInformationCard from "@/components/information-sharing-agreements/InformationSharingAgreementBasicInformationCard.vue"
import InformationSharingAgreementConfidentialityCard from "@/components/information-sharing-agreements/InformationSharingAgreementConfidentialityCard.vue"
import InformationSharingAgreementDurationCard from "@/components/information-sharing-agreements/InformationSharingAgreementDurationCard.vue"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { describe, expect, it } from "vitest"
import { mount } from "@vue/test-utils"

import { mockVuetify } from "@/tests/support"
import i18n from "@/plugins/vue-i18n-plugin"

import InformationSharingAgreementAdditionalDetailsCard from "@/components/information-sharing-agreements/InformationSharingAgreementAdditionalDetailsCard.vue"

function mountCard(
props: Partial<
InstanceType<typeof InformationSharingAgreementAdditionalDetailsCard>["$props"]
> = {}
) {
return mount(InformationSharingAgreementAdditionalDetailsCard, {
props: {
detailLevel: null,
detailNotes: null,
formats: null,
creditLines: null,
creditNotes: null,
expirationActions: null,
expirationNotes: null,
breachActions: null,
breachNotes: null,
disclosureNotes: null,
...props,
},
global: {
plugins: [mockVuetify(), i18n],
},
})
}

describe("InformationSharingAgreementAdditionalDetailsCard.vue", () => {
it("renders the translated labels for selected comma-joined options", () => {
const wrapper = mountCard({
detailLevel: "original,summary",
formats: "word_documents,photos",
creditLines: "external_organization",
})

const text = wrapper.text()
expect(text).toContain("Original: No alterations or summary")
expect(text).toContain("Summary: Notes, high-level summary, redacted portions")
expect(text).toContain("Word documents")
expect(text).toContain("Photos")
expect(text).toContain("Yukon First Nation or Indigenous Government")
})

it("renders the free-text notes for completed sections", () => {
const wrapper = mountCard({
detailNotes: "Review required before summarizing.",
disclosureNotes: "Notify the community before any compelled disclosure.",
})

const text = wrapper.text()
expect(text).toContain("Review required before summarizing.")
expect(text).toContain("Notify the community before any compelled disclosure.")
})

it("shows a placeholder for every optional section when nothing is filled in", () => {
const wrapper = mountCard()

const placeholders = wrapper.text().match(/Not specified/g) ?? []
// Sections 2, 3, 7, 8, 9 and 10 each fall back to the placeholder.
expect(placeholders).toHaveLength(6)
})

it("ignores values that are not valid options", () => {
const wrapper = mountCard({ formats: "word_documents,not_a_real_format" })

const text = wrapper.text()
expect(text).toContain("Word documents")
expect(text).not.toContain("not_a_real_format")
})
})