Skip to content

Commit

Permalink
Merge pull request #749 from eduhub-org/develop
Browse files Browse the repository at this point in the history
Onboarding improved, various other improvements and fixes
  • Loading branch information
steffen74 authored Oct 6, 2023
2 parents 7a9ac1c + 1d85d6b commit c1cde58
Show file tree
Hide file tree
Showing 35 changed files with 868 additions and 1,130 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/update-pr-description.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Update PR Description

on:
pull_request:
types:
- opened
- synchronize

jobs:
update_pr:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0 # Important: this fetches all branches

- name: Fetch additional branches
run: |
git fetch origin staging:staging
git fetch origin develop:develop
- name: Fetch commit messages and update PR description
run: |
# Fetch commit messages
messages=$(git log --pretty=format:"- %s" staging..develop)
# Update PR description
gh pr edit ${{ github.event.pull_request.number }} \
--add-label "auto-updated" \
--body "$messages"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
"editor.defaultFormatter": "ms-python.black-formatter"
},
"python.formatting.provider": "black",
"editor.defaultFormatter": "ms-python.black-formatter"
"editor.defaultFormatter": "ms-python.black-formatter",
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE FUNCTION "public"."set_invitation_expiration_date"()
-- RETURNS TRIGGER AS $$
-- BEGIN
-- IF (TG_OP = 'INSERT' AND NEW.status = 'INVITED') OR
-- (TG_OP = 'UPDATE' AND NEW.status = 'INVITED') THEN
-- NEW."invitationExpirationDate" = NOW() + interval '2 days';
-- END IF;
-- RETURN NEW;
-- END;
-- $$ LANGUAGE plpgsql;

-- CREATE TRIGGER "set_invitation_expiration_date_trigger"
-- BEFORE INSERT OR UPDATE ON "public"."CourseEnrollment"
-- FOR EACH ROW
-- EXECUTE PROCEDURE "public"."set_invitation_expiration_date"();
-- COMMENT ON TRIGGER "set_invitation_expiration_date_trigger" ON "public"."CourseEnrollment"
-- IS 'trigger to set default value of column "invitationExpirationDate" to two days after creation when status is set or updated to INVITED';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CREATE OR REPLACE FUNCTION "public"."set_invitation_expiration_date"()
RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'INSERT' AND NEW.status = 'INVITED') OR
(TG_OP = 'UPDATE' AND NEW.status = 'INVITED') THEN
NEW."invitationExpirationDate" = NOW() + interval '2 days';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER "set_invitation_expiration_date_trigger"
BEFORE INSERT OR UPDATE ON "public"."CourseEnrollment"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_invitation_expiration_date"();
COMMENT ON TRIGGER "set_invitation_expiration_date_trigger" ON "public"."CourseEnrollment"
IS 'trigger to set default value of column "invitationExpirationDate" to two days after creation when status is set or updated to INVITED';
3 changes: 2 additions & 1 deletion frontend-nx/.prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"singleQuote": true
"singleQuote": true,
"printWidth": 120
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,45 @@ import {
useEffect,
useState,
} from 'react';
import { IUserProfile } from '../../../hooks/user';
import { useKeycloakUserProfile, useUserId } from '../../../hooks/user';
import AchievementOptionDropDown from '../../achievements/AchievementOptionDropDown';
import { IUserProfile } from '../../hooks/user';
import { useKeycloakUserProfile, useUserId } from '../../hooks/user';
import AchievementOptionDropDown from '../achievements/AchievementOptionDropDown';
import {
AchievementOptionCourses,
AchievementOptionCoursesVariables,
} from '../../../queries/__generated__/AchievementOptionCourses';
import { Button } from '../../common/Button';
import { useAuthedQuery } from '../../../hooks/authedQuery';
} from '../../queries/__generated__/AchievementOptionCourses';
import { Button } from '../common/Button';
import { useAuthedQuery } from '../../hooks/authedQuery';
import { BlockTitle } from '@opencampus/shared-components';
import FormToUploadAchievementRecord from '../../FormToUploadAchievementRecord';
import FormToUploadAchievementRecord from '../FormToUploadAchievementRecord';
import {
makeFullName,
formattedDate,
formattedDateWithTime,
} from '../../../helpers/util';
import { AlertMessageDialog } from '../../common/dialogs/AlertMessageDialog';
import { ACHIEVEMENT_OPTION_COURSES } from '../../../queries/achievementOption';
import { INSERT_AN_ACHIEVEMENT_RECORD } from '../../../queries/achievementRecord';
} from '../../helpers/util';
import { AlertMessageDialog } from '../common/dialogs/AlertMessageDialog';
import { ACHIEVEMENT_OPTION_COURSES } from '../../queries/achievementOption';
import { INSERT_AN_ACHIEVEMENT_RECORD } from '../../queries/achievementRecord';

import {
order_by,
AchievementRecordType_enum,
AchievementRecordRating_enum,
} from '../../../__generated__/globalTypes';
} from '../../__generated__/globalTypes';
import {
AchievementRecordListWithAuthors,
AchievementRecordListWithAuthorsVariables,
AchievementRecordListWithAuthors_AchievementRecord,
} from '../../../queries/__generated__/AchievementRecordListWithAuthors';
import { ACHIEVEMENT_RECORDS_WITH_AUTHORS } from '../../../queries/achievementRecord';
} from '../../queries/__generated__/AchievementRecordListWithAuthors';
import { ACHIEVEMENT_RECORDS_WITH_AUTHORS } from '../../queries/achievementRecord';
import { Link } from '@material-ui/core';
import { MinAchievementOption } from '../../../helpers/achievement';
import { MinAchievementOption } from '../../helpers/achievement';
import useTranslation from 'next-translate/useTranslation';
import { useAuthedMutation } from '../../../hooks/authedMutation';
import { useAuthedMutation } from '../../hooks/authedMutation';
import {
InsertAnAchievementRecord,
InsertAnAchievementRecordVariables,
} from '../../../queries/__generated__/InsertAnAchievementRecord';
} from '../../queries/__generated__/InsertAnAchievementRecord';
import Trans from 'next-translate/Trans';
interface IContext {
achievementRecordUploadDeadline: any;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import useTranslation from 'next-translate/useTranslation';
import { FC } from 'react';
import Trans from 'next-translate/Trans';

import { Course_Course_by_pk } from '../../../queries/__generated__/Course';
import { Button } from '../../common/Button';

interface IProps {
course: Course_Course_by_pk;
onClickApply: () => void;
}

export const ApplyButton: FC<IProps> = ({ course, onClickApply }) => {
const { t, lang } = useTranslation('course-page');
const now = new Date();
now.setHours(0, 0, 0, 0); // reset hours, minutes, seconds, and milliseconds

console.log(new Date());
console.log(course.applicationEnd);

// check if current date is after application deadline
const currentDate = new Date();
currentDate.setHours(0, 0, 0, 0);

if (course.applicationEnd <= currentDate) {
return (
<div className="bg-gray-300 p-4">
<Trans
i18nKey="course-application:status.applicationPeriodEnded"
components={{
a: (
<a
href="https://opencampus.substack.com"
target="_blank"
rel="noopener noreferrer"
className="underline"
/>
),
}}
/>
</div>
);
} else {
return (
<div className="flex flex-1 flex-col justify-center items-center">
<Button
filled
inverted
onClick={onClickApply}
disabled={now > course.applicationEnd}
className="bg-edu-course-current"
>
{t('applyNow')}
</Button>
<span className="text-xs mt-4 text-white">
{t('application_deadline')}
{course.applicationEnd?.toLocaleDateString(lang, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}) ?? ''}
</span>
</div>
);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import useTranslation from 'next-translate/useTranslation';
import { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';

import { CourseEnrollmentStatus_enum } from '../../../__generated__/globalTypes';
import { useUser } from '../../../hooks/user';
import { CourseWithEnrollment_Course_by_pk_CourseEnrollments } from '../../../queries/__generated__/CourseWithEnrollment';

import { ApplyButton } from './ApplyButton';
import { ApplicationModal } from './ApplicationModal';

import { Button } from '../../common/Button';

import { Course_Course_by_pk } from '../../../queries/__generated__/Course';
import { useIsLoggedIn } from '../../../hooks/authentication';
import { signIn } from 'next-auth/react';

interface CourseLinkInfosProps {
course: Course_Course_by_pk;
}

const CourseLinkInfos: FC<CourseLinkInfosProps> = ({ course }) => {
const { t } = useTranslation();

const onlineLocation = course.CourseLocations.find((location) => location.locationOption === 'ONLINE');

return (
<div className="flex justify-center items-center">
{onlineLocation && onlineLocation.defaultSessionAddress && (
<div className="mx-4">
{/* <a
href={onlineLocation.defaultSessionAddress}
target="_blank"
rel="noopener noreferrer"
className="text-white inline-block max-w-[100px]"
>
{t('course-page:to-online-meeting')}
</a> */}
<Button
as="a"
href={onlineLocation.defaultSessionAddress}
filled
inverted
// className="bg-edu-course-current"
>
{t('course:toOnlineMeeting')}
</Button>
</div>
)}{' '}
<div className="mx-4">
<Button
as="a"
href={course.chatLink}
filled
inverted
// className="bg-edu-course-current"
>
{t('course:toCourseChat')}
</Button>
</div>
</div>
);
};

interface ActionButtonsProps {
course: Course_Course_by_pk;
courseEnrollment: CourseWithEnrollment_Course_by_pk_CourseEnrollments;
setOnboardingModalOpen: Dispatch<SetStateAction<boolean>>;
}

export const ActionButtons: FC<ActionButtonsProps> = ({ course, courseEnrollment, setOnboardingModalOpen }) => {
const [isApplicationModalVisible, setApplicationModalVisible] = useState(false);
const { t } = useTranslation('course-application');
const isLoggedIn = useIsLoggedIn();

const user = useUser();

const showModal = useCallback(() => {
if (user) {
setApplicationModalVisible(true);
}
}, [user]);
const hideApplicationModal = useCallback(() => setApplicationModalVisible(false), []);

let content = null;

if (!courseEnrollment) {
content = <ApplyButton course={course} onClickApply={showModal} />;
} else {
const status = courseEnrollment.status;

switch (status) {
case CourseEnrollmentStatus_enum.ABORTED: {
content = <span className="bg-gray-300 p-4">{t('status.aborted')}</span>;
break;
}
case CourseEnrollmentStatus_enum.APPLIED: {
content = <span className="bg-gray-300 p-4">{t('status.applied')}</span>;
break;
}
case CourseEnrollmentStatus_enum.REJECTED: {
content = <span className="bg-gray-300 p-4">{t('status.rejected')}</span>;
break;
}
case CourseEnrollmentStatus_enum.CANCELLED: {
content = <span className="bg-gray-300 p-4">{t('status.cancelled')}</span>;
break;
}
case CourseEnrollmentStatus_enum.INVITED: {
if (courseEnrollment.invitationExpirationDate.setHours(0, 0, 0, 0) >= new Date().setHours(0, 0, 0, 0)) {
content = (
<div className="flex flex-col sm:flex-row sm:items-center">
<div className="bg-gray-300 p-4 mb-6 sm:mb-0 sm:w-2/3 sm:mr-5">{t('status.invited')}</div>
<Button
filled
inverted
onClick={() => setOnboardingModalOpen(true)}
className="bg-edu-course-current sm:w-1/3"
>
{t('acceptInvitation')}
</Button>
</div>
);
} else {
content = <span className="bg-gray-300 p-4">{t('status.invitation_expired')}</span>;
}
break;
}
case CourseEnrollmentStatus_enum.CONFIRMED: {
content = <CourseLinkInfos course={course} />;
break;
}
case CourseEnrollmentStatus_enum.COMPLETED: {
content = <CourseLinkInfos course={course} />;
break;
}
default: {
content = null;
}
}
}

const signInHandler = () => {
console.log('signIN!');
return signIn('keycloak');
};

const hasExternalRegistration = course.externalRegistrationLink !== null && course.externalRegistrationLink !== '';
const eventHandler = () => {
window.open(course.externalRegistrationLink, '_blank');
};

return (
<>
<div className="flex mx-auto mb-10">
{hasExternalRegistration ? (
<div className="mx-auto mb-10">
<ApplyButton course={course} onClickApply={eventHandler} />
</div>
) : isLoggedIn && !hasExternalRegistration ? (
content
) : (
<div className="mx-auto mb-10">
<ApplyButton course={course} onClickApply={signInHandler} />
</div>
)}
</div>
<ApplicationModal visible={isApplicationModalVisible} closeModal={hideApplicationModal} course={course} />
</>
);
};
Loading

0 comments on commit c1cde58

Please sign in to comment.