Skip to content
Snippets Groups Projects
ListWorkflowsView.vue 5.93 KiB
<script setup lang="ts">
import { computed, onMounted, reactive } from "vue";
import type { WorkflowOut } from "@/client/workflow";
import WorkflowCard from "@/components/workflows/WorkflowCard.vue";
import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
import dayjs from "dayjs";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { WorkflowService } from "@/client/workflow";
import { environment } from "@/environment";
import ArbitraryWorkflowModal from "@/components/workflows/modals/ArbitraryWorkflowModal.vue";

const workflowsState = reactive<{
  loading: boolean;
  filterString: string;
  sortByAttribute: string;
  sortDesc: boolean;
  workflows: WorkflowOut[];
}>({
  loading: true,
  filterString: "",
  sortByAttribute: "name",
  sortDesc: true,
  workflows: [],
});

const filterFunctionMapping: Record<
  string,
  (a: WorkflowOut, b: WorkflowOut) => boolean
> = {
  name: (a: WorkflowOut, b: WorkflowOut) =>
    workflowsState.sortDesc ? a.name > b.name : a.name < b.name,
  release: (a: WorkflowOut, b: WorkflowOut) => {
    const a_date = dayjs(a.versions[a.versions.length - 1].created_at);
    const b_date = dayjs(b.versions[b.versions.length - 1].created_at);
    return workflowsState.sortDesc
      ? a_date.isBefore(b_date)
      : a_date.isAfter(b_date);
  },
};

function filterWorkflowByString(workflow: WorkflowOut): boolean {
  return workflowsState.filterString.length > 0
    ? workflow.name.includes(workflowsState.filterString)
    : true;
}

function filterWorkflowWithoutVersion(workflow: WorkflowOut): boolean {
  return workflow.versions.length > 0;
}

const processedWorkflows = computed<WorkflowOut[]>(() => {
  return [
    ...workflowsState.workflows.filter(
      (workflow) =>
        filterWorkflowByString(workflow) &&
        filterWorkflowWithoutVersion(workflow)
    ),
  ].sort((a, b) =>
    filterFunctionMapping[workflowsState.sortByAttribute](a, b) ? 1 : -1
  );
});

function fetchWorkflows() {
  WorkflowService.workflowListWorkflows()
    .then((workflows) => {
      workflowsState.workflows = workflows;
    })
    .finally(() => {
      workflowsState.loading = false;
    });
}

onMounted(() => {
  fetchWorkflows();
});
</script>

<template>
  <div class="row m-2 border-bottom mb-4">
    <h1 class="mb-2">Workflows</h1>
  </div>
  <div class="d-flex m-2 mb-3 align-items-center justify-content-between">
    <div class="col-5 me-auto">
      <div class="input-group rounded shadow-sm">
        <span class="input-group-text" id="workflows-search-wrapping"
          ><font-awesome-icon icon="fa-solid fa-magnifying-glass"
        /></span>
        <input
          type="text"
          class="form-control"
          placeholder="Filter Workflows"
          aria-label="Filter Workflows"
          aria-describedby="workflows-search-wrapping"
          :disabled="workflowsState.loading"
          v-model.trim="workflowsState.filterString"
          maxlength="20"
        />
      </div>
    </div>
    <span class="fs-5 me-3 text-shadow">Sort By</span>
    <div
      class="btn-group btn-group-sm w-fit shadow-sm"
      role="group"
      aria-label="Basic radio toggle button group"
    >
      <input
        type="radio"
        class="btn-check"
        name="btnradio"
        id="sortName"
        autocomplete="off"
        checked
        v-model="workflowsState.sortByAttribute"
        value="name"
      />
      <label class="btn btn-outline-secondary" for="sortName"
        >Alphabetical</label
      >

      <input
        type="radio"
        class="btn-check"
        name="btnradio"
        id="sortLatestRelease"
        autocomplete="off"
        v-model="workflowsState.sortByAttribute"
        value="release"
      />
      <label class="btn btn-outline-secondary" for="sortLatestRelease"
        >Latest Release</label
      >
    </div>
    <font-awesome-icon
      :icon="
        workflowsState.sortDesc
          ? 'fa-solid fa-arrow-down-wide-short'
          : 'fa-solid fa-arrow-up-wide-short'
      "
      @click="workflowsState.sortDesc = !workflowsState.sortDesc"
      class="fs-5 ms-3 cursor-pointer"
    />
  </div>
  <div v-if="environment.DEV_SYSTEM" class="d-grid gap-2 col-4 mx-auto">
    <button
      class="btn btn-success btn-lg fs-3"
      role="button"
      data-bs-toggle="modal"
      data-bs-target="#arbitraryWorkflowModal"
    >
      Arbitrary Workflow
    </button>
    <arbitrary-workflow-modal modal-i-d="arbitraryWorkflowModal" />
  </div>
  <div v-if="!workflowsState.loading">
    <div
      v-if="workflowsState.workflows.length === 0"
      class="text-center fs-2 mt-5"
    >
      <font-awesome-icon
        icon="fa-solid fa-x"
        class="my-5 fs-0"
        style="color: var(--bs-secondary)"
      />
      <p>There are no workflows in the system. Please come again later.</p>
    </div>
    <div
      v-else-if="processedWorkflows.length === 0"
      class="text-center fs-2 mt-5"
    >
      <font-awesome-icon
        icon="fa-solid fa-magnifying-glass"
        class="my-5 fs-0"
        style="color: var(--bs-secondary)"
      />
      <p>
        Could not find any Workflows containing<br />'{{
          workflowsState.filterString
        }}'
      </p>
    </div>
    <CardTransitionGroup
      v-else
      class="d-flex flex-wrap align-items-center justify-content-between"
    >
      <workflow-card
        v-for="workflow in processedWorkflows"
        :key="workflow.workflow_id"
        :workflow="workflow"
        :loading="false"
      />
    </CardTransitionGroup>
  </div>
  <div
    v-else
    class="d-flex flex-wrap align-items-center justify-content-between"
  >
    <workflow-card
      v-for="workflow in 4"
      :key="workflow"
      :workflow="{
        name: '',
        short_description: '',
        repository_url: '',
        workflow_id: '',
        versions: [],
        developer_id: '',
      }"
      loading
    />
  </div>
</template>

<style scoped>
.text-shadow {
  text-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}
</style>