WorkflowsUserTeam.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <template>
  2. <p v-if="!userStore.user" class="text-center my-4">
  3. <ui-spinner v-if="!userStore.retrieved" color="text-accent" />
  4. <template v-else>
  5. You must
  6. <a href="https://www.automa.site/auth" class="underline" target="_blank"
  7. >login</a
  8. >
  9. to use these workflows
  10. </template>
  11. </p>
  12. <div
  13. v-else-if="!isUnknownTeam && teamWorkflows.length === 0"
  14. class="text-center"
  15. >
  16. <img src="@/assets/svg/files-and-folder.svg" class="mx-auto w-96" />
  17. <p class="text-lg font-semibold">Nothing to see here</p>
  18. <p class="text-gray-600 dark:text-gray-200">
  19. Browse workflows that been shared by your team
  20. </p>
  21. <ui-button
  22. :href="`http://www.automa.site/workflows?teamId=${teamId}&workflowsBy=team`"
  23. tag="a"
  24. target="_blank"
  25. variant="accent"
  26. class="mt-8 inline-block"
  27. >
  28. Browse workflows
  29. </ui-button>
  30. </div>
  31. <div v-else class="workflows-container">
  32. <shared-card
  33. v-for="workflow in workflows"
  34. :key="workflow.id"
  35. :data="workflow"
  36. :menu="workflowMenus"
  37. :disabled="isUnknownTeam"
  38. @menuSelected="onMenuSelected"
  39. @execute="executeWorkflow(workflow)"
  40. @click="openWorkflowPage"
  41. >
  42. <template #footer-content>
  43. <span
  44. :class="tagColors[workflow.tag]"
  45. class="text-sm rounded-md text-black capitalize p-1"
  46. >
  47. {{ workflow.tag }}
  48. </span>
  49. </template>
  50. </shared-card>
  51. </div>
  52. </template>
  53. <script setup>
  54. import { computed } from 'vue';
  55. import { useI18n } from 'vue-i18n';
  56. import { useRouter } from 'vue-router';
  57. import { useToast } from 'vue-toastification';
  58. import { fetchApi } from '@/utils/api';
  59. import { useUserStore } from '@/stores/user';
  60. import { useTeamWorkflowStore } from '@/stores/teamWorkflow';
  61. import { arraySorter } from '@/utils/helper';
  62. import { useDialog } from '@/composable/dialog';
  63. import { tagColors } from '@/utils/shared';
  64. import { executeWorkflow } from '@/newtab/utils/workflowEngine';
  65. import SharedCard from '@/components/newtab/shared/SharedCard.vue';
  66. const props = defineProps({
  67. active: Boolean,
  68. search: {
  69. type: String,
  70. default: '',
  71. },
  72. teamId: {
  73. type: [String, Number],
  74. default: '',
  75. },
  76. sort: {
  77. type: Object,
  78. default: () => ({
  79. by: '',
  80. order: '',
  81. }),
  82. },
  83. });
  84. const menu = [
  85. {
  86. id: 'delete',
  87. name: 'Delete',
  88. hasAccess: true,
  89. icon: 'riDeleteBin7Line',
  90. attrs: {
  91. class: 'text-red-400 dark:text-red-500',
  92. },
  93. },
  94. {
  95. id: 'delete-team',
  96. name: 'Delete from team',
  97. icon: 'riDeleteBin7Line',
  98. permissions: ['owner', 'create'],
  99. attrs: {
  100. class: 'text-red-400 dark:text-red-500',
  101. },
  102. },
  103. ];
  104. const { t } = useI18n();
  105. const toast = useToast();
  106. const dialog = useDialog();
  107. const router = useRouter();
  108. const userStore = useUserStore();
  109. const teamWorkflowStore = useTeamWorkflowStore();
  110. const isUnknownTeam = computed(() => props.teamId === '(unknown)');
  111. const workflowMenus = computed(() =>
  112. menu.filter((item) => {
  113. if (!item.permissions) return true;
  114. return userStore.validateTeamAccess(props.teamId, item.permissions);
  115. })
  116. );
  117. const teamWorkflows = computed(() => {
  118. if (isUnknownTeam.value) {
  119. return Object.keys(teamWorkflowStore.workflows).reduce((acc, teamId) => {
  120. const teamExist = userStore.user?.teams?.some(
  121. (team) => team.id === teamId || team.id === +teamId
  122. );
  123. if (!teamExist) {
  124. acc.push(...Object.values(teamWorkflowStore.workflows[teamId]));
  125. }
  126. return acc;
  127. }, []);
  128. }
  129. return teamWorkflowStore.getByTeam(props.teamId);
  130. });
  131. const workflows = computed(() => {
  132. if (!props.active) return [];
  133. const filtered = teamWorkflows.value.filter(({ name }) =>
  134. name.toLocaleLowerCase().includes(props.search.toLocaleLowerCase())
  135. );
  136. return arraySorter({
  137. data: filtered,
  138. key: props.sort.by,
  139. order: props.sort.order,
  140. });
  141. });
  142. function onMenuSelected({ id, data }) {
  143. if (id === 'delete') {
  144. dialog.confirm({
  145. title: t('workflow.delete'),
  146. okVariant: 'danger',
  147. body: t('message.delete', { name: data.name }),
  148. onConfirm: () => {
  149. teamWorkflowStore.delete(data.teamId, data.id);
  150. },
  151. });
  152. } else if (id === 'delete-team') {
  153. dialog.confirm({
  154. async: true,
  155. title: 'Delete workflow from team',
  156. okVariant: 'danger',
  157. body: `Are you sure want to delete the "${data.name}" workflow from this team?`,
  158. onConfirm: async () => {
  159. try {
  160. const response = await fetchApi(
  161. `/teams/${props.teamId}/workflows/${data.id}`,
  162. { method: 'DELETE' }
  163. );
  164. const result = await response.json();
  165. if (!response.ok && response.status !== 404)
  166. throw new Error(result.message);
  167. await teamWorkflowStore.delete(props.teamId, data.id);
  168. return true;
  169. } catch (error) {
  170. toast.error('Something went wrong');
  171. console.error(error);
  172. return false;
  173. }
  174. },
  175. });
  176. }
  177. }
  178. function openWorkflowPage({ id }) {
  179. if (isUnknownTeam.value) return;
  180. router.push(`/teams/${props.teamId}/workflows/${id}`);
  181. }
  182. </script>