Skip to content

Commit 19408df

Browse files
- feat: Added ability to define an "alias" name for a CheckConfig in a Benefit.
- chore: Removed extra information from Check name/definitions on Published Screener View.
1 parent 69d72b5 commit 19408df

13 files changed

Lines changed: 264 additions & 42 deletions

File tree

builder-api/src/main/java/org/acme/controller/CustomBenefitResource.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.acme.model.domain.*;
1313
import org.acme.model.dto.CustomBenefit.AddCheckRequest;
1414
import org.acme.model.dto.CustomBenefit.CreateCustomBenefitRequest;
15+
import org.acme.model.dto.CustomBenefit.UpdateCheckAliasRequest;
1516
import org.acme.model.dto.CustomBenefit.UpdateCheckParametersRequest;
1617
import org.acme.model.dto.CustomBenefit.UpdateCustomBenefitRequest;
1718
import org.acme.persistence.EligibilityCheckRepository;
@@ -457,6 +458,71 @@ public Response updateCheckParameters(
457458
}
458459
}
459460

461+
@PATCH
462+
@Consumes(MediaType.APPLICATION_JSON)
463+
@Path("/screener/{screenerId}/benefit/{benefitId}/check/{checkId}/alias")
464+
public Response updateCheckAlias(
465+
@Context SecurityIdentity identity,
466+
@PathParam("screenerId") String screenerId,
467+
@PathParam("benefitId") String benefitId,
468+
@PathParam("checkId") String checkId,
469+
UpdateCheckAliasRequest request
470+
) {
471+
String userId = AuthUtils.getUserId(identity);
472+
473+
if (!isUserAuthorizedForScreener(userId, screenerId)) {
474+
return Response.status(Response.Status.UNAUTHORIZED).build();
475+
}
476+
477+
try {
478+
// Get the benefit
479+
Optional<Benefit> benefitOpt = screenerRepository.getCustomBenefit(screenerId, benefitId);
480+
if (benefitOpt.isEmpty()) {
481+
return Response.status(Response.Status.NOT_FOUND)
482+
.entity(Map.of("error", "Benefit not found"))
483+
.build();
484+
}
485+
486+
Benefit benefit = benefitOpt.get();
487+
List<CheckConfig> checks = benefit.getChecks();
488+
489+
if (checks == null || checks.isEmpty()) {
490+
return Response.status(Response.Status.NOT_FOUND)
491+
.entity(Map.of("error", "Check not found in benefit"))
492+
.build();
493+
}
494+
495+
// Find and update the check with the matching checkId
496+
Boolean checkUpdated = false;
497+
List<CheckConfig> checkListAfterUpdate = new ArrayList<>();
498+
for (CheckConfig check : checks) {
499+
if (check.getCheckId().equals(checkId)) {
500+
check.setAliasName(request.aliasName());
501+
checkUpdated = true;
502+
}
503+
checkListAfterUpdate.add(check);
504+
}
505+
506+
if (!checkUpdated) {
507+
return Response.status(Response.Status.NOT_FOUND)
508+
.entity(Map.of("error", "Check not found in benefit"))
509+
.build();
510+
}
511+
512+
benefit.setChecks(checkListAfterUpdate);
513+
514+
// Save the updated benefit
515+
screenerRepository.updateCustomBenefit(screenerId, benefit);
516+
517+
return Response.ok().build();
518+
} catch (Exception e) {
519+
Log.error(e);
520+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
521+
.entity(Map.of("error", "Could not update check alias"))
522+
.build();
523+
}
524+
}
525+
460526
// ========== Private Helper Methods ==========
461527

462528
private boolean isUserAuthorizedForScreener(String userId, String screenerId) {

builder-api/src/main/java/org/acme/controller/DecisionResource.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ private Map<String, Object> evaluateBenefit(Benefit benefit, Map<String, Object>
154154
String uniqueCheckKey = checkConfig.getCheckId() + checkNum;
155155
Map<String, Object> checkResultMap = new HashMap<>();
156156
checkResultMap.put("name", checkConfig.getCheckName());
157+
checkResultMap.put("aliasName", checkConfig.getAliasName());
157158
checkResultMap.put("result", evaluationResult);
158159
checkResultMap.put("module", checkConfig.getCheckModule() != null ? checkConfig.getCheckModule() : "");
159160
checkResultMap.put("version", checkConfig.getCheckVersion() != null ? checkConfig.getCheckVersion() : "");

builder-api/src/main/java/org/acme/model/domain/CheckConfig.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public class CheckConfig {
1919
private String evaluationUrl;
2020
private JsonNode inputDefinition;
2121
private List<ParameterDefinition> parameterDefinitions;
22+
// optional alias name for this check instance
23+
private String aliasName;
2224

2325
public CheckConfig() {
2426
}
@@ -116,4 +118,12 @@ public String getSourceCheckId() {
116118
public void setSourceCheckId(String sourceCheckId) {
117119
this.sourceCheckId = sourceCheckId;
118120
}
121+
122+
public String getAliasName() {
123+
return aliasName;
124+
}
125+
126+
public void setAliasName(String aliasName) {
127+
this.aliasName = aliasName;
128+
}
119129
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.acme.model.dto.CustomBenefit;
2+
3+
public record UpdateCheckAliasRequest(String aliasName) {}

builder-frontend/src/api/benefit.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,21 @@ export const updateCheckParameters = async (
9797
throw error;
9898
}
9999
};
100+
101+
export const updateCheckAlias = async (
102+
screenerId: string,
103+
benefitId: string,
104+
checkId: string,
105+
aliasName: string | null
106+
): Promise<void> => {
107+
const url = apiUrl + "/screener/" + screenerId + "/benefit/" + benefitId + "/check/" + checkId + "/alias";
108+
try {
109+
const response = await authPatch(url.toString(), { aliasName });
110+
if (!response.ok) {
111+
throw new Error(`Update alias failed with status: ${response.status}`);
112+
}
113+
} catch (error) {
114+
console.error("Error updating check alias:", error);
115+
throw error;
116+
}
117+
};

builder-frontend/src/components/project/manageBenefits/configureBenefit/ConfigureBenefit.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ const ConfigureBenefit = ({
116116
newCheckData,
117117
);
118118
}}
119+
updateCheckConfigAlias={(
120+
aliasName: string | null,
121+
) => {
122+
actions.updateCheckConfigAlias(
123+
checkConfig.checkId,
124+
aliasName,
125+
);
126+
}}
119127
/>
120128
);
121129
}}

builder-frontend/src/components/project/manageBenefits/configureBenefit/SelectedEligibilityCheck.tsx

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Accessor, createSignal, For, Show } from "solid-js";
22

33
import ConfigureCheckModal from "./modals/ConfigureCheckModal";
4+
import EditAliasModal from "./modals/EditAliasModal";
45

56
import { titleCase } from "@/utils/title_case";
67

@@ -9,18 +10,25 @@ import type {
910
ParameterDefinition,
1011
ParameterValues,
1112
} from "@/types";
13+
import { PencilIcon } from "lucide-solid";
1214

1315
const SelectedEligibilityCheck = ({
1416
checkConfig,
1517
onRemove,
1618
updateCheckConfigParams,
19+
updateCheckConfigAlias,
1720
}: {
1821
checkConfig: Accessor<CheckConfig>;
1922
updateCheckConfigParams: (newCheckData: ParameterValues) => void;
23+
updateCheckConfigAlias: (aliasName: string | null) => void;
2024
onRemove: () => void | null;
2125
}) => {
2226
const [configuringCheckModalOpen, setConfiguringCheckModalOpen] =
2327
createSignal(false);
28+
const [editAliasModalOpen, setEditAliasModalOpen] = createSignal(false);
29+
30+
const displayName = () =>
31+
checkConfig().aliasName || checkConfig().checkName;
2432

2533
const unfilledRequiredParameters = () => {
2634
return [];
@@ -47,9 +55,25 @@ const SelectedEligibilityCheck = ({
4755
X
4856
</div>
4957
</Show>
50-
<div class="text-xl font-bold mb-2">
51-
{titleCase(checkConfig().checkName)} - {checkConfig().checkVersion}
58+
<div class="text-xl font-bold mb-2 flex items-center gap-2">
59+
<span>{titleCase(displayName())}</span>
60+
<span class="text-gray-500">- {checkConfig().checkVersion}</span>
61+
<div
62+
class="text-sm text-gray-500 hover:text-gray-700 hover:rounded-2xl p-2 hover:bg-gray-400"
63+
onClick={(e) => {
64+
e.stopPropagation();
65+
setEditAliasModalOpen(true);
66+
}}
67+
title="Edit alias"
68+
>
69+
<PencilIcon size={16}/>
70+
</div>
5271
</div>
72+
<Show when={checkConfig().aliasName}>
73+
<div class="text-sm text-gray-500 mb-1">
74+
Original: {checkConfig().checkName}
75+
</div>
76+
</Show>
5377
<div class="pl-2 [&:has(+div)]:mb-2">
5478
{checkConfig().checkDescription}
5579
</div>
@@ -97,6 +121,16 @@ const SelectedEligibilityCheck = ({
97121
}}
98122
/>
99123
)}
124+
125+
{editAliasModalOpen() && (
126+
<EditAliasModal
127+
checkConfig={checkConfig}
128+
updateCheckConfigAlias={updateCheckConfigAlias}
129+
closeModal={() => {
130+
setEditAliasModalOpen(false);
131+
}}
132+
/>
133+
)}
100134
</>
101135
);
102136
};

builder-frontend/src/components/project/manageBenefits/configureBenefit/benefitResource.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
fetchScreenerBenefit,
66
addCheckToBenefit,
77
removeCheckFromBenefit,
8-
updateCheckParameters
8+
updateCheckParameters,
9+
updateCheckAlias
910
} from "@/api/benefit";
1011

1112
import type { Benefit, ParameterValues } from "@/types";
@@ -19,6 +20,10 @@ interface ScreenerBenefitsResource {
1920
checkId: string,
2021
parameters: ParameterValues
2122
) => void;
23+
updateCheckConfigAlias: (
24+
checkId: string,
25+
aliasName: string | null
26+
) => void;
2227
};
2328
actionInProgress: Accessor<boolean>;
2429
initialLoadStatus: {
@@ -92,12 +97,29 @@ const createScreenerBenefits = (
9297
setActionInProgress(false);
9398
};
9499

100+
const updateCheckConfigAlias = async (
101+
checkId: string,
102+
aliasName: string | null
103+
) => {
104+
if (!benefit) return;
105+
setActionInProgress(true);
106+
107+
try {
108+
await updateCheckAlias(screenerId(), benefitId(), checkId, aliasName);
109+
await refetch();
110+
} catch (e) {
111+
console.error("Failed to update check alias", e);
112+
}
113+
setActionInProgress(false);
114+
};
115+
95116
return {
96117
benefit: () => benefit,
97118
actions: {
98119
addCheck,
99120
removeCheck,
100121
updateCheckConfigParams,
122+
updateCheckConfigAlias,
101123
},
102124
actionInProgress,
103125
initialLoadStatus: {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { Accessor, createSignal } from "solid-js";
2+
3+
import { titleCase } from "@/utils/title_case";
4+
5+
import type { CheckConfig } from "@/types";
6+
7+
const EditAliasModal = ({
8+
checkConfig,
9+
updateCheckConfigAlias,
10+
closeModal,
11+
}: {
12+
checkConfig: Accessor<CheckConfig>;
13+
updateCheckConfigAlias: (aliasName: string | null) => void;
14+
closeModal: () => void;
15+
}) => {
16+
const [aliasValue, setAliasValue] = createSignal(
17+
checkConfig().aliasName ?? ""
18+
);
19+
20+
const confirmAndClose = () => {
21+
const trimmedValue = aliasValue().trim();
22+
updateCheckConfigAlias(trimmedValue === "" ? null : trimmedValue);
23+
closeModal();
24+
};
25+
26+
const clearAlias = () => {
27+
setAliasValue("");
28+
};
29+
30+
return (
31+
<div class="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
32+
<div class="bg-white px-12 py-8 rounded-xl max-w-140 w-1/2 min-w-80">
33+
<div class="text-2xl mb-4">
34+
Edit Alias: {titleCase(checkConfig().checkName)}
35+
</div>
36+
37+
<div class="mb-4">
38+
<div class="text-sm text-gray-600 mb-2">
39+
Set an alias name to display instead of the check's original name.
40+
Leave empty to use the original name.
41+
</div>
42+
<div class="flex gap-2">
43+
<input
44+
type="text"
45+
value={aliasValue()}
46+
onInput={(e) => setAliasValue(e.target.value)}
47+
placeholder={checkConfig().checkName}
48+
class="form-input-custom flex-1"
49+
/>
50+
{aliasValue() && (
51+
<button
52+
class="btn-default btn-gray !text-sm"
53+
onClick={clearAlias}
54+
>
55+
Clear
56+
</button>
57+
)}
58+
</div>
59+
</div>
60+
61+
<div class="flex justify-end gap-2 space-x-2">
62+
<button class="btn-default btn-gray !text-sm" onClick={closeModal}>
63+
Cancel
64+
</button>
65+
<button
66+
class="btn-default btn-blue !text-sm"
67+
onClick={confirmAndClose}
68+
>
69+
Save
70+
</button>
71+
</div>
72+
</div>
73+
</div>
74+
);
75+
};
76+
77+
export default EditAliasModal;

builder-frontend/src/components/project/preview/Results.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,23 @@ export default function Results({
102102
</Switch>
103103
</div>
104104
<div class="flex flex-col">
105-
<div>
106-
{check.name}
107-
<Show when={check.module || check.version}>
108-
<span class="text-gray-500 ml-1">
109-
({[check.module, check.version].filter(Boolean).join(" v")})
105+
<Show when={check.aliasName} fallback={
106+
<div>
107+
{check.name}
108+
<Show when={check.module || check.version}>
109+
<span class="text-gray-500 ml-1">
110+
({[check.module, check.version].filter(Boolean).join(", v")})
111+
</span>
112+
</Show>
113+
</div>
114+
}>
115+
<div>
116+
{check.aliasName}
117+
<span class="text-gray-500 text-sm ml-1">
118+
({check.name}, {[check.module, check.version].filter(Boolean).join(", v")})
110119
</span>
111-
</Show>
112-
</div>
120+
</div>
121+
</Show>
113122
<Show when={check.parameters && Object.keys(check.parameters).length > 0}>
114123
<div class="text-gray-500 text-sm">
115124
{formatParameters(check.parameters)}

0 commit comments

Comments
 (0)