Skip to content
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

Updated details dashboard logic to avoid merging 3 APIs and utilize f… #488

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
16 changes: 11 additions & 5 deletions __tests__/components/dashtable_v2.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ import { render } from '@testing-library/react';
import React from 'react';
import GlobalDashboardTable from '../../components/dashtable_v2.js';

import {studentData, certifications, classroomId, timestamps, totalChallenges} from '../../testing_data/testing-data';
import {
studentData,
classroomId,
studentsAreEnrolledInSuperblocks,
totalChallenges
} from '../../testing_data/testing-data';

describe('GlobalDashboardTable', () => {
// Define a fixed time in milliseconds
const fixedTime = new Date('2016-03-09T12:00:00Z').getTime();

// Spy on Date.prototype.getTime and mock its implementation
const getTimeSpy = jest.spyOn(Date.prototype, 'getTime').mockImplementation(() => fixedTime);
const getTimeSpy = jest
.spyOn(Date.prototype, 'getTime')
.mockImplementation(() => fixedTime);

// Restore the original getTime method after the test
afterEach(() => {
Expand All @@ -20,13 +27,12 @@ describe('GlobalDashboardTable', () => {
const { container } = render(
<GlobalDashboardTable
studentData={studentData}
certifications={certifications}
classroomId={classroomId}
totalChallenges={totalChallenges}
timestamps={timestamps}
studentsAreEnrolledInSuperblocks={studentsAreEnrolledInSuperblocks}
/>
);
expect(getTimeSpy).toHaveBeenCalled();
expect(container).toMatchSnapshot();
});
});
});
62 changes: 62 additions & 0 deletions components/DetailsCSS.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.student_header {
display: flex;
font-size: 1.1rem;
margin: 10px;
border-bottom: 2px solid purple;
color: navy;
font-weight: 100;
background-color: hsl(194, 35%, 76%);
max-width: fit-content;
}

.board_container {
border: 4px solid black;
padding: 10px;
margin: 0px 5px 1px;
background-color: grey;
color: white;
}

.list_container {
display: flex;

flex-direction: row;
align-items: center;
gap: 10px;
}

.list_container h1 {
font-weight: bold;
font-size: 1.2rem;
}
.list_container button {
font-size: 0.7rem;
text-transform: uppercase;
border: 1px solid navy;

padding: 5px 10px;
background-color: orange;
}

.list_container button:hover {
background-color: rgb(89, 103, 174);
color: white;
}
.inner_comp {
background-color: grey;
color: white;
}

.details_progress_stats {
background-color: rgb(27, 15, 86);
color: white;
display: flex;
flex-direction: row;
justify-content: space-between;
border: 1px solid rgb(93, 0, 255);
padding: 10px;
}

.detailsBlockTitle {
font-size: 1;
}
46 changes: 46 additions & 0 deletions components/DetailsDashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import styles from './DetailsCSS.module.css';
import DetailsDashboardList from './DetailsDashboardList';
//getStudentProgressInSuperblock

import { getStudentProgressInSuperblock } from '../util/api_proccesor';

export default function DetailsDashboard(props) {
const printSuperblockTitle = individualSuperblockJSON => {
let indexOfTitleInSuperblockTitlesArray =
props.superblocksDetailsJSONArray.indexOf(individualSuperblockJSON);
let superblockTitle =
props.superblockTitles[indexOfTitleInSuperblockTitlesArray];
return superblockTitle;
};

const superblockProgress = superblockDashedName => {
let studentProgress = props.studentData;

return getStudentProgressInSuperblock(
studentProgress,
superblockDashedName
);
};

return (
<>
{props.superblocksDetailsJSONArray.map((arrayOfBlockObjs, idx) => {
let index = props.superblocksDetailsJSONArray.indexOf(arrayOfBlockObjs);
let superblockDashedName =
props.superblocksDetailsJSONArray[index][0].superblock;
let progressInBlocks = superblockProgress(superblockDashedName);
let superblockTitle = printSuperblockTitle(arrayOfBlockObjs);
return (
<div key={idx} className={styles.board_container}>
<DetailsDashboardList
superblockTitle={superblockTitle}
blockData={arrayOfBlockObjs}
studentProgressInBlocks={progressInBlocks}
></DetailsDashboardList>
</div>
);
})}
Comment on lines +28 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an edge case here where if we do not have a superblocksDetailsJSONArray is undefined, or of length 0? We can add conditional rendering to check if our superblocksDetailsJSONArray is undefined/has a length of 0, display a message to alert the user that there is no data to display

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @GuillermoFloresV this is a good separate issue for someone wanting to contribute to the UI/Design side of the page. I prefer to create a separate issue for conditional rendering.

</>
);
}
70 changes: 70 additions & 0 deletions components/DetailsDashboardList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { useState } from 'react';
import styles from './DetailsCSS.module.css';
import { getStudentTotalChallengesCompletedInBlock } from '../util/api_proccesor';

export default function DetailsDashboardList(props) {
const [hideDetails, setHideDetails] = useState(true);
const [buttonText, setButtonText] = useState('View details');

const handleShowDetails = () => {
if (hideDetails) {
setHideDetails(false);
} else {
setHideDetails(true);
}
Comment on lines +10 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think we can reduce this to
const handleShowDetails = () => { setHideDetails(!hideDetails) } ...
just to shorten the function a little bit


handleButtonText(hideDetails);
};

const handleButtonText = hideDetails => {
if (hideDetails) {
setButtonText('View less');
} else {
setButtonText('View details');
}
};

const getStudentsProgressInBlock = blockName => {
return getStudentTotalChallengesCompletedInBlock(
props.studentProgressInBlocks,
blockName
);
};

return (
<>
<div className={styles.list_container}>
<h1>{props.superblockTitle} </h1>

<button onClick={handleShowDetails}>{buttonText}</button>
</div>
<div className={styles.inner_comp}>
{hideDetails ? (
''
) : (
<>
<ul>
<li>
{props.blockData.map((blockDetails, idx) => {
return (
<div className={styles.details_progress_stats} key={idx}>
<h1 className={styles.detailsBlockTitle}>
{blockDetails.blockName}
</h1>
<h1 className={styles.focus}>
{getStudentsProgressInBlock(blockDetails.selector) +
'/' +
blockDetails.allChallenges.length}
</h1>
</div>
);
})}
</li>
</ul>
</>
)}
</div>
</>
);
}
11 changes: 5 additions & 6 deletions components/dashtable_v2.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { useTable } from 'react-table';
import React from 'react';
import getStudentActivity from './studentActivity';
import { extractStudentCompletionTimestamps } from '../util/api_proccesor';

export default function GlobalDashboardTable(props) {
let grandTotalChallenges = props.totalChallenges;

let rawStudentSummary = props.studentData.map(studentJSON => {
let email = studentJSON.email;
let completionTimestamps = [];

props.timestamps.forEach(timestampObj => {
if (timestampObj.name === email) {
completionTimestamps = timestampObj.completedTimestamps;
}
});
completionTimestamps = extractStudentCompletionTimestamps(
studentJSON.certifications
);

let rawStudentActivity = {
recentCompletions: completionTimestamps
};

let studentActivity = getStudentActivity(rawStudentActivity);

let numCompletions = completionTimestamps.length;

let percentageCompletion = (
Expand Down
51 changes: 34 additions & 17 deletions pages/dashboard/v2/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { getSession } from 'next-auth/react';
import GlobalDashboardTable from '../../../components/dashtable_v2';
import React from 'react';
import {
createDashboardObject,
getTotalChallenges,
createSuperblockDashboardObject,
getTotalChallengesForSuperblocks,
getDashedNamesURLs,
getSuperBlockJsons,
formattedStudentData,
getCompletionTimestamps
fetchStudentData,
checkIfStudentHasProgressDataForSuperblocksSelectedByTeacher
} from '../../../util/api_proccesor';

export async function getServerSideProps(context) {
Expand Down Expand Up @@ -58,35 +58,52 @@ export async function getServerSideProps(context) {
}
});

let formattedStudentDataResponse = await formattedStudentData();

let timestamps = getCompletionTimestamps(formattedStudentDataResponse);

let superblockURLS = await getDashedNamesURLs(
certificationNumbers.fccCertifications
);

let superBlockJsons = await getSuperBlockJsons(superblockURLS);
let dashboardObjs = createDashboardObject(superBlockJsons);
let totalChallenges = getTotalChallenges(dashboardObjs);
let superBlockJsons = await getSuperBlockJsons(superblockURLS); // this is an array of urls
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a comment letting us know this is an array of URLs, could we make this variable more descriptive so that it describes what it is supposed to be?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @GuillermoFloresV this name choice was introduced prior to me joining FCC which is why I added the comment. I could also make a separate 'easy' issue for this for CTI students or update it, @utsab let me know what you would prefer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let dashboardObjs = await createSuperblockDashboardObject(superBlockJsons);

let totalChallenges = getTotalChallengesForSuperblocks(dashboardObjs);

let studentData = await fetchStudentData();

// Temporary check to map/accomodate hard-coded mock student data progress in unselected superblocks by teacher
let studentsAreEnrolledInSuperblocks =
checkIfStudentHasProgressDataForSuperblocksSelectedByTeacher(
studentData,
dashboardObjs
);
studentData.forEach(studentJSON => {
let indexToCheckProgress = studentData.indexOf(studentJSON);
let isStudentEnrolledInAtLeastOneSuperblock =
studentsAreEnrolledInSuperblocks[indexToCheckProgress].every(
val => val === true
);

if (!isStudentEnrolledInAtLeastOneSuperblock) {
studentData[indexToCheckProgress].certifications = [];
}
});

return {
props: {
userSession,
classroomId: context.params.id,
studentData: formattedStudentDataResponse,
studentData,
totalChallenges: totalChallenges,
timestamps: timestamps
studentsAreEnrolledInSuperblocks
}
};
}

export default function Home({
userSession,
studentData,
classroomId,
totalChallenges,
timestamps
studentData,
studentsAreEnrolledInSuperblocks
}) {
return (
<Layout>
Expand All @@ -106,10 +123,10 @@ export default function Home({
</div>
</Navbar>
<GlobalDashboardTable
studentData={studentData}
classroomId={classroomId}
timestamps={timestamps}
totalChallenges={totalChallenges}
studentData={studentData}
studentsAreEnrolledInSuperblocks={studentsAreEnrolledInSuperblocks}
></GlobalDashboardTable>
</>
)}
Expand Down
Loading