Skip to content

Commit e9abd1a

Browse files
Merge pull request #15 from akirachix/develop
Develop
2 parents 5502869 + 26c03d7 commit e9abd1a

File tree

44 files changed

+1972
-859
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1972
-859
lines changed

biopima/next.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@ const nextConfig: NextConfig = {
55
};
66

77
export default nextConfig;
8+
module.exports = {
9+
images: {
10+
domains: ['biopima-cfbfed4a262a.herokuapp.com'],
11+
},
12+
};

biopima/package-lock.json

Lines changed: 66 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

biopima/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
},
1212
"dependencies": {
1313
"@heroicons/react": "^2.2.0",
14+
"chart.js": "^4.5.0",
1415
"jest-canvas-mock": "^2.5.2",
1516
"lucide-react": "^0.544.0",
1617
"next": "15.5.3",
1718
"react": "19.1.0",
19+
"react-chartjs-2": "^5.3.0",
1820
"react-dom": "19.1.0",
1921
"react-icons": "^5.5.0",
2022
"recharts": "^3.2.1"
@@ -25,6 +27,8 @@
2527
"@testing-library/dom": "^10.4.1",
2628
"@testing-library/jest-dom": "^6.8.0",
2729
"@testing-library/react": "^16.3.0",
30+
"@testing-library/user-event": "^14.6.1",
31+
"@types/chart.js": "^2.9.41",
2832
"@types/jest": "^30.0.0",
2933
"@types/node": "^20",
3034
"@types/react": "^19",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const baseUrl = process.env.BASE_URL;
2+
3+
export async function POST(request: Request) {
4+
try {
5+
const body = await request.json();
6+
const { email, password } = body;
7+
if (!email || !password) {
8+
return new Response(
9+
"Missing required values: email, password",
10+
{ status: 400 }
11+
);
12+
}
13+
const response = await fetch(`${baseUrl}/api/set-password/`, {
14+
method: "POST",
15+
headers: { "Content-Type": "application/json" },
16+
body: JSON.stringify({
17+
email,
18+
password
19+
}),
20+
})
21+
22+
23+
24+
const result = await response.json();
25+
return new Response(JSON.stringify(result), {
26+
status: 201,
27+
statusText: "Password set successfully",
28+
});
29+
} catch (error) {
30+
return new Response("Failed to set password: " + (error as Error).message, {
31+
status: 500,
32+
});
33+
}
34+
}

biopima/src/app/api/user/route.ts

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export async function GET() {
1313
const result = await response.json();
1414
return NextResponse.json(result, {
1515
status: 200,
16-
statusText: 'User created successfully'
16+
statusText: result.message
1717
});
1818
} catch (error) {
1919
console.error('GET request error:', error);
@@ -29,35 +29,44 @@ export async function GET() {
2929
export async function POST(request: Request) {
3030
try {
3131
const bodyData = await request.json();
32-
const {name, email, phone_number, username} = bodyData;
32+
const { name, email, phone_number, username } = bodyData;
3333
if (!name || !email || !phone_number || !username) {
3434
return NextResponse.json(
3535
{ message: 'Missing required fields' },
36-
{
37-
status: 400,
38-
}
36+
{ status: 400 }
3937
);
4038
}
4139

4240
const response = await fetch(`${baseUrl}/api/user/`, {
4341
method: 'POST',
44-
headers: {
45-
'Content-Type': 'application/json',
46-
},
42+
headers: { 'Content-Type': 'application/json' },
4743
body: JSON.stringify(bodyData),
4844
});
4945

5046
const result = await response.json();
51-
47+
48+
if (response.status === 409 || (result.message && result.message.toLowerCase().includes("already"))) {
49+
return NextResponse.json(
50+
{ message: 'User already exists' },
51+
{ status: 409 }
52+
);
53+
}
54+
55+
if (!response.ok) {
56+
return NextResponse.json(
57+
{ message: result.message || 'User already exists' },
58+
{ status: response.status }
59+
);
60+
}
5261

5362
return new Response(JSON.stringify(result), {
5463
status: 201,
55-
statusText: 'User created successfully',
64+
statusText: result.message,
5665
});
57-
} catch(error){
58-
return new Response((error as Error).message, {
59-
status:500,
60-
statusText: 'Internal server error'
61-
})
62-
}
66+
} catch (error) {
67+
return new Response((error as Error).message, {
68+
status: 500,
69+
statusText: 'Internal server error',
70+
});
71+
}
6372
}

biopima/src/app/chooserole/page.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,18 @@ export default function RolePage() {
3838
<div className="flex flex-row sm:flex-row gap-6">
3939
<button
4040
onClick={() => handleSelect("institution")}
41-
42-
className="border-2 border-[#9EAF1B] bg-transparent text-[#9EAF1B] text-xl md:text-2xl font-bold py-3 px-8 rounded-2xl hover:bg-green-50 transition-all duration-200 shadow-md hover:shadow-lg min-w-[140px] cursor-pointer"
41+
className="border-2 border-[#9EAF1B] bg-transparent text-[#9EAF1B] text-xl md:text-2xl font-bold py-3 px-8 rounded-2xl hover:bg-[#9EAF1B] hover:text-white transition-all duration-200 shadow-md hover:shadow-lg min-w-[140px] cursor-pointer"
4342
>
44-
4543
Institution
4644
</button>
4745

4846
<button
4947
onClick={() => handleSelect("consultant")}
50-
className="bg-[#9EAF1B] text-white text-xl md:text-2xl font-bold py-3 px-8 rounded-2xl hover:bg-[#8a9d18] transition-all duration-200 shadow-md hover:shadow-lg min-w-[140px] cursor-pointer"
48+
className="border-2 border-[#9EAF1B] bg-transparent text-[#9EAF1B] text-xl md:text-2xl font-bold py-3 px-8 rounded-2xl hover:bg-[#9EAF1B] hover:text-white transition-all duration-200 shadow-md hover:shadow-lg min-w-[140px] cursor-pointer"
5149
>
5250
Consultant
5351
</button>
5452
</div>
5553
</div>
5654
);
5755
}
58-
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import ConsultantLayout from "../shared-components/Sidebar/ConsultantLayout";
2+
import Profile from "../profile/page";
3+
4+
export default function ConsultantProfile(){
5+
return(
6+
<ConsultantLayout>
7+
<Profile/>
8+
</ConsultantLayout>
9+
)
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import ConsultantLayout from "../shared-components/Sidebar/ConsultantLayout";
2+
import ReportsPage from "../report/page";
3+
4+
export default function ConsultantProfile(){
5+
return(
6+
<ConsultantLayout>
7+
<ReportsPage/>
8+
</ConsultantLayout>
9+
)
10+
}
Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,53 @@
1-
import { render, screen } from '@testing-library/react';
2-
import ActivityFeed from '.';
3-
4-
describe('ActivityFeed', () => {
5-
let container: HTMLElement;
6-
1+
// src/app/dashboard/components/ActivityFeed/index.test.tsx
2+
import React from "react";
3+
import { render, screen } from "@testing-library/react";
4+
import ActivityFeed from "./index";
5+
6+
// Mock icons for predictable testing
7+
jest.mock("lucide-react", () => ({
8+
Thermometer: (props: React.SVGProps<SVGSVGElement>) => (
9+
<svg data-testid="thermometer-icon" {...props} />
10+
),
11+
AlertTriangle: (props: React.SVGProps<SVGSVGElement>) => (
12+
<svg data-testid="alert-triangle-icon" {...props} />
13+
),
14+
Gauge: (props: React.SVGProps<SVGSVGElement>) => (
15+
<svg data-testid="gauge-icon" {...props} />
16+
),
17+
Activity: (props: React.SVGProps<SVGSVGElement>) => (
18+
<svg data-testid="activity-icon" {...props} />
19+
),
20+
}));
21+
22+
describe("ActivityFeed", () => {
723
beforeEach(() => {
8-
const rendered = render(<ActivityFeed />);
9-
container = rendered.container;
24+
render(<ActivityFeed />);
1025
});
1126

12-
it('renders main heading and subtitle', () => {
13-
expect(screen.getByRole('heading', { name: /Activity Feed/i })).toBeInTheDocument();
14-
expect(screen.getByText(/Recent system events and alerts/i)).toBeInTheDocument();
15-
});
27+
it("renders Temp Warning event correctly", () => {
28+
// Check title
29+
expect(screen.getByText("Temp Warning")).toBeInTheDocument();
1630

17-
it('renders two activity items with correct titles and descriptions', () => {
18-
expect(screen.getByText('Temp Warning')).toBeInTheDocument();
19-
expect(screen.getByText(/Low Temperature Warning: Digester temperature at 34.6°C./i)).toBeInTheDocument();
31+
// Use regex with wildcards to handle extra characters / formatting
32+
expect(screen.getByText(/Low Temperature Warning.*34\.6°C/i)).toBeInTheDocument();
2033

21-
expect(screen.getByText('Leak Warning')).toBeInTheDocument();
22-
expect(screen.getByText(/Leak Warning: Pressure at 1.02 bar \(15.0% drop\)./i)).toBeInTheDocument();
34+
// Check thermometer icon
35+
expect(screen.getByTestId("thermometer-icon")).toBeInTheDocument();
2336
});
2437

25-
it('displays correct timestamps for each alert', () => {
26-
const timestamps = screen.getAllByText(/less than a minute ago/i);
27-
expect(timestamps.length).toBe(2);
28-
});
38+
it("renders Leak Warning event correctly", () => {
39+
// Check title
40+
expect(screen.getByText("Leak Warning")).toBeInTheDocument();
2941

30-
it('renders Thermometer SVG icon in Temp Warning section', () => {
31-
const thermometerIcon = container.querySelector('svg.lucide-thermometer');
32-
expect(thermometerIcon).toBeInTheDocument();
33-
});
42+
// Use regex with wildcards to match the full dynamic text
43+
expect(screen.getByText(/Leak Warning.*1\.02 bar.*15\.0% drop/i)).toBeInTheDocument();
3444

35-
it('renders AlertTriangle SVG icon in Leak Warning section', () => {
36-
const alertTriangleIcon = container.querySelector('svg.lucide-triangle-alert');
37-
expect(alertTriangleIcon).toBeInTheDocument();
45+
// Check alert triangle icon
46+
expect(screen.getByTestId("alert-triangle-icon")).toBeInTheDocument();
3847
});
3948

40-
it('uses correct colors for status indicator dots', () => {
41-
const greenDot = container.querySelector('span.bg-green-500');
42-
expect(greenDot).toBeInTheDocument();
43-
44-
const yellowDot = container.querySelector('span.bg-yellow-700');
45-
expect(yellowDot).toBeInTheDocument();
49+
it("renders overall Activity Feed container and description", () => {
50+
expect(screen.getByText("Activity Feed")).toBeInTheDocument();
51+
expect(screen.getByText("Recent system events and alerts.")).toBeInTheDocument();
4652
});
4753
});

0 commit comments

Comments
 (0)