src/pages/accessControl/tabs/teams/Register.vue(217,12): error TS2322: Type '(value: User[]) => void' is not assignable to type '(value: User[] | undefined) => any'.
  Types of parameters 'value' and 'value' are incompatible.
    Type 'User[] | undefined' is not assignable to type 'User[]'.
      Type 'undefined' is not assignable to type 'User[]'.

Register.vue:

<script lang="ts" setup> import { storeToRefs } from 'pinia' import { computed, onMounted, reactive, ref } from 'vue' import type { Team, User } from '@/@types' import type { Profile } from '@/@types/profile' import Breadcrumb from '@/components/breadcrumb/Breadcrumb.vue' import type { BreadcrumbPath } from '@/components/breadcrumb/types' import { Button, Card, CardSection, Form, Input } from '@/components/v2' import { ButtonVisual } from '@/components/v2/button/types' import { translate as tableTranslate } from '@/components/v2/table/translate' import useToast from '@/composables/useToast' import { useMutation, useQuery } from '@/core/graphql/api/conviso' import { createTeamGql, updateTeamGql } from '@/core/graphql/mutations/team' import { teamGql } from '@/core/graphql/queries/team' import { router, useRoute } from '@/router' import { AccessControlRouteEnum } from '@/router/accessControl.routes' import { useSessionStore } from '@/stores/session' import { translate as teamTranslate } from '../../translate' import AccessFields from '../shared/accesses/AccessFields.vue' import { parseAccesses } from '../shared/accesses/helpers' import type { AccessField as AccessFieldType } from '../shared/accesses/types' import ProfileField from '../shared/ProfileField.vue' import { translate as formTranslate } from './translate' import type { CreateTeamInput } from './types' const route = useRoute() const toast = useToast() const sessionStore = useSessionStore() const { company } = storeToRefs(sessionStore) const ft = formTranslate().form const t = teamTranslate().teams const tt = tableTranslate() const teamId = ref(route.params?.teamId) const form = ref() const team = ref<Team>() const users = ref<User[]>() const profile = ref<Profile>() const accesses = ref<AccessFieldType>({ fullAccess: false }) const formData = reactive<CreateTeamInput>({ companyId: String(company.value.id), params: { label: '', userEmails: [], profileIds: [], accesses: { fullAccess: false, }, }, }) const isEditPage = computed(() => !!teamId.value) const breadcrumbPaths = computed<BreadcrumbPath[]>(() => [ { title: t.title, href: router.resolve({ name: AccessControlRouteEnum.TEAMS, params: router.currentRoute.value.params, }).path, }, { title: isEditPage.value ? tt.actions.edit : tt.actions.new, }, ]) const variables = reactive({ companyId: String(company.value.id), id: teamId.value, }) const queryOptions = reactive({ enabled: false }) const queryResult = useQuery(teamGql, variables, queryOptions) const { result: resultTeam, loading: loadingTeam, onResult: onResultTeam, } = queryResult onResultTeam(handleResult) const { mutate: createTeam, onDone: onCreateDone, onError: onCreateError, loading: loadingCreate, } = useMutation(createTeamGql) onCreateDone(handleOnSaveDone) onCreateError(handleOnSaveError) const { mutate: updateTeam, onDone: onUpdateDone, onError: onUpdateError, loading: loadingUpdate, } = useMutation(updateTeamGql) onUpdateDone(handleOnSaveDone) onUpdateError(handleOnSaveError) function handleOnSaveDone() { if (isEditPage.value) { toast.success(ft.messages.updatedSuccess) } else { toast.success(ft.messages.createSuccess) } router.push({ name: AccessControlRouteEnum.TEAMS }) } function handleOnSaveError() { if (isEditPage.value) { toast.error(ft.messages.updatedError) } else { toast.error(ft.messages.createError) } } function handleResult() { const res = resultTeam?.value?.team if (!res) return team.value = res formData.params.label = res?.label formData.params.userEmails = res?.users?.map((u: User) => u.email) users.value = res.users profile.value = res.profiles[0] accesses.value = parseAccesses(res.accesses) } async function createOrUpdate() { const isFormValid = await form.value?.validate() if (!isFormValid) return formData.params.profileIds = [String(profile.value?.id)] if (isEditPage.value) { updateTeam({ input: { ...formData, id: team.value?.id, companyId: undefined }, }) } else { createTeam({ input: { ...formData } }) } } function onUpdateAccesses(value: AccessFieldType) { formData.params.accesses = { fullAccess: value.fullAccess, assetIds: value.assets?.map(v => v.id), projectIds: value.projects?.map(v => v.id), vulnerabilityIds: value.vulnerabilities?.map(v => v.id), } } function onUpdateUsers(value: User[]) { formData.params.userEmails = value.map(u => u.email) } function handleOnMounted() { if (teamId.value) { queryOptions.enabled = true } } onMounted(handleOnMounted) </script><template> <div class="ac-teams-register"> <Breadcrumb class="q-mb-lg" :paths="breadcrumbPaths" />
<div class="ac-title-register">
  <h1 class="q-mb-lg">
    {{ isEditPage ? formData.params?.label : t.actions.new }}
  </h1>

  <Button
    :visual="[ButtonVisual.PRIMARY]"
    flat
    :label="tt.actions.save"
    icon="save"
    :loading="loadingCreate || loadingUpdate"
    @click="createOrUpdate"
  />
</div>

<Form ref="form">
  <Card>
    <h3>Team information</h3>
    <div class="flex column">
      <CardSection>
        <div class="col-md-12 col-md-8 col-md-6">
          <Input
            v-model="formData.params.label"
            :label="ft.label"
            required
            class="ac-team-label"
          />
        </div>
      </CardSection>
      <CardSection>
        <div class="col-md-12 col-md-8 col-md-6">
          <ProfileField v-model:profile="profile" :loading="loadingTeam" />
        </div>
      </CardSection>
    </div>
  </Card>

  <Card>
    <AccessFields
      v-model:accesses="accesses"
      v-model:users="users"
      :loading="loadingTeam"
      @update:accesses="onUpdateAccesses"
      @update:users="onUpdateUsers"
    />
  </Card>
</Form>
</div> </template><style lang="scss"> .ac-teams-register { .q-mb-lg { margin-bottom: 32px; } .ac-title-register { display: flex; flex-direction: row; justify-content: space-between; } .ac-team-label { margin-bottom: -12px !important; } .q-card { margin-top: 16px; } h3 { padding: 16px 0 0 16px; font-size: 20px; font-weight: 700; line-height: 24px; } .toggle-item-title { font-size: 16px; font-weight: 700; } .toggle-item-text { font-size: 12px; font-weight: 400; color: var(--conviso-tertiary-text-color); } } @media (min-width: 800px) { .col-md-12 { flex: 0 0 auto !important; width: 100% !important; } } @media (min-width: 1000px) { .col-md-8 { flex: 0 0 auto !important; width: 80% !important; } } @media (min-width: 1300px) { .col-md-6 { flex: 0 0 auto !important; width: 50% !important; } } </style>
AccessFields.vue:

<script lang="ts" setup> import { computed, ref, watch } from 'vue' import type { User } from '@/@types' import { Card, CardSection, Tab, Tabs, Toggle } from '@/components/v2' import { router } from '@/router' import { translate } from '../../users/translate' import AssetsTab from './AssetsTab.vue' import ProjectsTab from './ProjectsTab.vue' import type { AccessField } from './types' import UsersTab from './UsersTab.vue' import VulnerabilitiesTab from './VulnerabilitiesTab.vue' const t = translate().form defineProps<{ loading?: boolean }>() const emit = defineEmits(['update:accesses']) const form = defineModel<AccessField>('accesses', { default: () => ({ fullAccess: false, assets: [], projects: [], }), }) const users = defineModel<User[]>('users') const isTeamRoute = computed(() => router.currentRoute.value.path.includes('teams'), ) const hideTabs = computed(() => !isTeamRoute.value && form.value.fullAccess) const tab = ref(isTeamRoute.value ? 'users' : 'assets') watch(form, newValue => emit('update:accesses', newValue), { deep: true, }) </script><template> <Card> <h3>{{ t.sections.accessControl.title }}</h3>
<CardSection v-if="!loading" class="ac-access-fields">
  <div class="row toggle-section">
    <div class="toggle-item">
      <p class="toggle-item-title">
        {{ t.sections.accessControl.fullAccess }}
      </p>
      <p class="toggle-item-text">{{ t.sections.accessControl.text }}</p>
    </div>

    <Toggle v-model="form.fullAccess" color="conviso-yellow" />
  </div>

  <section>
    <Tabs v-if="!hideTabs" v-model="tab">
      <Tab v-if="isTeamRoute" :label="t.users" name="users" no-caps />
      <Tab
        :disable="form.fullAccess"
        :label="t.assets"
        name="assets"
        no-caps
      />
      <Tab
        :disable="form.fullAccess"
        :label="t.projects"
        name="projects"
        no-caps
      />
      <Tab
        :disable="form.fullAccess"
        :label="t.vulnerabilities"
        name="vulnerabilities"
        no-caps
      />
    </Tabs>

    <div class="ac-tab-content">
      <template v-if="tab === 'users'">
        <UsersTab v-model:users="users" />
      </template>

      <template v-if="tab === 'assets'">
        <AssetsTab v-if="!form.fullAccess" v-model:assets="form.assets" />
      </template>

      <template v-if="tab === 'projects'">
        <ProjectsTab
          v-if="!form.fullAccess"
          v-model:projects="form.projects"
        />
      </template>

      <template v-if="tab === 'vulnerabilities'">
        <VulnerabilitiesTab
          v-if="!form.fullAccess"
          v-model:vulnerabilities="form.vulnerabilities"
        />
      </template>
    </div>
  </section>
</CardSection>
</Card> </template><style lang="scss"> .ac-access-fields { .q-tab { padding: 0 16px; } .ac-rows { display: flex; flex-direction: row; justify-content: start; } .ac-select-field { margin-bottom: 16px; } .conviso-select-v2-wrapper { margin-top: 16px; } .conviso-button-v2 { margin: 8px 0 0 8px; } .add-item-button { margin-top: 24px; } .ac-select { width: 400px; } .toggle-section { border: 1px solid var(--conviso-shade-color-9); border-radius: var(--conviso-border-radius-xl); justify-content: space-between; margin-bottom: 16px; } .toggle-item { padding: 16px 0 0 16px; flex: 1; word-break: break-word; } .q-toggle { padding: 0 16px; } } </style>

Register.vue:

<script lang="ts" setup> import { storeToRefs } from 'pinia' import { computed, onMounted, reactive, ref } from 'vue' import type { Team, User } from '@/@types' import type { Profile } from '@/@types/profile' import Breadcrumb from '@/components/breadcrumb/Breadcrumb.vue' import type { BreadcrumbPath } from '@/components/breadcrumb/types' import { Button, Card, CardSection, Form, Input } from '@/components/v2' import { ButtonVisual } from '@/components/v2/button/types' import { translate as tableTranslate } from '@/components/v2/table/translate' import useToast from '@/composables/useToast' import { useMutation, useQuery } from '@/core/graphql/api/conviso' import { createTeamGql, updateTeamGql } from '@/core/graphql/mutations/team' import { teamGql } from '@/core/graphql/queries/team' import { router, useRoute } from '@/router' import { AccessControlRouteEnum } from '@/router/accessControl.routes' import { useSessionStore } from '@/stores/session' import { translate as teamTranslate } from '../../translate' import AccessFields from '../shared/accesses/AccessFields.vue' import { parseAccesses } from '../shared/accesses/helpers' import type { AccessField as AccessFieldType } from '../shared/accesses/types' import ProfileField from '../shared/ProfileField.vue' import { translate as formTranslate } from './translate' import type { CreateTeamInput } from './types' const route = useRoute() const toast = useToast() const sessionStore = useSessionStore() const { company } = storeToRefs(sessionStore) const ft = formTranslate().form const t = teamTranslate().teams const tt = tableTranslate() const teamId = ref(route.params?.teamId) const form = ref() const team = ref<Team>() const users = ref<User[]>() const profile = ref<Profile>() const accesses = ref<AccessFieldType>({ fullAccess: false }) const formData = reactive<CreateTeamInput>({ companyId: String(company.value.id), params: { label: '', userEmails: [], profileIds: [], accesses: { fullAccess: false, }, }, }) const isEditPage = computed(() => !!teamId.value) const breadcrumbPaths = computed<BreadcrumbPath[]>(() => [ { title: t.title, href: router.resolve({ name: AccessControlRouteEnum.TEAMS, params: router.currentRoute.value.params, }).path, }, { title: isEditPage.value ? tt.actions.edit : tt.actions.new, }, ]) const variables = reactive({ companyId: String(company.value.id), id: teamId.value, }) const queryOptions = reactive({ enabled: false }) const queryResult = useQuery(teamGql, variables, queryOptions) const { result: resultTeam, loading: loadingTeam, onResult: onResultTeam, } = queryResult onResultTeam(handleResult) const { mutate: createTeam, onDone: onCreateDone, onError: onCreateError, loading: loadingCreate, } = useMutation(createTeamGql) onCreateDone(handleOnSaveDone) onCreateError(handleOnSaveError) const { mutate: updateTeam, onDone: onUpdateDone, onError: onUpdateError, loading: loadingUpdate, } = useMutation(updateTeamGql) onUpdateDone(handleOnSaveDone) onUpdateError(handleOnSaveError) function handleOnSaveDone() { if (isEditPage.value) { toast.success(ft.messages.updatedSuccess) } else { toast.success(ft.messages.createSuccess) } router.push({ name: AccessControlRouteEnum.TEAMS }) } function handleOnSaveError() { if (isEditPage.value) { toast.error(ft.messages.updatedError) } else { toast.error(ft.messages.createError) } } function handleResult() { const res = resultTeam?.value?.team if (!res) return team.value = res formData.params.label = res?.label formData.params.userEmails = res?.users?.map((u: User) => u.email) users.value = res.users profile.value = res.profiles[0] accesses.value = parseAccesses(res.accesses) } async function createOrUpdate() { const isFormValid = await form.value?.validate() if (!isFormValid) return formData.params.profileIds = [String(profile.value?.id)] if (isEditPage.value) { updateTeam({ input: { ...formData, id: team.value?.id, companyId: undefined }, }) } else { createTeam({ input: { ...formData } }) } } function onUpdateAccesses(value: AccessFieldType) { formData.params.accesses = { fullAccess: value.fullAccess, assetIds: value.assets?.map(v => v.id), projectIds: value.projects?.map(v => v.id), vulnerabilityIds: value.vulnerabilities?.map(v => v.id), } } function onUpdateUsers(value: User[]) { formData.params.userEmails = value.map(u => u.email) } function handleOnMounted() { if (teamId.value) { queryOptions.enabled = true } } onMounted(handleOnMounted) </script><template> <div class="ac-teams-register"> <Breadcrumb class="q-mb-lg" :paths="breadcrumbPaths" />
<div class="ac-title-register">
  <h1 class="q-mb-lg">
    {{ isEditPage ? formData.params?.label : t.actions.new }}
  </h1>

  <Button
    :visual="[ButtonVisual.PRIMARY]"
    flat
    :label="tt.actions.save"
    icon="save"
    :loading="loadingCreate || loadingUpdate"
    @click="createOrUpdate"
  />
</div>

<Form ref="form">
  <Card>
    <h3>Team information</h3>
    <div class="flex column">
      <CardSection>
        <div class="col-md-12 col-md-8 col-md-6">
          <Input
            v-model="formData.params.label"
            :label="ft.label"
            required
            class="ac-team-label"
          />
        </div>
      </CardSection>
      <CardSection>
        <div class="col-md-12 col-md-8 col-md-6">
          <ProfileField v-model:profile="profile" :loading="loadingTeam" />
        </div>
      </CardSection>
    </div>
  </Card>

  <Card>
    <AccessFields
      v-model:accesses="accesses"
      v-model:users="users"
      :loading="loadingTeam"
      @update:accesses="onUpdateAccesses"
      @update:users="onUpdateUsers"
    />
  </Card>
</Form>
</div> </template><style lang="scss"> .ac-teams-register { .q-mb-lg { margin-bottom: 32px; } .ac-title-register { display: flex; flex-direction: row; justify-content: space-between; } .ac-team-label { margin-bottom: -12px !important; } .q-card { margin-top: 16px; } h3 { padding: 16px 0 0 16px; font-size: 20px; font-weight: 700; line-height: 24px; } .toggle-item-title { font-size: 16px; font-weight: 700; } .toggle-item-text { font-size: 12px; font-weight: 400; color: var(--conviso-tertiary-text-color); } } @media (min-width: 800px) { .col-md-12 { flex: 0 0 auto !important; width: 100% !important; } } @media (min-width: 1000px) { .col-md-8 { flex: 0 0 auto !important; width: 80% !important; } } @media (min-width: 1300px) { .col-md-6 { flex: 0 0 auto !important; width: 50% !important; } } </style>
AccessFields.vue:

<script lang="ts" setup> import { computed, ref, watch } from 'vue' import type { User } from '@/@types' import { Card, CardSection, Tab, Tabs, Toggle } from '@/components/v2' import { router } from '@/router' import { translate } from '../../users/translate' import AssetsTab from './AssetsTab.vue' import ProjectsTab from './ProjectsTab.vue' import type { AccessField } from './types' import UsersTab from './UsersTab.vue' import VulnerabilitiesTab from './VulnerabilitiesTab.vue' const t = translate().form defineProps<{ loading?: boolean }>() const emit = defineEmits(['update:accesses']) const form = defineModel<AccessField>('accesses', { default: () => ({ fullAccess: false, assets: [], projects: [], }), }) const users = defineModel<User[]>('users') const isTeamRoute = computed(() => router.currentRoute.value.path.includes('teams'), ) const hideTabs = computed(() => !isTeamRoute.value && form.value.fullAccess) const tab = ref(isTeamRoute.value ? 'users' : 'assets') watch(form, newValue => emit('update:accesses', newValue), { deep: true, }) </script><template> <Card> <h3>{{ t.sections.accessControl.title }}</h3>
<CardSection v-if="!loading" class="ac-access-fields">
  <div class="row toggle-section">
    <div class="toggle-item">
      <p class="toggle-item-title">
        {{ t.sections.accessControl.fullAccess }}
      </p>
      <p class="toggle-item-text">{{ t.sections.accessControl.text }}</p>
    </div>

    <Toggle v-model="form.fullAccess" color="conviso-yellow" />
  </div>

  <section>
    <Tabs v-if="!hideTabs" v-model="tab">
      <Tab v-if="isTeamRoute" :label="t.users" name="users" no-caps />
      <Tab
        :disable="form.fullAccess"
        :label="t.assets"
        name="assets"
        no-caps
      />
      <Tab
        :disable="form.fullAccess"
        :label="t.projects"
        name="projects"
        no-caps
      />
      <Tab
        :disable="form.fullAccess"
        :label="t.vulnerabilities"
        name="vulnerabilities"
        no-caps
      />
    </Tabs>

    <div class="ac-tab-content">
      <template v-if="tab === 'users'">
        <UsersTab v-model:users="users" />
      </template>

      <template v-if="tab === 'assets'">
        <AssetsTab v-if="!form.fullAccess" v-model:assets="form.assets" />
      </template>

      <template v-if="tab === 'projects'">
        <ProjectsTab
          v-if="!form.fullAccess"
          v-model:projects="form.projects"
        />
      </template>

      <template v-if="tab === 'vulnerabilities'">
        <VulnerabilitiesTab
          v-if="!form.fullAccess"
          v-model:vulnerabilities="form.vulnerabilities"
        />
      </template>
    </div>
  </section>
</CardSection>
</Card> </template><style lang="scss"> .ac-access-fields { .q-tab { padding: 0 16px; } .ac-rows { display: flex; flex-direction: row; justify-content: start; } .ac-select-field { margin-bottom: 16px; } .conviso-select-v2-wrapper { margin-top: 16px; } .conviso-button-v2 { margin: 8px 0 0 8px; } .add-item-button { margin-top: 24px; } .ac-select { width: 400px; } .toggle-section { border: 1px solid var(--conviso-shade-color-9); border-radius: var(--conviso-border-radius-xl); justify-content: space-between; margin-bottom: 16px; } .toggle-item { padding: 16px 0 0 16px; flex: 1; word-break: break-word; } .q-toggle { padding: 0 16px; } } </style>