Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.acme.model.domain.*;
import org.acme.model.dto.CustomBenefit.AddCheckRequest;
import org.acme.model.dto.CustomBenefit.CreateCustomBenefitRequest;
import org.acme.model.dto.CustomBenefit.UpdateCheckAliasRequest;
import org.acme.model.dto.CustomBenefit.UpdateCheckParametersRequest;
import org.acme.model.dto.CustomBenefit.UpdateCustomBenefitRequest;
import org.acme.persistence.EligibilityCheckRepository;
Expand Down Expand Up @@ -457,6 +458,71 @@ public Response updateCheckParameters(
}
}

@PATCH
@Consumes(MediaType.APPLICATION_JSON)
@Path("/screener/{screenerId}/benefit/{benefitId}/check/{checkId}/alias")
public Response updateCheckAlias(
@Context SecurityIdentity identity,
@PathParam("screenerId") String screenerId,
@PathParam("benefitId") String benefitId,
@PathParam("checkId") String checkId,
UpdateCheckAliasRequest request
) {
String userId = AuthUtils.getUserId(identity);

if (!isUserAuthorizedForScreener(userId, screenerId)) {
return Response.status(Response.Status.UNAUTHORIZED).build();
}

try {
// Get the benefit
Optional<Benefit> benefitOpt = screenerRepository.getCustomBenefit(screenerId, benefitId);
if (benefitOpt.isEmpty()) {
return Response.status(Response.Status.NOT_FOUND)
.entity(Map.of("error", "Benefit not found"))
.build();
}

Benefit benefit = benefitOpt.get();
List<CheckConfig> checks = benefit.getChecks();

if (checks == null || checks.isEmpty()) {
return Response.status(Response.Status.NOT_FOUND)
.entity(Map.of("error", "Check not found in benefit"))
.build();
}

// Find and update the check with the matching checkId
Boolean checkUpdated = false;
List<CheckConfig> checkListAfterUpdate = new ArrayList<>();
for (CheckConfig check : checks) {
if (check.getCheckId().equals(checkId)) {
check.setAliasName(request.aliasName());
checkUpdated = true;
}
checkListAfterUpdate.add(check);
}

if (!checkUpdated) {
return Response.status(Response.Status.NOT_FOUND)
.entity(Map.of("error", "Check not found in benefit"))
.build();
}

benefit.setChecks(checkListAfterUpdate);

// Save the updated benefit
screenerRepository.updateCustomBenefit(screenerId, benefit);

return Response.ok().build();
} catch (Exception e) {
Log.error(e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(Map.of("error", "Could not update check alias"))
.build();
}
}

// ========== Private Helper Methods ==========

private boolean isUserAuthorizedForScreener(String userId, String screenerId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ private Map<String, Object> evaluateBenefit(Benefit benefit, Map<String, Object>
String uniqueCheckKey = checkConfig.getCheckId() + checkNum;
Map<String, Object> checkResultMap = new HashMap<>();
checkResultMap.put("name", checkConfig.getCheckName());
checkResultMap.put("aliasName", checkConfig.getAliasName());
checkResultMap.put("result", evaluationResult);
checkResultMap.put("module", checkConfig.getCheckModule() != null ? checkConfig.getCheckModule() : "");
checkResultMap.put("version", checkConfig.getCheckVersion() != null ? checkConfig.getCheckVersion() : "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class CheckConfig {
private String evaluationUrl;
private JsonNode inputDefinition;
private List<ParameterDefinition> parameterDefinitions;
// optional alias name for this check instance
private String aliasName;

public CheckConfig() {
}
Expand Down Expand Up @@ -116,4 +118,12 @@ public String getSourceCheckId() {
public void setSourceCheckId(String sourceCheckId) {
this.sourceCheckId = sourceCheckId;
}

public String getAliasName() {
return aliasName;
}

public void setAliasName(String aliasName) {
this.aliasName = aliasName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.acme.model.dto.CustomBenefit;

public record UpdateCheckAliasRequest(String aliasName) {}
18 changes: 18 additions & 0 deletions builder-frontend/src/api/benefit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,21 @@ export const updateCheckParameters = async (
throw error;
}
};

export const updateCheckAlias = async (
screenerId: string,
benefitId: string,
checkId: string,
aliasName: string | null
): Promise<void> => {
const url = apiUrl + "/screener/" + screenerId + "/benefit/" + benefitId + "/check/" + checkId + "/alias";
try {
const response = await authPatch(url.toString(), { aliasName });
if (!response.ok) {
throw new Error(`Update alias failed with status: ${response.status}`);
}
} catch (error) {
console.error("Error updating check alias:", error);
throw error;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ const ConfigureBenefit = ({
newCheckData,
);
}}
updateCheckConfigAlias={(
aliasName: string | null,
) => {
actions.updateCheckConfigAlias(
checkConfig.checkId,
aliasName,
);
}}
/>
);
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Accessor, createSignal, For, Show } from "solid-js";

import ConfigureCheckModal from "./modals/ConfigureCheckModal";
import EditAliasModal from "./modals/EditAliasModal";

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

Expand All @@ -9,18 +10,25 @@ import type {
ParameterDefinition,
ParameterValues,
} from "@/types";
import { PencilIcon } from "lucide-solid";

const SelectedEligibilityCheck = ({
checkConfig,
onRemove,
updateCheckConfigParams,
updateCheckConfigAlias,
}: {
checkConfig: Accessor<CheckConfig>;
updateCheckConfigParams: (newCheckData: ParameterValues) => void;
updateCheckConfigAlias: (aliasName: string | null) => void;
onRemove: () => void | null;
}) => {
const [configuringCheckModalOpen, setConfiguringCheckModalOpen] =
createSignal(false);
const [editAliasModalOpen, setEditAliasModalOpen] = createSignal(false);

const displayName = () =>
checkConfig().aliasName || checkConfig().checkName;

const unfilledRequiredParameters = () => {
return [];
Expand All @@ -47,9 +55,25 @@ const SelectedEligibilityCheck = ({
X
</div>
</Show>
<div class="text-xl font-bold mb-2">
{titleCase(checkConfig().checkName)} - {checkConfig().checkVersion}
<div class="text-xl font-bold mb-2 flex items-center gap-2">
<span>{titleCase(displayName())}</span>
<span class="text-gray-500">- {checkConfig().checkVersion}</span>
<div
class="text-sm text-gray-500 hover:text-gray-700 hover:rounded-2xl p-2 hover:bg-gray-400"
onClick={(e) => {
e.stopPropagation();
setEditAliasModalOpen(true);
}}
title="Edit alias"
>
<PencilIcon size={16}/>
</div>
</div>
<Show when={checkConfig().aliasName}>
<div class="text-sm text-gray-500 mb-1">
Original: {checkConfig().checkName}
</div>
</Show>
<div class="pl-2 [&:has(+div)]:mb-2">
{checkConfig().checkDescription}
</div>
Expand Down Expand Up @@ -97,6 +121,16 @@ const SelectedEligibilityCheck = ({
}}
/>
)}

{editAliasModalOpen() && (
<EditAliasModal
checkConfig={checkConfig}
updateCheckConfigAlias={updateCheckConfigAlias}
closeModal={() => {
setEditAliasModalOpen(false);
}}
/>
)}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
fetchScreenerBenefit,
addCheckToBenefit,
removeCheckFromBenefit,
updateCheckParameters
updateCheckParameters,
updateCheckAlias
} from "@/api/benefit";

import type { Benefit, ParameterValues } from "@/types";
Expand All @@ -19,6 +20,10 @@ interface ScreenerBenefitsResource {
checkId: string,
parameters: ParameterValues
) => void;
updateCheckConfigAlias: (
checkId: string,
aliasName: string | null
) => void;
};
actionInProgress: Accessor<boolean>;
initialLoadStatus: {
Expand Down Expand Up @@ -92,12 +97,29 @@ const createScreenerBenefits = (
setActionInProgress(false);
};

const updateCheckConfigAlias = async (
checkId: string,
aliasName: string | null
) => {
if (!benefit) return;
setActionInProgress(true);

try {
await updateCheckAlias(screenerId(), benefitId(), checkId, aliasName);
await refetch();
} catch (e) {
console.error("Failed to update check alias", e);
}
setActionInProgress(false);
};

return {
benefit: () => benefit,
actions: {
addCheck,
removeCheck,
updateCheckConfigParams,
updateCheckConfigAlias,
},
actionInProgress,
initialLoadStatus: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Accessor, createSignal } from "solid-js";

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

import type { CheckConfig } from "@/types";

const EditAliasModal = ({
checkConfig,
updateCheckConfigAlias,
closeModal,
}: {
checkConfig: Accessor<CheckConfig>;
updateCheckConfigAlias: (aliasName: string | null) => void;
closeModal: () => void;
}) => {
const [aliasValue, setAliasValue] = createSignal(
checkConfig().aliasName ?? ""
);

const confirmAndClose = () => {
const trimmedValue = aliasValue().trim();
updateCheckConfigAlias(trimmedValue === "" ? null : trimmedValue);
closeModal();
};

const clearAlias = () => {
setAliasValue("");
};

return (
<div class="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
<div class="bg-white px-12 py-8 rounded-xl max-w-140 w-1/2 min-w-80">
<div class="text-2xl mb-4">
Edit Alias: {titleCase(checkConfig().checkName)}
</div>

<div class="mb-4">
<div class="text-sm text-gray-600 mb-2">
Set an alias name to display instead of the check's original name.
Leave empty to use the original name.
</div>
<div class="flex gap-2">
<input
type="text"
value={aliasValue()}
onInput={(e) => setAliasValue(e.target.value)}
placeholder={checkConfig().checkName}
class="form-input-custom flex-1"
/>
{aliasValue() && (
<button
class="btn-default btn-gray !text-sm"
onClick={clearAlias}
>
Clear
</button>
)}
</div>
</div>

<div class="flex justify-end gap-2 space-x-2">
<button class="btn-default btn-gray !text-sm" onClick={closeModal}>
Cancel
</button>
<button
class="btn-default btn-blue !text-sm"
onClick={confirmAndClose}
>
Save
</button>
</div>
</div>
</div>
);
};

export default EditAliasModal;
23 changes: 16 additions & 7 deletions builder-frontend/src/components/project/preview/Results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,23 @@ export default function Results({
</Switch>
</div>
<div class="flex flex-col">
<div>
{check.name}
<Show when={check.module || check.version}>
<span class="text-gray-500 ml-1">
({[check.module, check.version].filter(Boolean).join(" v")})
<Show when={check.aliasName} fallback={
<div>
{check.name}
<Show when={check.module || check.version}>
<span class="text-gray-500 ml-1">
({[check.module, check.version].filter(Boolean).join(", v")})
</span>
</Show>
</div>
}>
<div>
{check.aliasName}
<span class="text-gray-500 text-sm ml-1">
({check.name}, {[check.module, check.version].filter(Boolean).join(", v")})
</span>
</Show>
</div>
</div>
</Show>
<Show when={check.parameters && Object.keys(check.parameters).length > 0}>
<div class="text-gray-500 text-sm">
{formatParameters(check.parameters)}
Expand Down
1 change: 1 addition & 0 deletions builder-frontend/src/components/project/preview/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface BenefitResult {
}
interface CheckResult {
name: string;
aliasName?: string;
result: OptionalBoolean;
module: string;
version: string;
Expand Down
Loading
Loading