Nate's Work
OverView
Basic Functionality
- Homepage includes a list of classrooms, each classroom name is a link to the classroom show page
- Each classroom show page includes a list of students, a form to add a student, a calendar that lets you select a date, a form to submit attendance and a form to create a new note
- Each note or attendance displays immediately upon submission of the form and is persisted in the database
- example of a classroom page in the screenshot below
Features to add
- User authorization
- Student show page for total notes and total attendance
- Limit one daily attendance per day
- Either removal or limit to most recent note/attendance displayed on main classroom page
- Attendance reporting page for every student - basically I need to break up the UI into different pages rather than cluttering one page
- Lots of css and page layout
File Structure
Rails
- database migrations to create tables with activerecord and the schema and seedfile in the db directory
- associated models in app/models
- controllers associated with each route exposing models and specific requests in app/controllers
- rails serializers utilized to properly display and nest data in app/serializers
- unit tests for database models in spec/models
- still need to write controller tests
React
- everything in client directory
- each address is wrapped in react router in App.js to render different components based on the url (there's only 2 pages at the moment but they should be broken up into more)
- folders I worked in mostly were client/containers and client/components
- I put all my stateful components in client/containers and all my stateless display components in client/components
- Example of component nesting: Classroom link is nested in HomePageContainer which maintains the state of classroom information and dynamically displays a link to each page by mapping through an array of classrooms and returning classroom links
- Example of really awkward component nesting: ClassroomContainer has become a bit of a super-container with 3 other containers: createStudentContainer, createNoteContainer and createAttendanceContainer all displaying inside of it, however they are all actually nested in each classroomtile component - which the dynamic props have to be passed through from ClassroomContainer so that the students state stays properly updated and re-renders on each submission.
- To remedy this I could use redux and have a global state store instead of having to pass everything down through multiple components - I could also have other parent-containers that fetch up the data and render at different url's to break everything up a bit - or ideally, both.
- Need to write some front-end tests
Deploying and Troubleshooting
Deploying
- Decided to first deploy my app to heroku
- To do so, there were 2 options #1: I could run npm build locally, which would bundle all my react code and dependencies into a static folder, then manually move that folder into the public folder that gets served by rails. #2: I could automate that process on heroku so all I have to do is git push heroku master, rather than manually building my program every time I want to push it up. I chose option 2.
- To do this I needed to include a package.json file in the root directory that looked something like this:
{
"name": "student-data",
"engines": {
"node": "6.3.1"
},
"scripts": {
"build": "cd client && npm install && npm run build && cd ..",
"deploy": "cp -a client/build/. public/",
"postinstall": "npm run build && npm run deploy && echo 'Client built!'"
}
}
- I then had to create an app on heroku and run these commands to set up the heroku buildpacks - essentially nodejs will run first, it will find the package.json in the root directory and follow the script to build the client file according to the package.json there, then copy it into the rails public dir:
>>> heroku buildpacks:add heroku/nodejs --index 1
heroku buildpacks:add heroku/ruby --index 2
- I then had to create a procfile in the root direction to tell heroku to run my server, then push everything up, migrate and seed the database and we were good to go.
High Level App Structure
- Essentially my react files get bundled into a static folder of html, css and javascript and served to the client when they visit my homepage url
- from there, their browser makes requests for data from specific api endpoints being served up by my rails backend hosted on heroku
- For example, when a user clicks on a classroom link react-router renders the classroom container with the specified id - the classroomContainer fetches the student data from the rails server at 'api/v1/classrooms/{classroom_id}', the http request hits the controller, which hits the database via the models and returns a nested hash of data with the students, and all their attendances/notes. This information is sent back to the classroomContainer via the http protocol, then rendered and passed through a series of nested components contained within the bundle downloaded by the users browser. If the user decides to add a new note, the information is sent to the rails server through another api route, with the post header. The data is saved to the postgres database, the database is then re-queried for the updated information, which is sent back to the component that sent the request and the page is re-rendered based on the updated state.
Troubleshooting
- The first issue I ran into was that my application was crashing when it received a get request to the home route. I checked out the logs at heroku and that was the only information I found. I scoured the internet a bit and came up with trying to run the rails console remotely through heroku to get more information. This gave me a very clear error message that I had a few redundant serializers with improperly defined variable names that were crashing in production - so I removed them.
- The next issue I ran into was that things weren't rendering properly when I browsed beyond the homepage. It turns out rails was set up to serve index.html from the public folder at the root directory, but there were no routes set up to define the pages the links were sending you to. I had to set up a controller route that handled for any non-api route and re-directed them to the index.html file to let react-router handle for those routes. The controller set up looked like this:
class ApplicationController < ActionController::API
def fallback_index_html
render :file => 'public/index.html'
end
end
- The last issue I ran into was that I had left localhost:3000 as a prefix in my fetch request url's. When I went to submit a form I would get a blank white screen because state would set to an empty return value. I checked the log files and saw a 400 server not found error. I then changed the addresses in my fetch requests. Now my requests aren't pre-fixed and the default works fine.
componentDidMount() {
fetch(`/api/v1/classrooms`, {
credentials: 'same-origin',
method: 'GET',
headers: { 'Content-Type': 'application/json' }
})
Resources