Skip to content

Commit 5fcc480

Browse files
committed
feat(frontend): allow deletion of BaseImage used in completed campaign
Update the frontend to allow the deletion of a BaseImage even if in use by a Update Campaign, if that campaign is already completed. The user will be informed that the BaseImage is in use before attempting deletion that the operation will fail. Closes #598 Signed-off-by: Damiano Mason <damiano.mason@secomind.com>
1 parent 13e24c3 commit 5fcc480

3 files changed

Lines changed: 93 additions & 58 deletions

File tree

frontend/src/forms/UpdateCampaignForm.tsx

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ const UpdateCampaign = ({ campaignRef }: UpdateCampaignProps) => {
156156
}
157157

158158
const { baseImage } = campaignMechanism;
159-
const { baseImageCollection } = baseImage;
160159

161160
return (
162161
<Row>
@@ -181,39 +180,61 @@ const UpdateCampaign = ({ campaignRef }: UpdateCampaignProps) => {
181180
>
182181
<CampaignOutcome campaignRef={campaign} />
183182
</FormRow>
184-
<FormRow
185-
label={
186-
<FormattedMessage
187-
id="forms.UpdateCampaignForm.baseImageCollectionLabel"
188-
defaultMessage="Base Image Collection"
189-
/>
190-
}
191-
>
192-
<Link
193-
route={Route.baseImageCollectionsEdit}
194-
params={{ baseImageCollectionId: baseImageCollection.id }}
195-
>
196-
{baseImageCollection.name}
197-
</Link>
198-
</FormRow>
199-
<FormRow
200-
label={
201-
<FormattedMessage
202-
id="forms.UpdateCampaignForm.baseImageLabel"
203-
defaultMessage="Base Image"
204-
/>
205-
}
206-
>
207-
<Link
208-
route={Route.baseImagesEdit}
209-
params={{
210-
baseImageCollectionId: baseImageCollection.id,
211-
baseImageId: baseImage.id,
212-
}}
183+
{baseImage ? (
184+
<>
185+
<FormRow
186+
label={
187+
<FormattedMessage
188+
id="forms.UpdateCampaignForm.baseImageCollectionLabel"
189+
defaultMessage="Base Image Collection"
190+
/>
191+
}
192+
>
193+
<Link
194+
route={Route.baseImageCollectionsEdit}
195+
params={{
196+
baseImageCollectionId: baseImage.baseImageCollection.id,
197+
}}
198+
>
199+
{baseImage.baseImageCollection.name}
200+
</Link>
201+
</FormRow>
202+
<FormRow
203+
label={
204+
<FormattedMessage
205+
id="forms.UpdateCampaignForm.baseImageLabel"
206+
defaultMessage="Base Image"
207+
/>
208+
}
209+
>
210+
<Link
211+
route={Route.baseImagesEdit}
212+
params={{
213+
baseImageCollectionId: baseImage.baseImageCollection.id,
214+
baseImageId: baseImage.id,
215+
}}
216+
>
217+
{baseImage.name}
218+
</Link>
219+
</FormRow>
220+
</>
221+
) : (
222+
<FormRow
223+
label={
224+
<FormattedMessage
225+
id="forms.UpdateCampaignForm.baseImageLabel"
226+
defaultMessage="Base Image"
227+
/>
228+
}
213229
>
214-
{baseImage.name}
215-
</Link>
216-
</FormRow>
230+
<div className="d-flex align-content-center fst-italic text-muted">
231+
<FormattedMessage
232+
id="forms.UpdateCampaignForm.baseImageDeleted"
233+
defaultMessage="The Base Image has been deleted"
234+
/>
235+
</div>
236+
</FormRow>
237+
)}
217238
<FormRow
218239
label={
219240
<FormattedMessage

frontend/src/i18n/langs/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,6 +2125,9 @@
21252125
"forms.UpdateCampaignForm.baseImageCollectionLabel": {
21262126
"defaultMessage": "Base Image Collection"
21272127
},
2128+
"forms.UpdateCampaignForm.baseImageDeleted": {
2129+
"defaultMessage": "The Base Image has been deleted"
2130+
},
21282131
"forms.UpdateCampaignForm.baseImageLabel": {
21292132
"defaultMessage": "Base Image"
21302133
},

frontend/src/pages/BaseImage.tsx

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
/*
2-
* This file is part of Edgehog.
3-
*
4-
* Copyright 2023-2025 SECO Mind Srl
5-
*
6-
* Licensed under the Apache License, Version 2.0 (the "License");
7-
* you may not use this file except in compliance with the License.
8-
* You may obtain a copy of the License at
9-
*
10-
* http://www.apache.org/licenses/LICENSE-2.0
11-
*
12-
* Unless required by applicable law or agreed to in writing, software
13-
* distributed under the License is distributed on an "AS IS" BASIS,
14-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15-
* See the License for the specific language governing permissions and
16-
* limitations under the License.
17-
*
18-
* SPDX-License-Identifier: Apache-2.0
19-
*/
1+
// This file is part of Edgehog.
2+
//
3+
// Copyright 2023 - 2026 SECO Mind Srl
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
//
17+
// SPDX-License-Identifier: Apache-2.0
2018

2119
import { Suspense, useCallback, useEffect, useState } from "react";
2220
import { useParams } from "react-router-dom";
@@ -84,6 +82,9 @@ const DELETE_BASE_IMAGE_MUTATION = graphql`
8482
result {
8583
id
8684
}
85+
errors {
86+
message
87+
}
8788
}
8889
}
8990
`;
@@ -93,7 +94,10 @@ type BaseImageContentProps = {
9394
queryRef: BaseImage_getBaseImage_Query$data;
9495
};
9596

96-
const BaseImageContent = ({ baseImage, queryRef }: BaseImageContentProps) => {
97+
const BaseImageContent = ({
98+
baseImage,
99+
queryRef,
100+
}: BaseImageContentProps) => {
97101
const baseImageId = baseImage.id;
98102
const baseImageCollectionId = baseImage.baseImageCollection.id;
99103
const navigate = useNavigate();
@@ -111,7 +115,7 @@ const BaseImageContent = ({ baseImage, queryRef }: BaseImageContentProps) => {
111115
const handleDeleteBaseImage = useCallback(() => {
112116
deleteBaseImage({
113117
variables: { baseImageId },
114-
onCompleted(data, errors) {
118+
onCompleted(_data, errors) {
115119
if (!errors || errors.length === 0 || errors[0].code === "not_found") {
116120
return navigate({
117121
route: Route.baseImageCollectionsEdit,
@@ -158,7 +162,7 @@ const BaseImageContent = ({ baseImage, queryRef }: BaseImageContentProps) => {
158162
(baseImageChanges: BaseImageChanges) => {
159163
updateBaseImage({
160164
variables: { baseImageId, input: baseImageChanges },
161-
onCompleted(data, errors) {
165+
onCompleted(_data, errors) {
162166
if (errors) {
163167
const errorFeedback = errors
164168
.map(({ fields, message }) =>
@@ -244,7 +248,9 @@ type BaseImageWrapperProps = {
244248
getBaseImageQuery: PreloadedQuery<BaseImage_getBaseImage_Query>;
245249
};
246250

247-
const BaseImageWrapper = ({ getBaseImageQuery }: BaseImageWrapperProps) => {
251+
const BaseImageWrapper = ({
252+
getBaseImageQuery,
253+
}: BaseImageWrapperProps) => {
248254
const { baseImageCollectionId = "" } = useParams();
249255

250256
const queryData = usePreloadedQuery(GET_BASE_IMAGE_QUERY, getBaseImageQuery);
@@ -273,7 +279,10 @@ const BaseImageWrapper = ({ getBaseImageQuery }: BaseImageWrapperProps) => {
273279
}
274280

275281
return (
276-
<BaseImageContent baseImage={queryData.baseImage} queryRef={queryData} />
282+
<BaseImageContent
283+
baseImage={queryData.baseImage}
284+
queryRef={queryData}
285+
/>
277286
);
278287
};
279288

@@ -306,7 +315,9 @@ const BaseImagePage = () => {
306315
onReset={fetchBaseImage}
307316
>
308317
{getBaseImageQuery && (
309-
<BaseImageWrapper getBaseImageQuery={getBaseImageQuery} />
318+
<BaseImageWrapper
319+
getBaseImageQuery={getBaseImageQuery}
320+
/>
310321
)}
311322
</ErrorBoundary>
312323
</Suspense>

0 commit comments

Comments
 (0)