Team.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. <template>
  2. <div class="border border-dividerLight rounded flex flex-1 items-end">
  3. <div class="flex flex-1 items-start">
  4. <div class="p-4">
  5. <label
  6. class="cursor-pointer transition hover:text-secondaryDark"
  7. @click="team.myRole === 'OWNER' ? $emit('edit-team') : ''"
  8. >
  9. {{ team.name || $t("state.nothing_found") }}
  10. </label>
  11. <div class="flex -space-x-1 mt-2 overflow-hidden">
  12. <img
  13. v-for="(member, index) in team.members"
  14. :key="`member-${index}`"
  15. v-tippy="{ theme: 'tooltip' }"
  16. :title="member.user.displayName"
  17. :src="member.user.photoURL || undefined"
  18. :alt="member.user.displayName"
  19. class="rounded-full h-5 ring-primary ring-2 w-5 inline-block"
  20. />
  21. </div>
  22. </div>
  23. </div>
  24. <span>
  25. <tippy ref="options" interactive trigger="click" theme="popover" arrow>
  26. <template #trigger>
  27. <ButtonSecondary
  28. v-tippy="{ theme: 'tooltip' }"
  29. :title="$t('action.more')"
  30. svg="more-vertical"
  31. />
  32. </template>
  33. <SmartItem
  34. v-if="team.myRole === 'OWNER'"
  35. svg="edit"
  36. :label="$t('action.edit').toString()"
  37. @click.native="
  38. () => {
  39. $emit('edit-team')
  40. $refs.options.tippy().hide()
  41. }
  42. "
  43. />
  44. <SmartItem
  45. v-if="team.myRole === 'OWNER'"
  46. svg="trash-2"
  47. color="red"
  48. :label="$t('action.delete').toString()"
  49. @click.native="
  50. () => {
  51. deleteTeam()
  52. $refs.options.tippy().hide()
  53. }
  54. "
  55. />
  56. <SmartItem
  57. v-if="!(team.myRole === 'OWNER' && team.ownersCount == 1)"
  58. svg="trash"
  59. :label="$t('team.exit').toString()"
  60. @click.native="
  61. () => {
  62. exitTeam()
  63. $refs.options.tippy().hide()
  64. }
  65. "
  66. />
  67. </tippy>
  68. </span>
  69. </div>
  70. </template>
  71. <script setup lang="ts">
  72. import { useContext } from "@nuxtjs/composition-api"
  73. import { pipe } from "fp-ts/function"
  74. import * as TE from "fp-ts/TaskEither"
  75. import {
  76. deleteTeam as backendDeleteTeam,
  77. leaveTeam,
  78. } from "~/helpers/backend/mutations/Team"
  79. import { TeamMemberRole } from "~/helpers/backend/types/TeamMemberRole"
  80. const props = defineProps<{
  81. team: {
  82. name: string
  83. myRole: TeamMemberRole
  84. ownersCount: number
  85. members: Array<{
  86. user: {
  87. displayName: string
  88. photoURL: string | null
  89. }
  90. }>
  91. }
  92. teamID: string
  93. }>()
  94. const {
  95. app: { i18n },
  96. $toast,
  97. } = useContext()
  98. const t = i18n.t.bind(i18n)
  99. const deleteTeam = () => {
  100. if (!confirm(t("confirm.remove_team").toString())) return
  101. pipe(
  102. backendDeleteTeam(props.teamID),
  103. TE.match(
  104. (err) => {
  105. // TODO: Better errors ? We know the possible errors now
  106. $toast.error(t("error.something_went_wrong").toString(), {
  107. icon: "error_outline",
  108. })
  109. console.error(err)
  110. },
  111. () => {
  112. $toast.success(t("team.deleted").toString(), {
  113. icon: "done",
  114. })
  115. }
  116. )
  117. )() // Tasks (and TEs) are lazy, so call the function returned
  118. }
  119. const exitTeam = () => {
  120. if (!confirm("Are you sure you want to exit this team?")) return
  121. pipe(
  122. leaveTeam(props.teamID),
  123. TE.match(
  124. (err) => {
  125. // TODO: Better errors ?
  126. $toast.error(t("error.something_went_wrong").toString(), {
  127. icon: "error_outline",
  128. })
  129. console.error(err)
  130. },
  131. () => {
  132. $toast.success(t("team.left").toString(), {
  133. icon: "done",
  134. })
  135. }
  136. )
  137. )() // Tasks (and TEs) are lazy, so call the function returned
  138. }
  139. </script>