Skip to content

feat: create program area form #718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 82 commits into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
4c7cf21
feat: wip - user management table
mcmcgrath13 May 6, 2025
29e95ed
fix: creating client side sortable paginated table
mcmcgrath13 May 7, 2025
fb55012
fix: wip - styling
mcmcgrath13 May 7, 2025
76e3bf5
fix: wip - styling
mcmcgrath13 May 7, 2025
e1ec2b7
fix: types
mcmcgrath13 May 7, 2025
ee5aa5a
fix: table sorting
mcmcgrath13 May 7, 2025
e603a8d
fix: types, formating, get program area names
mcmcgrath13 May 7, 2025
87d957f
fix: continued types and formatting, add janky form to add users for …
mcmcgrath13 May 7, 2025
8ac6a2d
fix: put hacks behind flag so it won't deploy
mcmcgrath13 May 7, 2025
8ae40de
Merge branch 'main' of https://github.com/CDCgov/dibbs-ecr-viewer int…
mcmcgrath13 May 7, 2025
4f7be09
refactor: dry things out mroe
mcmcgrath13 May 8, 2025
7bba0dd
test: update unit tests
mcmcgrath13 May 8, 2025
d0bad24
test: pagination bar
mcmcgrath13 May 8, 2025
c8b7e97
test: organization, add sortable header tests
mcmcgrath13 May 8, 2025
983af37
test: paginated sorted table tests
mcmcgrath13 May 8, 2025
2de8959
test: add e2e test for page
mcmcgrath13 May 8, 2025
2ced1f0
test: fix integration list user tests
mcmcgrath13 May 8, 2025
687a7b9
feat: wip - user details pane
mcmcgrath13 May 8, 2025
b4aa89b
feat: wip - side panel
mcmcgrath13 May 8, 2025
e4356d3
fix: no program area text
mcmcgrath13 May 8, 2025
6173db3
refactor: abstract out the side panel
mcmcgrath13 May 9, 2025
f2fba4d
docs: more deets
mcmcgrath13 May 9, 2025
12ca454
fix: merge in main
mcmcgrath13 May 9, 2025
13f09c9
fix: merge in upstream
mcmcgrath13 May 9, 2025
ca2b8f3
feat: pipe through conditions
mcmcgrath13 May 9, 2025
c0798ab
feat: dibbs accordion, add program management addition hackery
mcmcgrath13 May 9, 2025
5a63213
fix: add condition count
mcmcgrath13 May 9, 2025
3b373a2
test: update snapshots for new classname
mcmcgrath13 May 9, 2025
1000bfa
test: update list program area integration test
mcmcgrath13 May 9, 2025
adf181d
test: expand e2e test
mcmcgrath13 May 9, 2025
f53d990
test: lighthouse admin test
mcmcgrath13 May 9, 2025
a34e7f0
Apply suggestions from code review
mcmcgrath13 May 9, 2025
1a50dff
test: side panel unit test
mcmcgrath13 May 9, 2025
67b291e
test: e2e check both names
mcmcgrath13 May 9, 2025
1a0c4b1
fix: force modal to client side since it requires document
mcmcgrath13 May 9, 2025
4b82cb1
fix: pr feedback
mcmcgrath13 May 9, 2025
15f290a
fix: enclose detail list in a list
mcmcgrath13 May 9, 2025
1c9939e
fix: focus close button on modal open
mcmcgrath13 May 9, 2025
a297bf3
test: axe fixing
mcmcgrath13 May 9, 2025
44c6738
fix: hydration error, wip loading state
mcmcgrath13 May 9, 2025
04dcbf1
fix: pr feedback
mcmcgrath13 May 12, 2025
5f11b25
Merge branch 'main' of https://github.com/CDCgov/dibbs-ecr-viewer int…
mcmcgrath13 May 12, 2025
5df0fe8
fix: merge in upstream
mcmcgrath13 May 12, 2025
8514596
fix: debugging hydration warning
mcmcgrath13 May 12, 2025
994d032
fix: hydration issue
mcmcgrath13 May 12, 2025
d6132d0
fix: merge in main
mcmcgrath13 May 12, 2025
9312559
test: update snapshots
mcmcgrath13 May 12, 2025
5104747
fix: back out loading changes
mcmcgrath13 May 12, 2025
8e68fba
test: update snapshots
mcmcgrath13 May 12, 2025
54683b1
fix: hacks to avoid styling hacks
mcmcgrath13 May 12, 2025
c6645c1
refactor: use color utility
mcmcgrath13 May 12, 2025
aefb50c
fix: cleanup
mcmcgrath13 May 12, 2025
7765206
Merge branch 'main' into mcm/feat-user-deets
mcmcgrath13 May 12, 2025
8f21610
Merge branch 'main' into mcm/feat-user-deets
mcmcgrath13 May 13, 2025
99d4293
test: add longer timeout for a11y test
mcmcgrath13 May 13, 2025
a3817c2
feat: wip - program management table
mcmcgrath13 May 13, 2025
c0e3515
test: add e2e test
mcmcgrath13 May 13, 2025
1ec5066
test: unit tests
mcmcgrath13 May 13, 2025
6182e8a
feat: wip - program area form basics
mcmcgrath13 May 13, 2025
f075ac5
feat: wip - make the form do things
mcmcgrath13 May 14, 2025
f070172
fix: cleanup
mcmcgrath13 May 14, 2025
2770f11
fix: merge in main
mcmcgrath13 May 15, 2025
eadfe93
refactor: abstract out admin login not found check
mcmcgrath13 May 15, 2025
0f4c8b8
fix: disabling, weird ts error, counts
mcmcgrath13 May 15, 2025
9befd18
fix: submit the form, cleanup
mcmcgrath13 May 15, 2025
bf603a6
refactor: abstract out form page thing
mcmcgrath13 May 16, 2025
ba3f7d1
refactor: rearranging
mcmcgrath13 May 16, 2025
8fbb6c1
fix: finish rearranging
mcmcgrath13 May 16, 2025
7c092a0
test: update existing test, add integration tests for admin checks
mcmcgrath13 May 16, 2025
b116889
test: unit test form page content thing
mcmcgrath13 May 16, 2025
4a5d3aa
test: program create page
mcmcgrath13 May 16, 2025
6ae9249
fix: words
mcmcgrath13 May 16, 2025
3e05f9b
fix: some ally - blocked on checkbox in button
mcmcgrath13 May 16, 2025
bc6f0d9
fix: move header checkbox to buttons, update tests
mcmcgrath13 May 19, 2025
b478058
test: more unit tests
mcmcgrath13 May 19, 2025
282dd25
fix: abstract submti action, e2e tests
mcmcgrath13 May 19, 2025
4022399
test: better handle parallel testing
mcmcgrath13 May 19, 2025
9320a26
fix: aria-controls, abstract buttons
mcmcgrath13 May 19, 2025
8e418a7
Merge branch 'main' into mcm/feat-add-prog
mcmcgrath13 May 20, 2025
2e2485d
fix: typo
mcmcgrath13 May 20, 2025
217bc69
Merge branch 'main' of https://github.com/CDCgov/dibbs-ecr-viewer int…
mcmcgrath13 May 20, 2025
eddabf4
Merge branch 'main' into mcm/feat-add-prog
mcmcgrath13 May 20, 2025
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
37 changes: 37 additions & 0 deletions containers/ecr-viewer/e2e/dual/admin-program.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,41 @@ test.describe("program management page", () => {
}).analyze();
expect(accessibilityScanResultsBase.violations).toEqual([]);
});

test("should create a program", async ({ page }) => {
await page.goto("/ecr-viewer/admin/program");

await expect(page.getByText("Program management")).toBeVisible();

await page.getByText("Create program area").click();

await page.waitForURL("/ecr-viewer/admin/program/create");
await expect(
page.getByRole("heading", { name: "Create program area" }),
).toBeVisible();

const accessibilityScanResultsBase = await new AxeBuilder({
page,
}).analyze();
expect(accessibilityScanResultsBase.violations).toEqual([]);

// Find a random condition (avoid clashes in parallel tests)
const checkboxes = await page.getByRole("checkbox").all();
const index = Math.floor(Math.random() * checkboxes.length);
const checkbox = checkboxes[index];
const conditionName = await checkbox.inputValue();
await checkbox.scrollIntoViewIfNeeded();
await checkbox.dispatchEvent("click");

page.getByLabel("Program area name").fill(conditionName);

await page
.getByRole("button", { name: "Create program area" })
.first()
.click();

await page.waitForURL("/ecr-viewer/admin/program");
await expect(page.getByText("Program management")).toBeVisible();
await expect(page.getByText(conditionName)).toBeVisible();
});
});
13 changes: 8 additions & 5 deletions containers/ecr-viewer/integration/programAreaService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe("program area service", () => {

it("should update a program area name", async () => {
const progName = "Sad Times";
const id = await createProgramArea(progName, []);
const id = await createProgramArea(progName, ["123"]);

const beforeNameConds = await listConditionReferences();
await updateProgramArea(id, { name: "Happy Days" });
Expand All @@ -129,11 +129,12 @@ describe("program area service", () => {

it("should update a program area conditions", async () => {
const progName = "Sad Times";
const id = await createProgramArea(progName, []);
const id = await createProgramArea(progName, ["123"]);

const beforeConds = await listConditionReferences();
const beforeCond = beforeConds.filter((c) => c.program_area_uuid === id);
expect(beforeCond).toBeArrayOfSize(0);
expect(beforeCond).toBeArrayOfSize(1);
expect(beforeCond[0]).toHaveProperty("code", "123");
await updateProgramArea(id, { conditions: ["789"] });
const afterConds = await listConditionReferences();
expect(beforeConds).not.toStrictEqual(afterConds);
Expand All @@ -144,7 +145,7 @@ describe("program area service", () => {

it("should delete a program area", async () => {
const beforeCreate = await listProgramAreas();
const id = await createProgramArea("test", []);
const id = await createProgramArea("test", ["123"]);
const afterCreate = await listProgramAreas();

await updateUserProgramAreas(adminId!, [id, progId!]);
Expand All @@ -155,7 +156,9 @@ describe("program area service", () => {

const afterDelete = await listProgramAreas();

expect(beforeCreate).toStrictEqual(afterDelete);
expect(beforeCreate.map(({ uuid }) => uuid)).toStrictEqual(
afterDelete.map(({ uuid }) => uuid),
);
expect(afterDelete).toBeArrayOfSize(3);
expect(afterCreate).toBeArrayOfSize(4);

Expand Down
84 changes: 70 additions & 14 deletions containers/ecr-viewer/integration/userService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,64 @@
* @jest-environment node
*/

import { notFound } from "next/navigation";

import { getDb } from "@/app/data/metadataDb/database";
import { Core } from "@/app/data/metadataDb/types/core";
import { createProgramArea } from "@/app/services/programAreaService";
import {
createInitialAdminUser,
createUser,
deleteUser,
getCheckAdmin,
listUserProgramAreas,
listUsers,
notFoundUnlessAdmin,
updateUser,
updateUserProgramAreas,
} from "@/app/services/userService";
import { getLoggedInUserSession } from "@/app/utils/auth-utils";

import { buildCore, dropExisting } from "./helpers/ddl";

beforeAll(async () => {
await buildCore();
});

afterAll(async () => {
await dropExisting();
});

const adminEmail = "[email protected]";
const adminName = "Adam Admin";
const userEmail = "[email protected]";

const cond123 = {
code: "123",
concept_name: "condition 1 (disease)",
condition_name: "condition 1",
condition_category: "category",
};

const UUID_REGEX =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;

jest.mock("../src/app/utils/auth-utils", () => ({
getLoggedInUserSession: jest
.fn()
.mockResolvedValue({ name: "Adam Admin", email: "[email protected]" }),
}));
jest.mock("../src/app/utils/auth-utils");

beforeAll(async () => {
await buildCore();
await getDb<Core>()
.insertInto("condition_reference")
.values(cond123)
.execute();
});

beforeEach(() => {
(getLoggedInUserSession as jest.Mock).mockResolvedValue({
name: "Adam Admin",
email: "[email protected]",
});
});

afterEach(() => {
jest.clearAllMocks();
});

afterAll(async () => {
await dropExisting();
});

describe("user service", () => {
let adminId;
Expand Down Expand Up @@ -173,7 +198,7 @@ describe("user service", () => {
it("should update a user's program areas", async () => {
// standard user created in prior test
await updateUser(userId!, { name: "Olga Nunes" });
const progId = await createProgramArea("Disease", []);
const progId = await createProgramArea("Disease", ["123"]);

await updateUserProgramAreas(userId!, [progId]);

Expand Down Expand Up @@ -214,4 +239,35 @@ describe("user service", () => {
const progAreas = await listUserProgramAreas(userId!);
expect(progAreas).toStrictEqual([]);
});

describe("getCheckAdmin", () => {
it("should return admin if user is an admin", async () => {
const admin = await getCheckAdmin("do a thing");
expect(admin.email).toBe(adminEmail);
});

it("should error if user is not an admin", async () => {
(getLoggedInUserSession as jest.Mock).mockResolvedValue({
name: "Sally Standard",
email: "[email protected]",
});
await expect(getCheckAdmin("do a thing")).rejects.toThrow();
});
});

describe("notFoundUnessAdmin", () => {
it("should do nothing if user is an admin", async () => {
await notFoundUnlessAdmin();
expect(notFound).not.toHaveBeenCalled();
});

it("should notFound if user is not an admin", async () => {
(getLoggedInUserSession as jest.Mock).mockResolvedValue({
name: "Sally Standard",
email: "[email protected]",
});
await notFoundUnlessAdmin();
expect(notFound).toHaveBeenCalled();
});
});
});
2 changes: 1 addition & 1 deletion containers/ecr-viewer/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion containers/ecr-viewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"@next/eslint-plugin-next": "^14.2.5",
"@playwright/test": "^1.51.1",
"@smithy/util-stream": "^4.1.1",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/fhir": "^0.0.41",
Expand Down
19 changes: 19 additions & 0 deletions containers/ecr-viewer/src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ReactNode } from "react";

import Header from "@/app/components/Header";

/**
* @param props React props
* @param props.children Page content
* @returns Basic elements present on all admin pages
*/
const AdminPageLayout = ({ children }: { children: ReactNode }) => {
return (
<div className="display-flex flex-column height-viewport">
<Header />
{children}
</div>
);
};

export default AdminPageLayout;
Loading