From bce6ca9b5a7acf35ae6060b47f4dab5161df819c Mon Sep 17 00:00:00 2001 From: Kyle Hua Date: Fri, 15 May 2020 01:06:12 -0700 Subject: [PATCH 1/2] can upload and view resume --- .gitignore | 2 +- .ruby-version | 2 +- .../professional_questionnaires_controller.rb | 110 ++++++++------- app/controllers/participants_controller.rb | 13 +- .../components/ParticipantShowPage/index.js | 17 ++- .../components/QuestionnaireForm/index.js | 128 +++++++++++++++--- .../components/QuestionnaireModal/index.js | 9 +- .../components/QuestionnaireView/index.js | 25 +++- app/models/professional_questionnaire.rb | 1 + app/views/participants/dashboard.html.erb | 1 + app/views/participants/show.html.erb | 3 +- 11 files changed, 229 insertions(+), 82 deletions(-) diff --git a/.gitignore b/.gitignore index 0dda205f..0bece6ec 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,4 @@ yarn-debug.log* /config/database.yml # Ignore vscode settings -.vscode/ \ No newline at end of file +.vscode/ diff --git a/.ruby-version b/.ruby-version index d64514e8..4560fb91 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-2.5.6 +ruby-2.6.3 diff --git a/app/controllers/api/professional_questionnaires_controller.rb b/app/controllers/api/professional_questionnaires_controller.rb index adca3e83..5e989cb7 100644 --- a/app/controllers/api/professional_questionnaires_controller.rb +++ b/app/controllers/api/professional_questionnaires_controller.rb @@ -1,60 +1,66 @@ class Api::ProfessionalQuestionnairesController < ApplicationController - before_action :set_questionnaire, only: [:update, :destroy] - respond_to :json - - def create - @questionnaire = ProfessionalQuestionnaire.new(questionnaire_params) - authorize @questionnaire, policy_class: QuestionnairePolicy - sentry_helper(@questionnaire) - if @questionnaire.save - render json: @questionnaire, status: :created - else - Raven.capture_message("Could not create professional questionnaire") - render json: { error: 'Could not create professional questionnaire' } - end - end - - def update - authorize @questionnaire, policy_class: QuestionnairePolicy - if @questionnaire.update(questionnaire_params) - render json: @questionnaire, status: :ok - else - Raven.capture_message("Could not update professional questionnaire") - render json: { error: 'Could not update professional questionnaire' }, status: :unprocessable_entity - end + before_action :set_questionnaire, only: [:update, :destroy] + respond_to :json + + def create + @questionnaire = ProfessionalQuestionnaire.new(questionnaire_params) + authorize @questionnaire, policy_class: QuestionnairePolicy + sentry_helper(@questionnaire) + if @questionnaire.save + render json: @questionnaire, status: :created + else + Raven.capture_message("Could not create professional questionnaire") + render json: { error: 'Could not create professional questionnaire' } end - - def destroy - authorize @questionnaire, policy_class: QuestionnairePolicy - if @questionnaire.destroy - render json: @questionnaire, status: :ok - else - Raven.capture_message("Failed to delete professional questionnaire") - render json: { error: 'Failed to delete professional questionnaire' }, status: :unprocessable_entity + end + + def update + authorize @questionnaire, policy_class: QuestionnairePolicy + if !questionnaire_params.nil? && questionnaire_params.fetch(:resume).present? && !(questionnaire_params.fetch(:resume).eql?("null")) + if @questionnaire.resume.attached? + @questionnaire.resume.purge end + @questionnaire.resume.attach(questionnaire_params.fetch(:resume)) end - - private - def set_questionnaire - @questionnaire = authorize ProfessionalQuestionnaire.find(params[:id]), policy_class: QuestionnairePolicy - rescue ActiveRecord::RecordNotFound => exception - Raven.extra_context(professional_questionnaire_id: params[:id]) - Raven.capture_exception(exception) - render json: { error: 'Could not find professional questionnaire' }, status: :not_found + + if @questionnaire.update!(questionnaire_params.except(:resume)) + render json: @questionnaire, status: :ok + else + Raven.capture_message("Could not update professional questionnaire") + render json: { error: 'Could not update professional questionnaire' }, status: :unprocessable_entity end + end - def sentry_helper(professional_questionnaire) - Raven.extra_context(professional_questionnaire: professional_questionnaire.attributes) - Raven.extra_context(participant: professional_questionnaire.participant.user.attributes) - end - - # may not work - def questionnaire_params - questionnaire_params = params.require(:professional_questionnaire).permit(:participant_id, :course_completion, - :work_history, :job_search_materials, :professional_goals, - :barriers, :education_history, :begin_skills_assessment_date, :end_skills_assessment_date, - :assigned_mentor, :success_strategies) + def destroy + authorize @questionnaire, policy_class: QuestionnairePolicy + if @questionnaire.destroy + render json: @questionnaire, status: :ok + else + Raven.capture_message("Failed to delete professional questionnaire") + render json: { error: 'Failed to delete professional questionnaire' }, status: :unprocessable_entity end - end - \ No newline at end of file + + private + def set_questionnaire + @questionnaire = authorize ProfessionalQuestionnaire.find(params[:id]), policy_class: QuestionnairePolicy + rescue ActiveRecord::RecordNotFound => exception + Raven.extra_context(professional_questionnaire_id: params[:id]) + Raven.capture_exception(exception) + render json: { error: 'Could not find professional questionnaire' }, status: :not_found + end + + def sentry_helper(professional_questionnaire) + Raven.extra_context(professional_questionnaire: professional_questionnaire.attributes) + Raven.extra_context(participant: professional_questionnaire.participant.user.attributes) + end + + # may not work + def questionnaire_params + questionnaire_params = params.require(:professional_questionnaire).permit(:participant_id, :course_completion, + :work_history, :job_search_materials, :professional_goals, + :barriers, :education_history, :begin_skills_assessment_date, :end_skills_assessment_date, + :assigned_mentor, :success_strategies, :resume) + end + +end \ No newline at end of file diff --git a/app/controllers/participants_controller.rb b/app/controllers/participants_controller.rb index 2757f44c..6081151c 100644 --- a/app/controllers/participants_controller.rb +++ b/app/controllers/participants_controller.rb @@ -22,6 +22,11 @@ def show end @professional_questionnaire = ProfessionalQuestionnairesSerializer.new(professional_q) + @resumeURL = nil + if (professional_q.resume.attached?) + @resumeURL = url_for(professional_q.resume) + end + @assignments = authorize @participant.assignments @assignment_list = [] @assignments.each do |a| @@ -54,9 +59,15 @@ def dashboard def set_participant @participant = authorize Participant.find(params[:id]) + puts @participant.professional_questionnaire + @resumeURL = "https://guides.rubyonrails.org/routing.html" + if @participant.professional_questionnaire.nil? && @participant.professional_questionnaire.resume.attached? + puts url_for(@participant.professional_questionnaire.resume) + @resumeURL = url_for(@participant.professional_questionnaire.resume) + end rescue ActiveRecord::RecordNotFound => exception Raven.extra_context(participant_id: params[:id]) Raven.capture_exception(exception) redirect_to participant_path end -end +end \ No newline at end of file diff --git a/app/javascript/components/ParticipantShowPage/index.js b/app/javascript/components/ParticipantShowPage/index.js index 4689763a..0ad3dc61 100644 --- a/app/javascript/components/ParticipantShowPage/index.js +++ b/app/javascript/components/ParticipantShowPage/index.js @@ -47,8 +47,21 @@ class ParticipantShowPage extends React.Component { professionalQuestionnaire, studioAssessments, assignmentList, + resumeURL, } = this.props; + console.log(userType); + console.log(paperworks); + console.log(participant); + console.log(status); + console.log(participantId); + console.log(personalQuestionnaire); + console.log(professionalQuestionnaire); + console.log(studioAssessments); + console.log(assignmentList); + console.log(resumeURL); + + return ( @@ -98,6 +112,7 @@ class ParticipantShowPage extends React.Component { questionnaireType="professional" participantId={participantId} questionnaire={professionalQuestionnaire} + resumeURL={resumeURL} /> @@ -156,4 +171,4 @@ ParticipantShowPage.propTypes = { assignmentList: PropTypes.array, }; -export default withStyles(styles)(ParticipantShowPage); +export default withStyles(styles)(ParticipantShowPage); \ No newline at end of file diff --git a/app/javascript/components/QuestionnaireForm/index.js b/app/javascript/components/QuestionnaireForm/index.js index 0d2238ac..3f9b6637 100644 --- a/app/javascript/components/QuestionnaireForm/index.js +++ b/app/javascript/components/QuestionnaireForm/index.js @@ -27,7 +27,9 @@ import styles from './styles'; class QuestionnaireForm extends React.Component { constructor(props) { super(props); - this.state = {}; + this.state = { + file: null, + }; this.handleSubmit = this.handleSubmit.bind(this); } @@ -46,28 +48,52 @@ class QuestionnaireForm extends React.Component { } handleSubmit() { - const qType = `${this.props.type}_questionnaire`; - const body = {}; + if (this.props.type === 'professional') { + const qType = `${this.props.type}_questionnaire`; + const formData = new FormData(); - Object.keys(this.state.questionnaire).forEach(f => { - body[f] = this.state.questionnaire[f]; - }); - body.participant_id = this.props.participantId; + Object.keys(this.state.questionnaire).forEach(f => { + formData.append(`${qType}[${f}]`, this.state.questionnaire[f]); + }); + formData.append(`${qType}[resume]`, this.state.file); + formData.append(`${qType}[participant_id]`, this.props.participantId); + const { id } = this.props.questionnaire; + const request = `/api/${qType}s/${id}`; + apiPut(request, formData) + .then(() => window.location.reload()) + .catch(error => { + Sentry.configureScope(function (scope) { + scope.setExtra('file', 'QuestionnaireForm'); + scope.setExtra('action', 'apiPut'); + scope.setExtra('QuestionnaireForm', JSON.stringify(formData)); + scope.setExtra('qType', qType); + }); + Sentry.captureException(error); + }); + } else { + const qType = `${this.props.type}_questionnaire`; + const body = {}; + + Object.keys(this.state.questionnaire).forEach(f => { + body[f] = this.state.questionnaire[f]; + }); + body.participant_id = this.props.participantId; - const { id } = this.props.questionnaire; - const request = `/api/${qType}s/${id}`; + const { id } = this.props.questionnaire; + const request = `/api/${qType}s/${id}`; - apiPut(request, { [qType]: body }) - .then(() => window.location.reload()) - .catch(error => { - Sentry.configureScope(function(scope) { - scope.setExtra('file', 'QuestionnaireForm'); - scope.setExtra('action', 'apiPut'); - scope.setExtra('QuestionnaireForm', body); - scope.setExtra('qType', qType); + apiPut(request, { [qType]: body }) + .then(() => window.location.reload()) + .catch(error => { + Sentry.configureScope(function (scope) { + scope.setExtra('file', 'QuestionnaireForm'); + scope.setExtra('action', 'apiPut'); + scope.setExtra('QuestionnaireForm', body); + scope.setExtra('qType', qType); + }); + Sentry.captureException(error); }); - Sentry.captureException(error); - }); + } } handleTextFormChange(e) { @@ -139,6 +165,23 @@ class QuestionnaireForm extends React.Component { createTextForm(fieldName, fieldValue, contentText) { // content text is prompt/title for the text box // field name is the name of the field that will be filled in the database + if (fieldValue === null || fieldValue === 'null') { + return ( +
+ {contentText} + this.handleTextFormChange(e)} + variant="outlined" + id={fieldName} + multiline + type="text" + margin="dense" + maxRows={20} + /> +
+ ); + } if (fieldName === 'DOC_status') { return (
@@ -357,10 +400,55 @@ class QuestionnaireForm extends React.Component { return this.createTextForm(f, questionnaire[f], sentenceCase); }); + if (this.props.type === 'professional') { + questionnaires.push(this.getFileUpload()); + } + if (this.props.type === 'personal') { + questionnaires.push(this.showUploadedFile); + } return
{questionnaires}
; } } + getFileUpload() { + return ( +
+ Upload Resume + {this.showUploadedFile()} + {this.showSelectedFile()} + { + this.setState({ file: event.target.files[0] }); + }} + /> +
+ ); + } + + showUploadedFile() { + if (this.props.resumeURL) { + return ( + + ); + } + return (
No Resume Uploaded
); + } + + showSelectedFile() { + const { file } = this.state; + if (file) { + const objectURL = window.URL.createObjectURL(file); + return ( + + ); + } + } + render() { return ( <> @@ -396,4 +484,4 @@ QuestionnaireForm.propTypes = { handleClose: PropTypes.func, }; -export default withStyles(styles)(QuestionnaireForm); +export default withStyles(styles)(QuestionnaireForm); \ No newline at end of file diff --git a/app/javascript/components/QuestionnaireModal/index.js b/app/javascript/components/QuestionnaireModal/index.js index b0758911..585e9818 100644 --- a/app/javascript/components/QuestionnaireModal/index.js +++ b/app/javascript/components/QuestionnaireModal/index.js @@ -13,11 +13,12 @@ function QuestionnaireModal({ participantId, questionnaireType, userType, + resumeURL, }) { const [open, setOpen] = useState(false); const qType = questionnaireType.charAt(0).toUpperCase() + questionnaireType.slice(1); - + console.log(resumeURL); let content; if (userType === 'staff') { content = ( @@ -26,6 +27,7 @@ function QuestionnaireModal({ participantId={participantId} questionnaire={questionnaire} handleClose={() => setOpen(false)} + resumeURL={resumeURL} /> ); } else if (userType === 'participant') { @@ -34,6 +36,7 @@ function QuestionnaireModal({ type={questionnaireType} questionnaire={questionnaire} handleClose={() => setOpen(false)} + resumeURL={resumeURL} /> ); } @@ -41,7 +44,7 @@ function QuestionnaireModal({ return ( <> { if ( title === 'participant' || @@ -44,12 +44,33 @@ function QuestionnaireView({ classes, questionnaire }) { ); }; + const showUploadedFile = () => { + console.log(resumeURL); + if (resumeURL) { + return ( + + Resume + + + ); + } + return ( + Resume + + ); + } + return ( {Object.entries(questionnaire).map(field => renderField(field[0], field[1]), )} + {showUploadedFile()} ); diff --git a/app/models/professional_questionnaire.rb b/app/models/professional_questionnaire.rb index d4dbd1b4..d601b554 100644 --- a/app/models/professional_questionnaire.rb +++ b/app/models/professional_questionnaire.rb @@ -1,3 +1,4 @@ class ProfessionalQuestionnaire < ApplicationRecord belongs_to :participant + has_one_attached :resume end diff --git a/app/views/participants/dashboard.html.erb b/app/views/participants/dashboard.html.erb index f0f08be2..214e7f72 100644 --- a/app/views/participants/dashboard.html.erb +++ b/app/views/participants/dashboard.html.erb @@ -12,4 +12,5 @@ professional_questionnaire: @professional_questionnaire, studio_assessments: @studio_assessments.all, assignment_list: @assignment_list, + resumeURL: @resumeURL, }) %> \ No newline at end of file diff --git a/app/views/participants/show.html.erb b/app/views/participants/show.html.erb index 68c6e3ef..96e12324 100644 --- a/app/views/participants/show.html.erb +++ b/app/views/participants/show.html.erb @@ -15,4 +15,5 @@ assignments: @assignments.all, studio_assessments: @studio_assessments.all, assignment_list: @assignment_list, -}) %> + resumeURL: @resumeURL, +}) %> \ No newline at end of file From e8f4263f5affb37ca1330e3002c4eec8408a55a6 Mon Sep 17 00:00:00 2001 From: Kyle Hua Date: Fri, 15 May 2020 01:13:12 -0700 Subject: [PATCH 2/2] fixed bug where resume wouldnt show on participant side --- app/controllers/pages_controller.rb | 8 ++++++-- app/controllers/participants_controller.rb | 6 ------ .../components/ParticipantShowPage/index.js | 11 ----------- app/javascript/components/QuestionnaireModal/index.js | 2 +- app/javascript/components/QuestionnaireView/index.js | 2 +- 5 files changed, 8 insertions(+), 21 deletions(-) diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 4d1b57b8..7033fe03 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -30,7 +30,7 @@ def dashboard @case_notes = policy_scope(CaseNote) @studio_assessments = policy_scope(StudioAssessment) @assignments = policy_scope(Assignment) - + @assignment_list = [] @assignments.each do |a| action_item = ActionItem.where(id: a.action_item_id).first @@ -64,7 +64,11 @@ def dashboard professional_q = authorize @participant.professional_questionnaire, policy_class: QuestionnairePolicy end @professional_questionnaire = ProfessionalQuestionnairesSerializer.new(professional_q) - + @resumeURL = nil + @resumeURL = nil + if (professional_q.resume.attached?) + @resumeURL = url_for(professional_q.resume) + end authorize Participant dashboard_participants_path diff --git a/app/controllers/participants_controller.rb b/app/controllers/participants_controller.rb index 6081151c..92bcf7a9 100644 --- a/app/controllers/participants_controller.rb +++ b/app/controllers/participants_controller.rb @@ -59,12 +59,6 @@ def dashboard def set_participant @participant = authorize Participant.find(params[:id]) - puts @participant.professional_questionnaire - @resumeURL = "https://guides.rubyonrails.org/routing.html" - if @participant.professional_questionnaire.nil? && @participant.professional_questionnaire.resume.attached? - puts url_for(@participant.professional_questionnaire.resume) - @resumeURL = url_for(@participant.professional_questionnaire.resume) - end rescue ActiveRecord::RecordNotFound => exception Raven.extra_context(participant_id: params[:id]) Raven.capture_exception(exception) diff --git a/app/javascript/components/ParticipantShowPage/index.js b/app/javascript/components/ParticipantShowPage/index.js index 0ad3dc61..bddbefaa 100644 --- a/app/javascript/components/ParticipantShowPage/index.js +++ b/app/javascript/components/ParticipantShowPage/index.js @@ -51,17 +51,6 @@ class ParticipantShowPage extends React.Component { } = this.props; console.log(userType); - console.log(paperworks); - console.log(participant); - console.log(status); - console.log(participantId); - console.log(personalQuestionnaire); - console.log(professionalQuestionnaire); - console.log(studioAssessments); - console.log(assignmentList); - console.log(resumeURL); - - return ( { - console.log(resumeURL); + if (resumeURL) { return (