Skip to content
Merged
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
@@ -0,0 +1,21 @@
import React from 'react';
import { ProgramProgressCoursesProps } from '../data/types';

const ProgramProgressCourses: React.FC<ProgramProgressCoursesProps> = ({
courseData,
}) => (
<div>
{courseData.uuid}
<div>
|| Courses In Progress ||
</div>
<div>
|| Courses Remaining ||
</div>
<div>
|| Courses Completed ||
</div>
</div>
);

export default ProgramProgressCourses;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { ProgramProgressHeaderProps } from '../data/types';
import { getProgramIcon } from '../data/util';

const ProgramProgressHeader: React.FC<ProgramProgressHeaderProps> = ({
programTitle, programType, authoringOrganizations,
}) => {
const programIcon = getProgramIcon(programType);

return (
<div>
<p>
{programTitle}
</p>
<p>
{programType}
</p>
<p>
{authoringOrganizations && authoringOrganizations.length}
</p>
<img src={programIcon} alt={`${programType} icon`} />
</div>
);
};

export default ProgramProgressHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { ProgramProgressInfoProps } from '../data/types';

const ProgramProgressInfo: React.FC<ProgramProgressInfoProps> = ({
allCoursesCompleted, totalCoursesInProgram,
}) => (
allCoursesCompleted
? (
<div>
Render all courses completed text
</div>
)
: (
<div>
Render upgrade button and info about the {totalCoursesInProgram} courses in this program
</div>
)
);

export default ProgramProgressInfo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';

const ProgramProgressSidebar: React.FC = () => (
<>
<div>
|| Program Certificate Progress ||
</div>
<div>
|| Earned Certificates ||
</div>
<div>
|| Program Record ||
</div>
</>
);

export default ProgramProgressSidebar;
83 changes: 83 additions & 0 deletions src/containers/ProgramDashboard/ProgramProgress/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet';
import { useParams } from 'react-router-dom';
import { Col, Container, Row } from '@openedx/paragon';
import { logError } from '@edx/frontend-platform/logging';
import { camelCaseObject } from '@edx/frontend-platform/utils';
import { getProgramProgressData } from '../data/api';
import { ProgramProgressData } from '../data/types';
import ProgramProgressCourses from './ProgramProgressCourses';
import ProgramProgressHeader from './ProgramProgressHeader';
import ProgramProgressSidebar from './ProgramProgressSidebar';
import ProgramProgressInfo from './ProgramProgressInfo';

const ProgramProgress: React.FC = () => {
const [programProgressData, setProgramProgressData] = useState<ProgramProgressData>();
const [programProgressEndpointError, setProgramProgressEndpointError] = useState(false);
const hasProgramProgressData : Boolean = programProgressData?.courseData
&& programProgressData.programData
&& programProgressData.urls;

// TODO: for review: https://stackoverflow.com/questions/75706357/react-useparams-returns-string-undefined

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what's this about?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

On line 22, there is some typecasting going on. I've never had to do it before but it's the only way I've found to satisfy Typescript when using the useParams hook from React Router. I put the link to the stackoverflow that gave me the idea so that reviewers could easily see why I did that. But it should probably be removed and just included in the PR description

const { uuid } = useParams() as { uuid: string };
useEffect(() => {
getProgramProgressData(uuid)
.then(responseData => {
setProgramProgressData(camelCaseObject(responseData.data));
})
.catch(err => {
logError(err);
setProgramProgressEndpointError(true);
});
}, [uuid]);

if (programProgressEndpointError) {
return (
<div>Not found page</div>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this placeholder?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes it is. It will be replaced with an alert similar to what's in the enterprise version

);
}

if (!hasProgramProgressData) {
return (
<div>Loading...</div>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this the error condition that we would see if there is a request out that hasn't returned yet? Is this placeholder?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes it is placeholder. I will replace it with the LoadingSpinner (link) from Paragon.

And yes it is what the user will see while the request is pending. It's the first thing that is rendered to the page, then the request is made, the response comes back, updates state variables, which triggers a re-render in React, which will either render the error state (NotFoundPage) or the actual page

);
}

const programData = programProgressData?.programData;
const courseData = programProgressData?.courseData;

const totalCoursesInProgram = (courseData.notStarted?.length || 0)
+ (courseData.completed?.length || 0)
+ (courseData.inProgress?.length || 0);

const allCoursesCompleted = !courseData.notStarted?.length
&& !courseData.inProgress?.length
&& courseData.completed?.length;

return (
<>
<Helmet title={`${programData.title}`} />
<Container>
<ProgramProgressHeader
programTitle={programData?.title}
programType={programData?.type}
authoringOrganizations={programData?.authoringOrganizations}
/>
<Row>
<Col>
<ProgramProgressInfo
allCoursesCompleted={allCoursesCompleted}
totalCoursesInProgram={totalCoursesInProgram}
/>
<ProgramProgressCourses courseData={courseData} />
</Col>
<Col>
<ProgramProgressSidebar />
</Col>
</Row>
</Container>
</>
);
};

export default ProgramProgress;
1 change: 1 addition & 0 deletions src/containers/ProgramDashboard/ProgramsList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const ProgramsList: React.FC = () => {
setProgramsData(camelCaseObject(responseData.data));
})
.catch(err => logError(err));
// TODO: add error handling alert component
}, []);

return (
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading