client/routes/domain/workflow-archival/basic.vue (309 lines of code) (raw):

<script> // Copyright (c) 2017-2024 Uber Technologies Inc. // // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import debounce from 'lodash-es/debounce'; import { ArchivalTable, ArchivalTableRow } from './components'; import { ARCHIVAL_STATUS_LIST, LOADING_MESSAGE_DELAY } from './constants'; import { getQueryParams, getRange, getStatus, getStatusValue, updateQueryFromRange, mapArchivedWorkflowResponse, } from './helpers'; import WorkflowArchivalService from './workflow-archival-service'; import pagedGrid from '~components/paged-grid'; import { ButtonFill, DateRangePicker, ErrorMessage, FlexGrid, FlexGridItem, LoadingMessage, LoadingSpinner, NoResults, SelectInput, TextInput, } from '~components'; import { getEndTimeIsoString, getErrorMessage, getStartTimeIsoString, } from '~helpers'; export default pagedGrid({ name: 'workflow-archival-basic', props: ['clusterName', 'dateFormat', 'domain', 'timeFormat', 'timezone'], data() { return { error: undefined, filterBy: 'CloseTime', loading: false, loadingMessageDelay: LOADING_MESSAGE_DELAY, nextPageToken: undefined, npt: undefined, results: undefined, statusList: ARCHIVAL_STATUS_LIST, }; }, computed: { endTime() { const { range } = this; const { endTime } = this.$route.query || {}; return getEndTimeIsoString(range, endTime); }, formattedResults() { const { dateFormat, results, timeFormat, timezone } = this; return mapArchivedWorkflowResponse({ dateFormat, results, timeFormat, timezone, }); }, queryParams() { const { endTime, workflowName, statusValue, startTime, workflowId, } = this; return getQueryParams({ endTime, workflowName, statusValue, startTime, workflowId, }); }, range() { const { endTime, range, startTime } = this.$route.query || {}; return getRange({ endTime, range, startTime }); }, workflowName() { const { workflowName = '' } = this.$route.query || {}; return workflowName; }, status() { const statusValue = this.$route.query && this.$route.query.status; const { statusList } = this; return getStatus({ statusList, statusValue }); }, statusValue() { const { status, statusList } = this; return getStatusValue({ status, statusList }); }, startTime() { const { range } = this; const { startTime } = this.$route.query || {}; return getStartTimeIsoString(range, startTime); }, workflowId() { const { workflowId = '' } = this.$route.query || {}; return workflowId; }, }, created() { const { domain, queryParams } = this; this.workflowArchivalService = WorkflowArchivalService({ domain }); this.onQueryChange({ ...queryParams, nextPageToken: undefined }); }, methods: { resetState() { this.error = undefined; this.loading = false; this.results = undefined; this.npt = undefined; this.nextPageToken = undefined; }, fetchArchivalRecord: debounce(async function fetchArchivalRecord( queryParams ) { if (!queryParams || !queryParams.startTime || !queryParams.endTime) { return; } this.loading = true; try { const { results, nextPageToken, } = await this.workflowArchivalService.fetchArchivalRecords( queryParams ); this.results = this.results ? this.results.concat(results) : results; this.npt = nextPageToken; } catch (error) { if (error.name === 'AbortError') { return; } this.npt = undefined; this.error = getErrorMessage(error); } this.loading = false; }, 200), onDateRangeChange(updatedRange) { const { query } = this.$route; const updatedQuery = updateQueryFromRange({ query, updatedRange }); this.$router.replace({ query: updatedQuery }); }, onQueryChange(queryParams) { this.resetState(); if (queryParams) { this.fetchArchivalRecord({ ...queryParams, nextPageToken: undefined }); } }, onNextTokenChange(queryParams) { this.fetchArchivalRecord(queryParams); }, onTextChange({ target: { name, value } }) { this.setQueryParam(name, value.trim()); }, onSelectChange({ value }) { this.setQueryParam('status', value); }, setQueryParam(name, value) { this.$router.replaceQueryParam(name, value); }, }, watch: { queryParams(queryParams) { this.onQueryChange(queryParams); }, nextPageToken(nextPageToken) { const { queryParams } = this; if (nextPageToken && queryParams) { this.onNextTokenChange({ ...queryParams, nextPageToken }); } }, }, components: { 'archival-table': ArchivalTable, 'archival-table-row': ArchivalTableRow, 'button-fill': ButtonFill, 'date-range-picker': DateRangePicker, 'error-message': ErrorMessage, 'flex-grid': FlexGrid, 'flex-grid-item': FlexGridItem, 'loading-message': LoadingMessage, 'loading-spinner': LoadingSpinner, 'no-results': NoResults, 'select-input': SelectInput, 'text-input': TextInput, }, }); </script> <template> <section class="workflow-archival-basic"> <header> <flex-grid> <flex-grid-item grow="1"> <text-input label="Workflow ID" name="workflowId" type="search" :value="workflowId" @input="onTextChange" /> </flex-grid-item> <flex-grid-item grow="1"> <text-input label="Workflow Name" name="workflowName" type="search" :value="workflowName" @input="onTextChange" /> </flex-grid-item> <flex-grid-item width="160px"> <select-input label="Status" :options="statusList" :value="status" @change="onSelectChange" /> </flex-grid-item> <flex-grid-item width="165px"> <text-input label="Filter by" readonly :value="filterBy" /> </flex-grid-item> <flex-grid-item width="325px"> <date-range-picker :date-range="range" @change="onDateRangeChange" /> </flex-grid-item> <flex-grid-item width="120px"> <button-fill label="ADVANCED" tag="router-link" :to="{ name: 'workflow-archival-advanced', params: { clusterName }, }" /> </flex-grid-item> </flex-grid> </header> <section class="results"> <archival-table v-infinite-scroll="nextPage" infinite-scroll-disabled="disableInfiniteScroll" infinite-scroll-distance="20" infinite-scroll-immediate-check="false" > <archival-table-row v-for="result in formattedResults" :close-status="result.closeStatus" :close-time="result.closeTime" :cluster-name="clusterName" :domain-name="result.domainName" :key="result.runId" :run-id="result.runId" :start-time="result.startTime" :workflow-id="result.workflowId" :workflow-name="result.workflowName" /> </archival-table> <error-message :error="error" /> <no-results :results="results" /> <loading-spinner v-if="loading" /> <loading-message :delay="loadingMessageDelay" :loading="loading"> <p>It looks like this request is taking some time to process.</p> <p>Try narrowing the time range to improve search time.</p> </loading-message> </section> </section> </template> <style lang="stylus"> section.workflow-archival-basic { display: flex; flex: 1 1 auto; flex-direction: column; overflow-y: auto; header { padding: 16px; } section.results { margin-top: 16px; min-height: 100px; overflow: auto; } paged-grid(); } </style>