mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: implement basic archive ui to make archiving failed versions easy (#10182)
* feat: implement basic archive ui to make archiving failed versions easy.
This commit is contained in:
Generated
+6
@@ -2294,6 +2294,12 @@ const docTemplate = `{
|
||||
"name": "after_id",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Include archived versions in the list",
|
||||
"name": "include_archived",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page limit",
|
||||
|
||||
Generated
+6
@@ -2002,6 +2002,12 @@
|
||||
"name": "after_id",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Include archived versions in the list",
|
||||
"name": "include_archived",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page limit",
|
||||
|
||||
@@ -704,6 +704,7 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re
|
||||
// @Tags Templates
|
||||
// @Param template path string true "Template ID" format(uuid)
|
||||
// @Param after_id query string false "After ID" format(uuid)
|
||||
// @Param include_archived query bool false "Include archived versions in the list"
|
||||
// @Param limit query int false "Page limit"
|
||||
// @Param offset query int false "Page offset"
|
||||
// @Success 200 {array} codersdk.TemplateVersion
|
||||
|
||||
Generated
+7
-6
@@ -827,12 +827,13 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions \
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
| ---------- | ----- | ------------ | -------- | ----------- |
|
||||
| `template` | path | string(uuid) | true | Template ID |
|
||||
| `after_id` | query | string(uuid) | false | After ID |
|
||||
| `limit` | query | integer | false | Page limit |
|
||||
| `offset` | query | integer | false | Page offset |
|
||||
| Name | In | Type | Required | Description |
|
||||
| ------------------ | ----- | ------------ | -------- | ------------------------------------- |
|
||||
| `template` | path | string(uuid) | true | Template ID |
|
||||
| `after_id` | query | string(uuid) | false | After ID |
|
||||
| `include_archived` | query | boolean | false | Include archived versions in the list |
|
||||
| `limit` | query | integer | false | Page limit |
|
||||
| `offset` | query | integer | false | Page offset |
|
||||
|
||||
### Example responses
|
||||
|
||||
|
||||
@@ -387,6 +387,20 @@ export const patchTemplateVersion = async (
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const archiveTemplateVersion = async (templateVersionId: string) => {
|
||||
const response = await axios.post<TypesGen.TemplateVersion>(
|
||||
`/api/v2/templateversions/${templateVersionId}/archive`,
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const unarchiveTemplateVersion = async (templateVersionId: string) => {
|
||||
const response = await axios.post<TypesGen.TemplateVersion>(
|
||||
`/api/v2/templateversions/${templateVersionId}/unarchive`,
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const updateTemplateMeta = async (
|
||||
templateId: string,
|
||||
data: TypesGen.UpdateTemplateMeta,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { useMutation, useQuery } from "react-query";
|
||||
import { getTemplateVersions, updateActiveTemplateVersion } from "api/api";
|
||||
import {
|
||||
archiveTemplateVersion,
|
||||
getTemplateVersions,
|
||||
updateActiveTemplateVersion,
|
||||
} from "api/api";
|
||||
import { getErrorMessage } from "api/errors";
|
||||
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
|
||||
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
|
||||
@@ -34,9 +38,31 @@ const TemplateVersionsPage = () => {
|
||||
displayError(getErrorMessage(error, "Failed to promote version"));
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: archiveVersion, isLoading: isArchiving } = useMutation({
|
||||
mutationFn: (templateVersionId: string) => {
|
||||
return archiveTemplateVersion(templateVersionId);
|
||||
},
|
||||
onSuccess: async () => {
|
||||
// The reload is unfortunate. When a version is archived, we should hide
|
||||
// the row. I do not know an easy way to do that, so a reload makes the API call
|
||||
// resend and now the version is omitted.
|
||||
// TODO: Improve this to not reload the page.
|
||||
location.reload();
|
||||
setSelectedVersionIdToArchive(undefined);
|
||||
displaySuccess("Version archived successfully");
|
||||
},
|
||||
onError: (error) => {
|
||||
displayError(getErrorMessage(error, "Failed to archive version"));
|
||||
},
|
||||
});
|
||||
|
||||
const [selectedVersionIdToPromote, setSelectedVersionIdToPromote] = useState<
|
||||
string | undefined
|
||||
>();
|
||||
const [selectedVersionIdToArchive, setSelectedVersionIdToArchive] = useState<
|
||||
string | undefined
|
||||
>();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -50,8 +76,14 @@ const TemplateVersionsPage = () => {
|
||||
? setSelectedVersionIdToPromote
|
||||
: undefined
|
||||
}
|
||||
onArchiveClick={
|
||||
permissions.canUpdateTemplate
|
||||
? setSelectedVersionIdToArchive
|
||||
: undefined
|
||||
}
|
||||
activeVersionId={latestActiveVersion}
|
||||
/>
|
||||
{/* Promote confirm */}
|
||||
<ConfirmDialog
|
||||
type="info"
|
||||
hideCancel={false}
|
||||
@@ -65,6 +97,20 @@ const TemplateVersionsPage = () => {
|
||||
confirmText="Promote"
|
||||
description="Are you sure you want to promote this version? Workspaces will be prompted to “Update” to this version once promoted."
|
||||
/>
|
||||
{/* Archive Confirm */}
|
||||
<ConfirmDialog
|
||||
type="info"
|
||||
hideCancel={false}
|
||||
open={selectedVersionIdToArchive !== undefined}
|
||||
onConfirm={() => {
|
||||
archiveVersion(selectedVersionIdToArchive as string);
|
||||
}}
|
||||
onClose={() => setSelectedVersionIdToArchive(undefined)}
|
||||
title="Archive version"
|
||||
confirmLoading={isArchiving}
|
||||
confirmText="Archive"
|
||||
description="Are you sure you want to archive this version (this is reversible)? Archived versions cannot be used by workspaces."
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface VersionRowProps {
|
||||
isActive: boolean;
|
||||
isLatest: boolean;
|
||||
onPromoteClick?: (templateVersionId: string) => void;
|
||||
onArchiveClick?: (templateVersionId: string) => void;
|
||||
}
|
||||
|
||||
export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
@@ -24,6 +25,7 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
isActive,
|
||||
isLatest,
|
||||
onPromoteClick,
|
||||
onArchiveClick,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const navigate = useNavigate();
|
||||
@@ -90,14 +92,30 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
<Pill text="Canceled" type="neutral" lightBorder />
|
||||
)}
|
||||
{jobStatus === "failed" && <Pill text="Failed" type="error" />}
|
||||
{onPromoteClick && (
|
||||
{jobStatus === "failed" ? (
|
||||
<Button
|
||||
className={styles.promoteButton}
|
||||
disabled={isActive || version.archived}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (onArchiveClick) {
|
||||
onArchiveClick(version.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Archive…
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={styles.promoteButton}
|
||||
disabled={isActive || jobStatus !== "succeeded"}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onPromoteClick(version.id);
|
||||
if (onPromoteClick) {
|
||||
onPromoteClick(version.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Promote…
|
||||
|
||||
@@ -21,11 +21,13 @@ export const Language = {
|
||||
export interface VersionsTableProps {
|
||||
activeVersionId: string;
|
||||
onPromoteClick?: (templateVersionId: string) => void;
|
||||
onArchiveClick?: (templateVersionId: string) => void;
|
||||
|
||||
versions?: TypesGen.TemplateVersion[];
|
||||
}
|
||||
|
||||
export const VersionsTable: FC<VersionsTableProps> = (props) => {
|
||||
const { versions, onPromoteClick, activeVersionId } = props;
|
||||
const { versions, onArchiveClick, onPromoteClick, activeVersionId } = props;
|
||||
|
||||
const latestVersionId = versions?.reduce(
|
||||
(latestSoFar, against) => {
|
||||
@@ -55,6 +57,7 @@ export const VersionsTable: FC<VersionsTableProps> = (props) => {
|
||||
getDate={(version) => new Date(version.created_at)}
|
||||
row={(version) => (
|
||||
<VersionRow
|
||||
onArchiveClick={onArchiveClick}
|
||||
onPromoteClick={onPromoteClick}
|
||||
version={version}
|
||||
key={version.id}
|
||||
|
||||
Reference in New Issue
Block a user