diff --git a/apps/src/code-studio/pd/application_dashboard/application_dashboard.jsx b/apps/src/code-studio/pd/application_dashboard/application_dashboard.jsx index d6a432d173d8e..9184f93615058 100644 --- a/apps/src/code-studio/pd/application_dashboard/application_dashboard.jsx +++ b/apps/src/code-studio/pd/application_dashboard/application_dashboard.jsx @@ -118,6 +118,7 @@ export default class ApplicationDashboard extends React.Component { breadcrumbs={cohort_path_name} component={CohortView} applicationType={cohort_path_name} + viewType={paths[path].type} /> ) ]; diff --git a/apps/src/code-studio/pd/application_dashboard/cohort_view.jsx b/apps/src/code-studio/pd/application_dashboard/cohort_view.jsx index fca235ba0d37d..f0ea37c07ff9a 100644 --- a/apps/src/code-studio/pd/application_dashboard/cohort_view.jsx +++ b/apps/src/code-studio/pd/application_dashboard/cohort_view.jsx @@ -12,10 +12,15 @@ class CohortView extends React.Component{ regionalPartnerName: PropTypes.string.isRequired, route: PropTypes.shape({ path: PropTypes.string.isRequired, - applicationType: PropTypes.string.isRequired + applicationType: PropTypes.string.isRequired, + viewType: PropTypes.oneOf(['teacher', 'facilitator']).isRequired }) } + static contextTypes = { + router: PropTypes.object.isRequired + } + state = { loading: true, applications: null @@ -47,6 +52,8 @@ class CohortView extends React.Component{

{this.props.route.applicationType}

); diff --git a/apps/src/code-studio/pd/application_dashboard/cohort_view_table.jsx b/apps/src/code-studio/pd/application_dashboard/cohort_view_table.jsx index 7b4a94d2b515d..498de8c73291a 100644 --- a/apps/src/code-studio/pd/application_dashboard/cohort_view_table.jsx +++ b/apps/src/code-studio/pd/application_dashboard/cohort_view_table.jsx @@ -1,5 +1,6 @@ import React, {PropTypes} from 'react'; import {Table} from 'reactabular'; +import {Button} from 'react-bootstrap'; const styles = { table: { @@ -10,19 +11,20 @@ const styles = { export default class CohortViewTable extends React.Component { static propTypes = { data: PropTypes.array.isRequired, + path: PropTypes.string.isRequired, + viewType: PropTypes.oneOf(['facilitator', 'teacher']).isRequired + } + + static contextTypes = { + router: PropTypes.object } constructColumns() { - return [ + let columns = [ { - property: 'date_accepted', + property: 'accepted_at', header: { label: 'Date Accepted' - }, - cell: { - format: (date_accepted) => { - return new Date(date_accepted).toLocaleDateString('en-us', {month: 'long', day: 'numeric'}); - } } }, { property: 'applicant_name', @@ -45,14 +47,73 @@ export default class CohortViewTable extends React.Component { label: 'Email' } }, { - property: 'registered_for_summer_workshop', + property: 'notified', header: { - label: 'Registered Summer Workshop' + label: 'Notified' } } ]; + + if (this.props.viewType === 'facilitator') { + columns.push({ + property: 'assigned_fit', + header: { + label: 'Assigned FIT' + } + }, { + property: 'registered_fit', + header: { + label: 'Registered FIT' + } + } + ); + } else { + columns.push( + { + property: 'assigned_workshop', + header: { + label: 'Assigned Workshop' + } + }, { + property: 'registered_workshop', + header: { + label: 'Registered Workshop' + } + } + ); + } + + columns.push({ + property: 'id', + header: { + label: 'View Application' + }, + cell: { + format: this.formatViewButton + } + }); + + return columns; } + formatViewButton = (id) => { + return ( + + ); + }; + + handleViewClick = (id, event) => { + event.preventDefault(); + this.context.router.push(`/${this.props.path.replace('_cohort', '')}/${id}`); + }; + render() { return ( - + ); } diff --git a/apps/src/code-studio/pd/application_dashboard/cohort_view_table.story.jsx b/apps/src/code-studio/pd/application_dashboard/cohort_view_table.story.jsx index 7138e5e9f3886..b3a301d98feb6 100644 --- a/apps/src/code-studio/pd/application_dashboard/cohort_view_table.story.jsx +++ b/apps/src/code-studio/pd/application_dashboard/cohort_view_table.story.jsx @@ -8,27 +8,71 @@ export default storybook => { .addDecorator(reactBootstrapStoryDecorator) .addStoryTable([ { - name: 'Cohort view for application', + name: 'Cohort view for teacher application', story: () => ( + ) + }, { + name: 'Cohort view for facilitator application', + story: () => ( + ) } diff --git a/dashboard/app/controllers/api/v1/pd/applications_controller.rb b/dashboard/app/controllers/api/v1/pd/applications_controller.rb index 9dbf26da4fb50..d3eec0bd00d0b 100644 --- a/dashboard/app/controllers/api/v1/pd/applications_controller.rb +++ b/dashboard/app/controllers/api/v1/pd/applications_controller.rb @@ -61,7 +61,14 @@ def quick_view def cohort_view applications = get_applications_by_role(params[:role].to_sym).where(status: 'accepted').where.not(locked_at: nil) - render json: applications, each_serializer: Api::V1::Pd::ApplicationCohortViewSerializer + serializer = + if TYPES_BY_ROLE[params[:role].to_sym] == Pd::Application::Facilitator1819Application + Api::V1::Pd::FacilitatorApplicationCohortViewSerializer + elsif TYPES_BY_ROLE[params[:role].to_sym] == Pd::Application::Teacher1819Application + Api::V1::Pd::TeacherApplicationCohortViewSerializer + end + + render json: applications, each_serializer: serializer end # PATCH /api/v1/pd/applications/1 diff --git a/dashboard/app/models/pd/application/application_base.rb b/dashboard/app/models/pd/application/application_base.rb index f206e402d4711..0e0c235aa1c35 100644 --- a/dashboard/app/models/pd/application/application_base.rb +++ b/dashboard/app/models/pd/application/application_base.rb @@ -97,6 +97,7 @@ class ApplicationBase < ActiveRecord::Base before_create -> {self.status = :unreviewed} after_initialize :set_type_and_year before_validation :set_type_and_year + before_save :update_accepted_date, if: :status_changed? def set_type_and_year # Override in derived classes and set to valid values. @@ -105,6 +106,10 @@ def set_type_and_year self.application_type = nil end + def update_accepted_date + self.accepted_at = status == 'accepted' ? Time.now : nil + end + self.table_name = 'pd_applications' enum status: %w( diff --git a/dashboard/app/serializers/api/v1/pd/application_cohort_view_serializer.rb b/dashboard/app/serializers/api/v1/pd/application_cohort_view_serializer.rb deleted file mode 100644 index 5554916d68ae8..0000000000000 --- a/dashboard/app/serializers/api/v1/pd/application_cohort_view_serializer.rb +++ /dev/null @@ -1,18 +0,0 @@ -class Api::V1::Pd::ApplicationCohortViewSerializer < ActiveModel::Serializer - attributes :date_accepted, :applicant_name, :district_name, :school_name, :email, - :registered_for_summer_workshop - - def email - object.user.email - end - - def date_accepted - # TODO: mehal - Implement this - 'Not implemented yet' - end - - def registered_for_summer_workshop - # TODO: mehal - Implement this - 'Not implemented yet' - end -end diff --git a/dashboard/app/serializers/api/v1/pd/facilitator_application_cohort_view_serializer.rb b/dashboard/app/serializers/api/v1/pd/facilitator_application_cohort_view_serializer.rb new file mode 100644 index 0000000000000..2608102f4fb18 --- /dev/null +++ b/dashboard/app/serializers/api/v1/pd/facilitator_application_cohort_view_serializer.rb @@ -0,0 +1,27 @@ +class Api::V1::Pd::FacilitatorApplicationCohortViewSerializer < ActiveModel::Serializer + attributes :id, :date_accepted, :applicant_name, :district_name, :school_name, :email, + :notified, :assigned_fit, :registered_fit + + def date_accepted + object.accepted_at.try(:strftime, '%b %e') + end + + def email + object.user.email + end + + def notified + # TODO: (mehal) implement this + 'Not implemented' + end + + def assigned_fit + # TODO: (mehal) implement this + 'Not implemented' + end + + def registered_fit + # TODO: (mehal) implement this + 'Not implemented' + end +end diff --git a/dashboard/app/serializers/api/v1/pd/teacher_application_cohort_view_serializer.rb b/dashboard/app/serializers/api/v1/pd/teacher_application_cohort_view_serializer.rb new file mode 100644 index 0000000000000..a57d0f736f10b --- /dev/null +++ b/dashboard/app/serializers/api/v1/pd/teacher_application_cohort_view_serializer.rb @@ -0,0 +1,31 @@ +class Api::V1::Pd::TeacherApplicationCohortViewSerializer < ActiveModel::Serializer + attributes :id, :date_accepted, :applicant_name, :district_name, :school_name, :email, + :notified, :assigned_workshop, :registered_workshop, :accepted_teachercon + + def date_accepted + object.accepted_at.try(:strftime, '%b %e') + end + + def email + object.user.email + end + + def notified + # TODO: (mehal) implement this + 'Not implemented' + end + + def assigned_workshop + # TODO: (mehal) implement this + 'Not implemented' + end + + def registered_workshop + # TODO: (mehal) implement this + 'Not implemented' + end + + def accepted_teachercon + 'Not implemented' + end +end diff --git a/dashboard/test/controllers/api/v1/pd/applications_controller_test.rb b/dashboard/test/controllers/api/v1/pd/applications_controller_test.rb index 4ff3695aea183..111b46cad9657 100644 --- a/dashboard/test/controllers/api/v1/pd/applications_controller_test.rb +++ b/dashboard/test/controllers/api/v1/pd/applications_controller_test.rb @@ -34,6 +34,17 @@ class ApplicationsControllerTest < ::ActionController::TestCase @csd_teacher_application = create :pd_teacher1819_application, course: 'csd' @csp_teacher_application = create :pd_teacher1819_application, course: 'csp' @csp_facilitator_application = create :pd_facilitator1819_application, course: 'csp' + + @serializing_teacher = create(:teacher, + email: 'minerva@hogwarts.edu', + school_info: create( + :school_info, + school: create( + :school, + name: 'Hogwarts' + ) + ) + ) end test_redirect_to_sign_in_for :index @@ -238,49 +249,77 @@ class ApplicationsControllerTest < ::ActionController::TestCase ).values.any? {|x| response_csv.first.exclude?(x + "\n")} end - test 'cohort view returns expected columns' do - application = create( - :pd_teacher1819_application, - course: 'csp', - regional_partner: @regional_partner, - user: ( - create( - :teacher, + test 'cohort view returns expected columns for a teacher' do + time = Date.new(2017, 3, 15) + + Timecop.freeze(time) do + application = create( + :pd_teacher1819_application, + course: 'csp', + regional_partner: @regional_partner, + user: @serializing_teacher + ) + + application.update_form_data_hash({first_name: 'Minerva', last_name: 'McGonagall'}) + application.save + application.update(status: 'accepted') + application.lock! + + sign_in @workshop_organizer + get :cohort_view, params: {role: 'csp_teachers'} + assert :success + + assert_equal( + { + id: application.id, + date_accepted: 'Mar 15', + applicant_name: 'Minerva McGonagall', + district_name: 'A School District', + school_name: 'Hogwarts', email: 'minerva@hogwarts.edu', - school_info: ( - create( - :school_info, - school: ( - create( - :school, - name: 'Hogwarts' - ) - ) - ) - ) - ) + notified: 'Not implemented', + assigned_workshop: 'Not implemented', + registered_workshop: 'Not implemented', + accepted_teachercon: 'Not implemented', + }.stringify_keys, JSON.parse(@response.body).first ) - ) + end + end - application.update_form_data_hash({first_name: 'Minerva', last_name: 'McGonagall'}) - application.save - application.update(status: 'accepted') - application.lock! + test 'cohort view returns expected columns for a facilitator' do + time = Date.new(2017, 3, 15) - sign_in @workshop_organizer - get :cohort_view, params: {role: 'csp_teachers'} - assert :success - - assert_equal( - { - date_accepted: 'Not implemented yet', - applicant_name: 'Minerva McGonagall', - district_name: 'A School District', - school_name: 'Hogwarts', - email: 'minerva@hogwarts.edu', - registered_for_summer_workshop: 'Not implemented yet' - }.stringify_keys, JSON.parse(@response.body).first - ) + Timecop.freeze(time) do + application = create( + :pd_facilitator1819_application, + course: 'csp', + regional_partner: @regional_partner, + user: @serializing_teacher + ) + + application.update_form_data_hash({first_name: 'Minerva', last_name: 'McGonagall'}) + application.save + application.update(status: 'accepted') + application.lock! + + sign_in @workshop_organizer + get :cohort_view, params: {role: 'csp_facilitators'} + assert :success + + assert_equal( + { + id: application.id, + date_accepted: 'Mar 15', + applicant_name: 'Minerva McGonagall', + district_name: 'A School District', + school_name: 'Hogwarts', + email: 'minerva@hogwarts.edu', + notified: 'Not implemented', + assigned_fit: 'Not implemented', + registered_fit: 'Not implemented' + }.stringify_keys, JSON.parse(@response.body).first + ) + end end end end diff --git a/dashboard/test/models/pd/application/teacher1819_application_test.rb b/dashboard/test/models/pd/application/teacher1819_application_test.rb index 78f9e4bc4aa15..e3719128f1130 100644 --- a/dashboard/test/models/pd/application/teacher1819_application_test.rb +++ b/dashboard/test/models/pd/application/teacher1819_application_test.rb @@ -6,6 +6,8 @@ class Teacher1819ApplicationTest < ActiveSupport::TestCase include Teacher1819ApplicationConstants include ApplicationConstants + freeze_time + test 'application guid is generated on create' do teacher_application = build :pd_teacher1819_application assert_nil teacher_application.application_guid @@ -349,5 +351,28 @@ class Teacher1819ApplicationTest < ActiveSupport::TestCase application.send_decision_notification_email end end + + test 'accepted_at updates times' do + today = Date.today.to_time + tomorrow = Date.tomorrow.to_time + application = create :pd_teacher1819_application + assert_nil application.accepted_at + + Timecop.freeze(today) do + application.update(status: 'accepted') + application.reload + assert_equal today, application.accepted_at.to_time + + application.update(status: 'declined') + application.reload + assert_nil application.accepted_at + end + + Timecop.freeze(tomorrow) do + application.update(status: 'accepted') + application.reload + assert_equal tomorrow, application.accepted_at.to_time + end + end end end