Volto Refactoring

Good afternoon, Plone community!

I have a question regarding refacti\oring items in Volto. I have been refactoring my code in Volto using the example for TalkList in the Plone 6 training. However, the view doesn't display in a neat fashion. I am currently using a custom version of this code for my project.

A screenshot of the page has been included in this posting as a reference:

Also, here is the code for the view I am currently using which renders this example:

const ApplicationForm = ({ content }) => {
  const results = useSelector(
    (state) => state.search.subrequests.applicationform?.items,
  );
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(
      searchContent(
        '/',
        {
          // portal_type: ['applicationform'],
          fullobjects: true,
        },
        'applicationform',
      ),
    );
  }, [dispatch]);

My question is how do I go about making the display neater without using bbb_getContentFetchesFullobjects in the config.js file? The reason I am trying to refactor this code is to prevent performance issues in rendering content in Volto.

I thank you kindly in advance for your help.

Sincerely,

rbrown12

I don't really understand the intention of the code, so perhaps you can add some more details. For example, I see the component name as "ApplicationForm", so I presume it's a view for ApplicationForm content types? But then you're searching for applicationform inside that view, so probably you couldn't get it to work for the real applicationforms?

@tiberiuichim,
Thank you for your reply. The rest of the code is below:

import moment from 'moment';
import { Container, Modal, Table } from 'semantic-ui-react';
import React, { useEffect } from 'react';
import PDFViews from '../../PDFViews/PDFViews';
import ContentStatusHistory from '../ListingViews/ContentStatusHistory';
import { Link } from 'react-router-dom';
import Voting from '../../../Voting/Voting';
import { useDispatch, useSelector } from 'react-redux';
import { searchContent } from '../../../../../../../../omelette/src/actions/search/search';

const SchoolWidget = ({ school }) => {
  return (
    __CLIENT__ && (
      <div>
        <Table celled className="igibschool_list">
          <Table.Header>
            <Table.Row>
              <Table.HeaderCell>School</Table.HeaderCell>
              <Table.HeaderCell>Location</Table.HeaderCell>
              <Table.HeaderCell>Year of Graduation</Table.HeaderCell>
              <Table.HeaderCell>GPA</Table.HeaderCell>
              <Table.HeaderCell>Major</Table.HeaderCell>
            </Table.Row>
          </Table.Header>
          <Table.Body>
            {school?.items?.map((item) => (
              <Table.Row key={item.school}>
                <Table.Cell>{item.school}</Table.Cell>
                <Table.Cell>{item.location}</Table.Cell>
                <Table.Cell>{item.year}</Table.Cell>
                <Table.Cell>{item.gpa}</Table.Cell>
                <Table.Cell>{item.major}</Table.Cell>
              </Table.Row>
            ))}
          </Table.Body>
        </Table>
      </div>
    )
  );
};

const EmployerWidget = ({ employer }) => {
  return (
    __CLIENT__ && (
      <div>
        <Table celled className="igibemployer_list">
          <Table.Header>
            <Table.Row>
              <Table.HeaderCell>Employer Name</Table.HeaderCell>
              <Table.HeaderCell>Employer Phone</Table.HeaderCell>
              <Table.HeaderCell>Street</Table.HeaderCell>
              <Table.HeaderCell>City</Table.HeaderCell>
              <Table.HeaderCell>State</Table.HeaderCell>
              <Table.HeaderCell>Postal Code</Table.HeaderCell>
            </Table.Row>
          </Table.Header>
          <Table.Body>
            {employer?.items?.map((item) => (
              <Table.Row key={item.employer}>
                <Table.Cell>{item.employer}</Table.Cell>
                <Table.Cell>{item.employerPhone}</Table.Cell>
                <Table.Cell>{item.street}</Table.Cell>
                <Table.Cell>{item.city}</Table.Cell>
                <Table.Cell>{item.state}</Table.Cell>
                <Table.Cell>{item.zip}</Table.Cell>
              </Table.Row>
            ))}
          </Table.Body>
        </Table>
      </div>
    )
  );
};

const ReferencesWidget = ({ references }) => {
  return (
    __CLIENT__ && (
      <div>
        <Table celled className="igibreferences_list">
          <Table.Header>
            <Table.Row>
              <Table.HeaderCell>
                Name <em>Recommender</em>
              </Table.HeaderCell>
              <Table.HeaderCell>Email</Table.HeaderCell>
              <Table.HeaderCell>
                Institution/Organization/Company
              </Table.HeaderCell>
              <Table.HeaderCell>Address</Table.HeaderCell>
              <Table.HeaderCell>Telephone Number</Table.HeaderCell>
              <Table.HeaderCell>Relationship to You</Table.HeaderCell>
            </Table.Row>
          </Table.Header>
          <Table.Body>
            {references?.items?.map((item) => (
              <Table.Row key={item.name}>
                <Table.Cell>{item.name}</Table.Cell>
                <Table.Cell>{item.email}</Table.Cell>
                <Table.Cell>{item.ioc}</Table.Cell>
                <Table.Cell>{item.address}</Table.Cell>
                <Table.Cell>{item.phoneNumber}</Table.Cell>
                <Table.Cell>{item.relationship}</Table.Cell>
              </Table.Row>
            ))}
          </Table.Body>
        </Table>
      </div>
    )
  );
};

const ApplicationForm = ({ content }) => {
  const results = useSelector(
    (state) => state.search.subrequests.applicationform?.items,
  );
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(
      searchContent(
        '/',
        {
          // portal_type: ['applicationform'],
          fullobjects: true,
        },
        'applicationform',
      ),
    );
  }, [dispatch]);
  return (
    <Container className="view-wrapper">
      <h1 className="documentFirstHeading">
        {content.last_name}, {content.first_name}{' '}
        {content.employeeID && -content.employeeID}
      </h1>
      <span className="contentStatusHistory">
        <Modal closeIcon trigger={<Link>Change State</Link>} scrolling>
          <Modal.Content>
            <div>
              <ContentStatusHistory {...content} />
            </div>
          </Modal.Content>
          <br />
        </Modal>
      </span>
      {results && <h2>Current Recommendations</h2>}
      {results && (
        <Table celled striped sortable fixed>
          <Table.Header>
            <Table.Row>
              <Table.HeaderCell>Recommender</Table.HeaderCell>
              <Table.HeaderCell>Recommend Applicant?</Table.HeaderCell>
            </Table.Row>
          </Table.Header>
          <Table.Body>
            {results &&
              results.map((item) => (
                <Table.Row key={item.id}>
                  <Table.Cell>
                    <Modal
                      trigger={<Link>{item.refereeName}</Link>}
                      scrolling
                      closeIcon
                    >
                      <Modal.Content>
                        <h1 className="documentFirstHeading">
                          {item.refereeName}
                        </h1>
                        <Voting />
                        {item.candidateName && (
                          <div>
                            <h3>
                              <strong>Applicant Name</strong>
                            </h3>
                            <h4>{item.candidateName}</h4>
                          </div>
                        )}
                        <br />
                        {item.refereeName && (
                          <div>
                            <h3>
                              <strong>Referee Name</strong>
                            </h3>
                            <h4>{item.refereeName}</h4>
                          </div>
                        )}
                        <br />
                        {item.email && (
                          <div>
                            <h3>
                              <strong>Email</strong>
                            </h3>
                            <h4>{item.email}</h4>
                          </div>
                        )}
                        <br />
                        {item.phone && (
                          <div>
                            <h3>
                              <strong>Telephone</strong>
                            </h3>
                            <h4>{item.phone}</h4>
                          </div>
                        )}
                        <br />
                        {item.address && (
                          <div>
                            <h3>
                              <strong>Mailing Address</strong>
                            </h3>
                            <h4>{item.address}</h4>
                          </div>
                        )}
                        <br />
                        {item.capacity && (
                          <div>
                            <h3>
                              <strong>Knowledge of Applicant</strong>
                            </h3>
                            <h3>
                              <strong>
                                What capacity have you known the Applicant?
                              </strong>
                            </h3>
                            <h4>{item.capacity}</h4>
                          </div>
                        )}
                        <br />
                        {item.howLong && (
                          <div>
                            <h3>
                              <strong>
                                How long have you known the Applicant?
                              </strong>
                            </h3>
                            <p>
                              <em>(years/months)</em>
                            </p>
                            <h4>{item.howLong}</h4>
                          </div>
                        )}
                        <br />
                        {item.knowledge && (
                          <div>
                            <h3>
                              <strong>Other</strong>
                            </h3>
                            <h4>{item.knowledge}</h4>
                          </div>
                        )}
                        <br />
                        {item.adapt && (
                          <div>
                            <h3>
                              <strong>Ability to adapt and change</strong>
                            </h3>
                            <h4>{item.adapt.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.awareness && (
                          <div>
                            <h3>
                              <strong>Awareness of self and others</strong>
                            </h3>
                            <h4>{item.awareness.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.maturity && (
                          <div>
                            <h3>
                              <strong>Maturity</strong>
                            </h3>
                            <h4>{item.maturity.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.openness && (
                          <div>
                            <h3>
                              <strong>
                                Openness to feedback and constructive criticism{' '}
                              </strong>
                            </h3>
                            <h4>{item.openness.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.interpersonal && (
                          <div>
                            <h3>
                              <strong>
                                Interpersonal skills (with superiors/executives){' '}
                              </strong>
                            </h3>
                            <h4>{item.interpersonal.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.confidence && (
                          <div>
                            <h3>
                              <strong>Confidence</strong>
                            </h3>
                            <h4>{item.confidence.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.initiative && (
                          <div>
                            <h3>
                              <strong>Initiative/Self-Motivation</strong>
                            </h3>
                            <h4>{item.initiative.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.collaboration && (
                          <div>
                            <h3>
                              <strong>Collaboration/Teamwork</strong>
                            </h3>
                            <h4>{item.collaboration.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.thinking && (
                          <div>
                            <h3>
                              <strong>Critical Thinking Skills </strong>
                            </h3>
                            <h4>{item.thinking.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.curiosity && (
                          <div>
                            <h3>
                              <strong>Intellectual Curiosity</strong>
                            </h3>
                            <h4>{item.curiosity.token.toString()}</h4>
                          </div>
                        )}
                        <br />
                        {item.problemSolvingSkills && (
                          <div>
                            <h3>
                              <strong>Problem Solving Skills</strong>
                            </h3>
                            <h4>
                              {item.problemSolvingSkills.token.toString()}
                            </h4>
                          </div>
                        )}
                        <br />
                        {item.comments && (
                          <div>
                            <h3>
                              <strong>Additional comments</strong>
                            </h3>
                            <h4>{item.comments}</h4>
                          </div>
                        )}
                        <br />
                        {item.letter_recommendation && (
                          <div>
                            <h3>
                              <strong>Formal letter of recommendation</strong>
                            </h3>
                            <PDFViews
                              file={item.letter_recommendation.download}
                            />
                          </div>
                        )}
                        <br />
                        {item.acceptanceAR && (
                          <div>
                            <h3>
                              <strong>For Academic Referees Only</strong>
                            </h3>
                            <h4>{item.acceptanceAR.token}</h4>
                          </div>
                        )}
                        <br />
                        {item.acceptanceNR && (
                          <div>
                            <h3>
                              <strong>For Non-Academic Referees Only</strong>
                            </h3>
                            <h4>{item.acceptanceNR.token}</h4>
                          </div>
                        )}
                        <br />
                        <h3>
                          <em>
                            <span> last modified </span>
                            <span> {moment(item.modified).fromNow('ll')}</span>
                            <span> ago </span>{' '}
                          </em>
                        </h3>
                      </Modal.Content>
                    </Modal>
                  </Table.Cell>
                  <Table.Cell>
                    {(item.acceptanceAR && item.acceptanceAR.token) ||
                      (item.acceptanceNR && item.acceptanceNR.token)}
                  </Table.Cell>
                </Table.Row>
              ))}
          </Table.Body>
        </Table>
      )}
      <h3>
        <strong>Reviewer's Comments</strong>
      </h3>
      {content.comments && <h4>{content.comments}</h4>}
      {content.semester && (
        <h3>
          <strong>Semester</strong>
        </h3>
      )}
      <h4>{content.semester && content.semester.token.toString()}</h4>
      {content.program && (
        <h3>
          <strong>Program</strong>
        </h3>
      )}
      <h4>{content.program && content.program.token.toString()}</h4>
      {content.first_name && (
        <h3>
          <strong>First Name</strong>
        </h3>
      )}
      <h4>{content.first_name && content.first_name}</h4>
      {content.last_name && (
        <h3>
          <strong>Last Name</strong>
        </h3>
      )}
      <h4>{content.last_name && content.last_name}</h4>
      {content.street2 && (
        <h3>
          <strong>Street</strong>
        </h3>
      )}
      <h4>{content.street2 && content.street2}</h4>
      {content.city2 && (
        <h3>
          <strong>City</strong>
        </h3>
      )}
      <h4>{content.city2 && content.city2}</h4>
      {content.state2 && (
        <h3>
          <strong>State</strong>
        </h3>
      )}
      <h4>{content.state2 && content.state2}</h4>
      {content.zip2 && (
        <h3>
          <strong>Zip</strong>
        </h3>
      )}
      <h4>{content.zip2 && content.zip2}</h4>
      {content.phoneNumber && (
        <h3>
          <strong>Phone Number</strong>
        </h3>
      )}
      <h4>{content.phoneNumber && content.phoneNumber}</h4>
      {content.appl_email && (
        <h3>
          <strong>Email Address</strong>
        </h3>
      )}
      <h4>{content.appl_email && content.appl_email}</h4>
      {content.birthDate && (
        <h3>
          <strong>Date of Birth</strong>
        </h3>
      )}
      <h4>{content.birthDate && moment(content.birthDate).format('ll')}</h4>
      {content.gender && (
        <h3>
          <strong>Gender Identity</strong>
        </h3>
      )}
      <h4>{content.gender && content.gender.token.toString()}</h4>
      {content.veteran && (
        <h3>
          <strong>Are you a Veteran?</strong>
        </h3>
      )}
      <h4>{content.veteran && content.veteran.token.toString()}</h4>
      {content.f1student && (
        <h3>
          <strong>Are you a F1 student?</strong>
        </h3>
      )}
      <h4>{content.f1student && content.f1student.token.toString()}</h4>
      {content.citizen && (
        <h3>
          <strong>Are you a U.S. Citizen?</strong>
        </h3>
      )}
      <h4>{content.citizen && content.citizen.token.toString()}</h4>
      {content.birthCountry && (
        <h3>
          <strong>Country of Birth</strong>
        </h3>
      )}
      <h4>{content.birthCountry && content.birthCountry.token.toString()}</h4>
      <h3>
        <strong>Country of Citizenship</strong>
      </h3>
      {content.citizenshipCountry && (
        <h4>{content.citizenshipCountry.token.toString()}</h4>
      )}
      {content.permanent && (
        <div>
          <h2>Additional Citizenship Information:</h2>
          <h2>U.S. Permanent Resident:</h2>
          <h3>
            <strong>Alien (A) Number</strong>
          </h3>
        </div>
      )}
      <h4>{content.permanent && content.permanent}</h4>
      {content.permanent2 && (
        <h3>
          <strong>Date Obtained MM/YY</strong>
        </h3>
      )}
      <h4>{content.permanent2 && moment(content.permanent2).format('ll')}</h4>
      {content.temporary && <h2>Temporary Visa:</h2>}
      {content.temporary && (
        <h3>
          <strong>Type of Visa</strong>
        </h3>
      )}
      <h4>{content.temporary && content.temporary}</h4>
      {content.temporary2 && (
        <h3>
          <strong>Date Obtained MM/YY</strong>
        </h3>
      )}
      <h4>{content.temporary2 && moment(content.temporary2).format('ll')}</h4>
      {content.temporary3 && (
        <h3>
          <strong>Date Expired MM/YY</strong>
        </h3>
      )}
      <h4>{content.temporary3 && moment(content.temporary3).format('ll')}</h4>
      {content.other && (
        <h3>
          <strong>Other: Explain</strong>
        </h3>
      )}
      <h4>{content.other && content.other}</h4>
      <h3>
        <strong>Current Resume</strong>
      </h3>
      {content.resume && <PDFViews file={content.resume.download} />}
      {content.statement && (
        <div>
          <h3>
            <strong>Personal Statement</strong>
          </h3>
          <span></span>
        </div>
      )}
      {content.statement && <PDFViews file={content.statement.download} />}
      {content.toefl && (
        <div>
          <h3>
            <strong>TOEFL or IELTS score</strong>
          </h3>
        </div>
      )}
      {content.toefl && <h4>{content.toefl}</h4>}
      {content.score && (
        <div>
          <h3>
            <strong>Unofficial copy of TOEFL or IELTS score</strong>
          </h3>
        </div>
      )}
      {content.score && <PDFViews file={content.score.download} />}
      {content.date && (
        <h3>
          <strong>Date TOEFL/IELTS taken:</strong>
        </h3>
      )}
      {content.date && <h4>{moment(content.date).format('ll')}</h4>}
      {content.native && (
        <h3>
          <strong>Native Language</strong>
        </h3>
      )}
      {content.native && <h4>{content.native}</h4>}
      <h3>School Information</h3>
      <SchoolWidget school={content.school} />
      {content.occupation && (
        <h3>
          <strong>Present Occupation</strong>
        </h3>
      )}
      {content.occupation && <h4>{content.occupation}</h4>}
      <h3>Employer Information</h3>
      <EmployerWidget employer={content.employerAddress} />
      <h3>References Information</h3>
      <ReferencesWidget references={content.references} />
      {content.rconsent && (
        <h3>
          <strong>Letters of Recommendation</strong>
        </h3>
      )}
      {content.rconsent && <h4>{content.rconsent.token.toString()}</h4>}
      <br />
      <p>
        {content.statementConsent && content.statementConsent.token.toString()}
      </p>
      <br />
      {content.transcript && (
        <div>
          <h3>
            <strong>Official Transcript</strong>
          </h3>
          <p>
            <em>Internal Use</em>
          </p>
        </div>
      )}
      <br />
      {content.transcript && <PDFViews file={content.transcript.download} />}
      <br />
      {content.transcript1 && (
        <div>
          <h3>
            <strong>Official Transcript</strong>
          </h3>
          <p>
            <span>
              <em>Internal Use</em>
            </span>
          </p>
        </div>
      )}
      <br />
      {content.transcript1 && <PDFViews file={content.transcript1.download} />}
      <br />
      {content.transcript2 && (
        <div>
          <h3>
            <strong>Official Transcript</strong>
          </h3>
          <p>
            <span>
              <em>Internal Use</em>
            </span>
          </p>
        </div>
      )}
      <br />
      {content.transcript2 && <PDFViews file={content.transcript2.download} />}
      <br />
      {content.transcript3 && (
        <div>
          <h3>
            <strong>Official Transcript</strong>
          </h3>
          <p>
            <span>
              <em>Internal Use</em>
            </span>
          </p>
        </div>
      )}
      <br />
      {content.transcript3 && <PDFViews file={content.transcript3.download} />}
      <br />
      {content.grade_equivalent && (
        <div>
          <h3>
            <strong>Grade Equivalent</strong>
          </h3>
          <p>
            <span>
              <em>Internal Use</em>
            </span>
          </p>
        </div>
      )}
      <br />
      {content.grade_equivalent && (
        <PDFViews file={content.grade_equivalent.download} />
      )}
      <br />
      {content.english_translation && (
        <div>
          <h3>
            <strong>English Translation</strong>
          </h3>
          <p>
            <span>
              <em>Internal Use</em>
            </span>
          </p>
        </div>
      )}
      <br />
      {content.english_translation && (
        <PDFViews file={content.english_translation.download} />
      )}
      <br />
      <h3>
        <em>
          <span> last modified </span>
          <span> {moment(content.modified).fromNow('ll')}</span>
          <span> ago </span>{' '}
        </em>
      </h3>
    </Container>
  );
};

export default ApplicationForm;

Yes, the view is for the ApplicationForm content types. It was working before when using bbb_getContentFetchesFullobjects in the config.js file and I would like to have it work without using this.

I thank you kindly.

Sincerely,

rbrown12

As far as I understand, your issue is the fact that you need to do an extra @search endpoint call, instead of relying on the items list brought by the regular getContent call.

I don't think that's really an issue. It forces you to be aware of the batching problem: assuming you set the bbb_getContentFetchesFullobjects to true, your users will see only a partial set of results, which is dangerous for the application.

Now, some extra tips based on your code:

  • you can rewrite import { searchContent } from '../../../../../../../../omelette/src/actions/search/search'; as import { searchContent } from '@plone/volto/actions'.
  • you can rewrite import Voting from '../../../Voting/Voting'; as import Voting from '@package/Voting/Voting'; or import Voting from '~/Voting/Voting' (note, your path might be different, the important point is that the <root>/src of your project is aliased as @package and ~.
  • the use of __CLIENT__ is a smell.

Otherwise, looks good. Nice to see you advancing on your Volto app!

Plone Foundation Code of Conduct