import {Fragment} from 'react'; import {RouteComponentProps} from 'react-router'; import styled from '@emotion/styled'; import { addErrorMessage, addLoadingMessage, addSuccessMessage, } from 'sentry/actionCreators/indicator'; import Access from 'sentry/components/acl/access'; import Button from 'sentry/components/button'; import ButtonBar from 'sentry/components/buttonBar'; import Confirm from 'sentry/components/confirm'; import Pagination from 'sentry/components/pagination'; import {PanelTable} from 'sentry/components/panels'; import SearchBar from 'sentry/components/searchBar'; import TextOverflow from 'sentry/components/textOverflow'; import Tooltip from 'sentry/components/tooltip'; import Version from 'sentry/components/version'; import {IconDelete} from 'sentry/icons'; import {t} from 'sentry/locale'; import space from 'sentry/styles/space'; import {Artifact, Organization, Project} from 'sentry/types'; import {formatVersion} from 'sentry/utils/formatters'; import {decodeScalar} from 'sentry/utils/queryString'; import routeTitleGen from 'sentry/utils/routeTitle'; import AsyncView from 'sentry/views/asyncView'; import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader'; import SourceMapsArtifactRow from './sourceMapsArtifactRow'; type RouteParams = {name: string; orgId: string; projectId: string}; type Props = RouteComponentProps & { organization: Organization; project: Project; }; type State = AsyncView['state'] & { artifacts: Artifact[]; }; class ProjectSourceMapsDetail extends AsyncView { getTitle() { const {projectId, name} = this.props.params; return routeTitleGen(t('Archive %s', formatVersion(name)), projectId, false); } getDefaultState(): State { return { ...super.getDefaultState(), artifacts: [], }; } getEndpoints(): ReturnType { return [['artifacts', this.getArtifactsUrl(), {query: {query: this.getQuery()}}]]; } getArtifactsUrl() { const {orgId, projectId, name} = this.props.params; return `/projects/${orgId}/${projectId}/releases/${encodeURIComponent(name)}/files/`; } handleSearch = (query: string) => { const {location, router} = this.props; router.push({ ...location, query: {...location.query, cursor: undefined, query}, }); }; handleArtifactDelete = async (id: string) => { addLoadingMessage(t('Removing artifact\u2026')); try { await this.api.requestPromise(`${this.getArtifactsUrl()}${id}/`, { method: 'DELETE', }); this.fetchData(); addSuccessMessage(t('Artifact removed.')); } catch { addErrorMessage(t('Unable to remove artifact. Please try again.')); } }; handleArchiveDelete = async () => { const {orgId, projectId, name} = this.props.params; addLoadingMessage(t('Removing artifacts\u2026')); try { await this.api.requestPromise( `/projects/${orgId}/${projectId}/files/source-maps/`, { method: 'DELETE', query: {name}, } ); this.fetchData(); addSuccessMessage(t('Artifacts removed.')); } catch { addErrorMessage(t('Unable to remove artifacts. Please try again.')); } }; getQuery() { const {query} = this.props.location.query; return decodeScalar(query); } getEmptyMessage() { if (this.getQuery()) { return t('There are no artifacts that match your search.'); } return t('There are no artifacts in this archive.'); } renderLoading() { return this.renderBody(); } renderArtifacts() { const {organization} = this.props; const {artifacts} = this.state; const artifactApiUrl = this.api.baseUrl + this.getArtifactsUrl(); if (!artifacts.length) { return null; } return artifacts.map(artifact => { return ( ); }); } renderBody() { const {loading, artifacts, artifactsPageLinks} = this.state; const {name, orgId} = this.props.params; const {project} = this.props; return ( {t('Archive')}  } action={ {t('Go to Release')} {({hasAccess}) => (