├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── .keep │ │ ├── Aarron.jpeg │ │ ├── Ben_Blue_.jpeg │ │ ├── Benjamin_Berman_.jpeg │ │ ├── Chao_.jpeg │ │ ├── Chris_Lee.jpeg │ │ ├── Chris_Lew_.jpeg │ │ ├── Collin_.jpeg │ │ ├── Corey_.jpeg │ │ ├── Daniel_Nam.jpeg │ │ ├── David_Veytsman_.jpeg │ │ ├── David_Webster.jpeg │ │ ├── Drew.jpeg │ │ ├── Elliott_.jpeg │ │ ├── Emily_.jpeg │ │ ├── Ethan_.jpeg │ │ ├── George.jpeg │ │ ├── Glenn_.jpeg │ │ ├── Hanhee.jpeg │ │ ├── Isaac.jpeg │ │ ├── Kelby_.jpeg │ │ ├── Kennan_Zhong_.jpeg │ │ ├── Kevin_Coppa.jpeg │ │ ├── Kevin_Migueis_.jpeg │ │ ├── Kristina_.jpeg │ │ ├── Matthew_Liu_.jpeg │ │ ├── Matthew_Ruddy_.jpeg │ │ ├── Max.jpeg │ │ ├── Mengling_Ren_.jpeg │ │ ├── Michael_Cohen_.jpeg │ │ ├── Neil_Gewirtz.jpeg │ │ ├── Noe.jpeg │ │ ├── Oguzhen_.jpeg │ │ ├── Omar.jpeg │ │ ├── Ommi.jpeg │ │ ├── Patrick_.jpeg │ │ ├── Patrick_Fulghum_.jpeg │ │ ├── Raymond_.jpeg │ │ ├── Rick.jpeg │ │ ├── Steven_Ossorio_.jpeg │ │ ├── Thomas_H.jpeg │ │ ├── Thuy_.jpeg │ │ ├── Valery_.jpeg │ │ ├── Victor.jpeg │ │ ├── accounting.jpeg │ │ ├── background.jpg │ │ ├── biology.jpg │ │ ├── chemistry.jpeg │ │ ├── coding.jpg │ │ ├── default.jpeg │ │ ├── favicon.ico │ │ ├── history.jpg │ │ ├── howitworks1.svg │ │ ├── howitworks2.svg │ │ ├── howitworks3.svg │ │ ├── loginpage.jpg │ │ ├── logo.png │ │ ├── physics.jpeg │ │ ├── pledge.svg │ │ ├── socicon.eot │ │ ├── socicon.svg │ │ ├── socicon.ttf │ │ ├── socicon.woff │ │ ├── taskrabbitbg.jpg │ │ ├── test-prep.jpeg │ │ ├── trusticon.svg │ │ └── workspace.jpg │ ├── javascripts │ │ ├── application.js │ │ ├── cable.js │ │ └── channels │ │ │ └── .keep │ └── stylesheets │ │ ├── account.scss │ │ ├── application.css │ │ ├── booking_item.scss │ │ ├── completebooking.scss │ │ ├── confirm.scss │ │ ├── dashboard.scss │ │ ├── fbgooglelogin.scss │ │ ├── form.scss │ │ ├── google.scss │ │ ├── homepage.scss │ │ ├── login.scss │ │ ├── modal.scss │ │ ├── searchbar.scss │ │ ├── show.scss │ │ ├── signup.scss │ │ ├── socialicons.scss │ │ └── taskerform.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── api │ │ ├── hired_tutors_controller.rb │ │ ├── reviews_controller.rb │ │ ├── sessions_controller.rb │ │ ├── subjects_controller.rb │ │ ├── tutors_for_hires_controller.rb │ │ └── users_controller.rb │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ └── static_pages_controller.rb ├── helpers │ └── application_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── application_record.rb │ ├── available_time.rb │ ├── concerns │ │ └── .keep │ ├── hired_tutor.rb │ ├── review.rb │ ├── subject.rb │ ├── time_of_day.rb │ ├── tutors_for_hire.rb │ └── user.rb └── views │ ├── api │ ├── hired_tutors │ │ └── index.json.jbuilder │ ├── tutors_for_hires │ │ └── index.json.jbuilder │ └── users │ │ ├── _user.json.jbuilder │ │ └── show.json.jbuilder │ ├── layouts │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ └── static_pages │ └── root.html.erb ├── bin ├── bundle ├── rails ├── rake ├── setup ├── spring ├── update └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── secrets.yml └── spring.rb ├── db ├── migrate │ ├── 20171024150133_create_users.rb │ ├── 20171024150536_deleteusers.rb │ ├── 20171024150642_addnewusertable.rb │ ├── 20171024151058_deletenewusers.rb │ ├── 20171024152237_newusers.rb │ ├── 20171024170632_changenames.rb │ ├── 20171024212555_create_subjects.rb │ ├── 20171024212841_create_tutors_for_hires.rb │ ├── 20171024214306_create_available_times.rb │ ├── 20171024214544_create_hired_tutors.rb │ ├── 20171024214708_create_reviews.rb │ ├── 20171024234541_deletethingsfromtutorstable.rb │ ├── 20171024234943_updatebooleans.rb │ ├── 20171027131139_add_attachment_image_to_users.rb │ ├── 20171027193700_create_time_of_days.rb │ ├── 20171029231855_changehiredtutors.rb │ ├── 20171029235356_edit_hiredtutor.rb │ ├── 20171029235954_add_default_completed.rb │ └── 20171101134706_addsubjecttoreview.rb ├── schema.rb └── seeds.rb ├── frontend ├── actions │ ├── session_actions.js │ ├── tutor_actions.js │ └── ui_actions.js ├── components │ ├── App.jsx │ ├── account │ │ ├── account.jsx │ │ ├── account_container.js │ │ ├── account_edit_form.jsx │ │ ├── account_edit_form_container.js │ │ ├── account_edit_form_container.jsx │ │ ├── account_info.jsx │ │ └── account_info_container.js │ ├── becometasker │ │ ├── tasker_form.jsx │ │ └── tasker_form_container.js │ ├── dashboard │ │ ├── booking_item.jsx │ │ ├── complete_booking.jsx │ │ ├── complete_booking_container.js │ │ ├── dashboard.jsx │ │ ├── dashboard_container.js │ │ └── how_to_get_started.jsx │ ├── greeting │ │ ├── greeting.jsx │ │ └── greeting_container.js │ ├── homepage │ │ ├── header.jsx │ │ ├── homepage.jsx │ │ ├── homepage_container.js │ │ ├── how_it_works.jsx │ │ └── results.jsx │ ├── login_form │ │ ├── login_form.jsx │ │ └── login_form_container.js │ ├── root.jsx │ ├── shared │ │ ├── error_handler.jsx │ │ ├── footer.jsx │ │ ├── header.jsx │ │ └── trust_banner.jsx │ ├── signup_form │ │ ├── signup_form.jsx │ │ └── signup_form_container.js │ └── tutor_form │ │ ├── date_box_item.jsx │ │ ├── google │ │ └── google_autocomplete.js │ │ ├── modals │ │ ├── login_modal.jsx │ │ ├── login_modal_container.js │ │ └── signup_modal.jsx │ │ ├── sorting_container.jsx │ │ ├── tutor_confirm.jsx │ │ ├── tutor_confirm_container.js │ │ ├── tutor_form.jsx │ │ ├── tutor_form_container.js │ │ ├── tutor_list_item.jsx │ │ ├── tutor_show.jsx │ │ └── tutor_show_container.js ├── reducers │ ├── errors_reducer.js │ ├── root_reducer.js │ ├── session_errors_reducer.js │ ├── session_reducer.js │ ├── tutors_reducer.js │ └── ui_reducer.js ├── store │ └── store.js ├── taskable.jsx └── util │ ├── auth_route.jsx │ ├── session_api_util.js │ └── tutor_api_util.js ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── package.json ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── test ├── application_system_test_case.rb ├── controllers │ └── .keep ├── fixtures │ ├── .keep │ ├── available_times.yml │ ├── files │ │ └── .keep │ ├── hired_tutors.yml │ ├── reviews.yml │ ├── subjects.yml │ ├── time_of_days.yml │ ├── tutors_for_hires.yml │ └── users.yml ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── available_time_test.rb │ ├── hired_tutor_test.rb │ ├── review_test.rb │ ├── subject_test.rb │ ├── time_of_day_test.rb │ ├── tutors_for_hire_test.rb │ └── user_test.rb ├── system │ └── .keep └── test_helper.rb ├── vendor └── .keep ├── webpack.config.js └── wireframe ├── homepage:signup.png ├── login:taskform.png └── taskshow.png /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | /node_modules 17 | /yarn-error.log 18 | 19 | .byebug_history 20 | .bundle.js 21 | .bundle.map.js 22 | .byebug_history 23 | .DS_Store 24 | npm-debug.log 25 | 26 | # Ignore application configuration 27 | /config/application.yml 28 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | git_source(:github) do |repo_name| 4 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") 5 | "https://github.com/#{repo_name}.git" 6 | end 7 | gem 'paperclip', "~> 5.0.0" 8 | gem 'figaro' 9 | gem 'rails_12factor' 10 | gem 'aws-sdk', '< 3.0' 11 | gem 'annotate' 12 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 13 | gem 'rails', '~> 5.1.4' 14 | # Use postgresql as the database for Active Record 15 | gem 'pg', '~> 0.18' 16 | # Use Puma as the app server 17 | gem 'puma', '~> 3.7' 18 | # Use SCSS for stylesheets 19 | gem 'sass-rails', '~> 5.0' 20 | # Use Uglifier as compressor for JavaScript assets 21 | gem 'uglifier', '>= 1.3.0' 22 | # See https://github.com/rails/execjs#readme for more supported runtimes 23 | # gem 'therubyracer', platforms: :ruby 24 | 25 | # Use CoffeeScript for .coffee assets and views 26 | gem 'coffee-rails', '~> 4.2' 27 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 28 | gem 'jbuilder', '~> 2.5' 29 | # Use Redis adapter to run Action Cable in production 30 | # gem 'redis', '~> 3.0' 31 | # Use ActiveModel has_secure_password 32 | gem 'bcrypt', '~> 3.1.7' 33 | gem 'jquery-rails' 34 | 35 | # Use Capistrano for deployment 36 | # gem 'capistrano-rails', group: :development 37 | 38 | group :development, :test do 39 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 40 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 41 | # Adds support for Capybara system testing and selenium driver 42 | gem 'capybara', '~> 2.13' 43 | gem 'selenium-webdriver' 44 | end 45 | 46 | group :development do 47 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 48 | gem 'web-console', '>= 3.3.0' 49 | gem 'listen', '>= 3.0.5', '< 3.2' 50 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 51 | gem 'spring' 52 | gem 'spring-watcher-listen', '~> 2.0.0' 53 | end 54 | 55 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 56 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Taskable 2 | ========== 3 | 4 | ![Logo](https://s3.us-east-2.amazonaws.com/app-taskable-pro/logo.png) 5 | 6 | This is a Ruby on Rails application with a React-redux front-end inspired by the website TaskRabbit. It has the essential functionality of the TaskRabbit application but instead of hiring taskers for manual labor, Taskable allows hiring of tutors for educational help. 7 | 8 | --- 9 | 10 | ## Demo 11 | Here is the working live demo : https://taskable.me/ 12 | 13 | 14 | ![demologin](https://media.giphy.com/media/l4Epek6Tq7cdmvar6/giphy.gif) 15 | 16 | ## Features 17 | - User authentication with additional social log in buttons (Facebook/Google) 18 | - Searching for address using Google Places Api autocomplete 19 | - Front-end sorting of search results using date, time, and other parameters 20 | - Ability to browse website while not signed in 21 | - Prompts a sign-in if attempting to book a tutor 22 | - Select a tutor, input fake payment information 23 | - Complete bookings by adding a review 24 | - Update user information with a profile picture 25 | - many more awesome features to come... 26 | 27 | 28 | ## Landing Page 29 | 30 | ![Splash Page](https://s3.us-east-2.amazonaws.com/app-taskable-pro/Screen+Shot+2017-11-03+at+9.21.59+AM.png) 31 | 32 | ## Result Filtering and Sorting 33 | 34 | ![sorting results](https://media.giphy.com/media/xT1Ra7FBsNjpPnREly/giphy.gif) 35 | 36 | A sorted by field, a date field, and time selection dropdown were created to help filter and sort the results of your search. 37 | Implementing all three parameters was tricky, making sure each change changed the React state of the page accordingly. A change in the state triggers a re-filter of the list of available tutors and re-renders the updated, sorted list. 38 | 39 | #### Creating an array of date selection boxes 40 | 41 | ``` 42 | dateArrays(num) { 43 | let result = []; 44 | let x = new Date; 45 | for (var i = 0; i < num; i++) { 46 | result.push({ 47 | date: (x.toDateString().slice(4,10).trim()), 48 | day: x.getDay(), 49 | day_string: x.toDateString().slice(0,3) 50 | }); 51 | x.setDate(x.getDate() + 1); 52 | } 53 | result[0].day_string = 'Today'; 54 | return result; 55 | } 56 | ``` 57 | 58 | ## Prompt login or signup upon booking 59 | 60 | If user attempts to make a booking but is not signed in, a modal comes up to prompt user to sign up or log in. 61 | 62 | After logging in or signing up, they are subsequently logged in and able to make a booking. Users can switch between the signup and login modal. 63 |
64 | 65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 | ## Built with 73 | - [Ruby on Rails](http://rubyonrails.org/) - A server-side web application framework written in Ruby, a MVC framework. 74 | - [React](https://reactjs.org/) - React is a JS library to build an interactive, seamless UI 75 | - [Redux](https://redux.js.org/) - Predictable state container for JS apps 76 | - [React-Redux](https://github.com/reactjs/react-redux) - Binds React state with Redux store 77 | - [Google Maps Places Api](https://developers.google.com/places/web-service/) - Gets data from same database used by Google Maps. 78 | - [Facebook Login Api](https://developers.facebook.com/docs/facebook-login/) - Facebook Login is a secure, fast and convenient way for people to log into your app or website. 79 | - [Google Signin](https://developers.google.com/identity/) - Google Sign-In is a secure authentication system that reduces the burden of login for your users, by enabling them to sign in with their Google account. 80 | 81 | ## To-do 82 | - Implement reviews for each tutor. When browsing available tutors, users should be able to click on an individual user and view all of their previous reviews and a rating based on all of those reviews. 83 | - Add location functionality - check whether or not user inputted address is within range of any tutors. 84 | - Add a form for users to become a tutor 85 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/Aarron.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Aarron.jpeg -------------------------------------------------------------------------------- /app/assets/images/Ben_Blue_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Ben_Blue_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Benjamin_Berman_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Benjamin_Berman_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Chao_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Chao_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Chris_Lee.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Chris_Lee.jpeg -------------------------------------------------------------------------------- /app/assets/images/Chris_Lew_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Chris_Lew_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Collin_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Collin_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Corey_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Corey_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Daniel_Nam.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Daniel_Nam.jpeg -------------------------------------------------------------------------------- /app/assets/images/David_Veytsman_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/David_Veytsman_.jpeg -------------------------------------------------------------------------------- /app/assets/images/David_Webster.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/David_Webster.jpeg -------------------------------------------------------------------------------- /app/assets/images/Drew.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Drew.jpeg -------------------------------------------------------------------------------- /app/assets/images/Elliott_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Elliott_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Emily_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Emily_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Ethan_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Ethan_.jpeg -------------------------------------------------------------------------------- /app/assets/images/George.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/George.jpeg -------------------------------------------------------------------------------- /app/assets/images/Glenn_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Glenn_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Hanhee.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Hanhee.jpeg -------------------------------------------------------------------------------- /app/assets/images/Isaac.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Isaac.jpeg -------------------------------------------------------------------------------- /app/assets/images/Kelby_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Kelby_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Kennan_Zhong_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Kennan_Zhong_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Kevin_Coppa.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Kevin_Coppa.jpeg -------------------------------------------------------------------------------- /app/assets/images/Kevin_Migueis_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Kevin_Migueis_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Kristina_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Kristina_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Matthew_Liu_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Matthew_Liu_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Matthew_Ruddy_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Matthew_Ruddy_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Max.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Max.jpeg -------------------------------------------------------------------------------- /app/assets/images/Mengling_Ren_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Mengling_Ren_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Michael_Cohen_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Michael_Cohen_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Neil_Gewirtz.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Neil_Gewirtz.jpeg -------------------------------------------------------------------------------- /app/assets/images/Noe.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Noe.jpeg -------------------------------------------------------------------------------- /app/assets/images/Oguzhen_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Oguzhen_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Omar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Omar.jpeg -------------------------------------------------------------------------------- /app/assets/images/Ommi.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Ommi.jpeg -------------------------------------------------------------------------------- /app/assets/images/Patrick_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Patrick_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Patrick_Fulghum_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Patrick_Fulghum_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Raymond_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Raymond_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Rick.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Rick.jpeg -------------------------------------------------------------------------------- /app/assets/images/Steven_Ossorio_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Steven_Ossorio_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Thomas_H.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Thomas_H.jpeg -------------------------------------------------------------------------------- /app/assets/images/Thuy_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Thuy_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Valery_.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Valery_.jpeg -------------------------------------------------------------------------------- /app/assets/images/Victor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/Victor.jpeg -------------------------------------------------------------------------------- /app/assets/images/accounting.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/accounting.jpeg -------------------------------------------------------------------------------- /app/assets/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/background.jpg -------------------------------------------------------------------------------- /app/assets/images/biology.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/biology.jpg -------------------------------------------------------------------------------- /app/assets/images/chemistry.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/chemistry.jpeg -------------------------------------------------------------------------------- /app/assets/images/coding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/coding.jpg -------------------------------------------------------------------------------- /app/assets/images/default.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/default.jpeg -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/history.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/history.jpg -------------------------------------------------------------------------------- /app/assets/images/howitworks3.svg: -------------------------------------------------------------------------------- 1 | Asset 18heartheartheartheartheartheart -------------------------------------------------------------------------------- /app/assets/images/loginpage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/loginpage.jpg -------------------------------------------------------------------------------- /app/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/logo.png -------------------------------------------------------------------------------- /app/assets/images/physics.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/physics.jpeg -------------------------------------------------------------------------------- /app/assets/images/pledge.svg: -------------------------------------------------------------------------------- 1 | Asset 10 -------------------------------------------------------------------------------- /app/assets/images/socicon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/socicon.eot -------------------------------------------------------------------------------- /app/assets/images/socicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/socicon.ttf -------------------------------------------------------------------------------- /app/assets/images/socicon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/socicon.woff -------------------------------------------------------------------------------- /app/assets/images/taskrabbitbg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/taskrabbitbg.jpg -------------------------------------------------------------------------------- /app/assets/images/test-prep.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/test-prep.jpeg -------------------------------------------------------------------------------- /app/assets/images/workspace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/images/workspace.jpg -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's 5 | // vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require rails-ujs 14 | //= require_tree . 15 | //= require jquery 16 | //= require jquery_ujs 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/account.scss: -------------------------------------------------------------------------------- 1 | .account-container { 2 | width: 960px; 3 | margin: 0 auto; 4 | } 5 | 6 | .account-box { 7 | border: 1px solid #dce0e6; 8 | background-color: white; 9 | border-radius: 4px; 10 | } 11 | 12 | .account-portal { 13 | display: flex; 14 | 15 | } 16 | 17 | .account-info { 18 | width: 70%; 19 | padding: 1em; 20 | } 21 | 22 | .account-info-title { 23 | font-size: 1.75rem; 24 | position: absolute; 25 | top: -18px; 26 | } 27 | 28 | .account-panel { 29 | width: 25%; 30 | float: left; 31 | border-right: 1px solid #dce0e6; 32 | } 33 | 34 | .account-info-title-container { 35 | position: relative; 36 | } 37 | .account-info-row { 38 | display: flex; 39 | justify-content: space-between; 40 | align-items: baseline; 41 | margin-bottom: 1rem; 42 | border-bottom: 1px solid #dce0e6; 43 | padding-bottom: 1em; 44 | } 45 | 46 | .account-info-container { 47 | display: flex; 48 | } 49 | 50 | .account-infos-container { 51 | display: flex; 52 | flex-direction: column; 53 | } 54 | 55 | .account-list { 56 | list-style: none; 57 | margin-top: 2rem; 58 | } 59 | 60 | .account-list li { 61 | margin-bottom: 5px; 62 | font-size: 23px; 63 | font-weight: 300; 64 | display: flex; 65 | } 66 | 67 | .account-picture { 68 | height: 175px; 69 | width: 175px; 70 | border-radius: 50%; 71 | margin: 1rem 3rem; 72 | cursor: pointer; 73 | object-fit: cover; 74 | } 75 | 76 | .account-picture-container { 77 | display: flex; 78 | flex-direction: column; 79 | align-items: center; 80 | } 81 | 82 | .upload-photo-label { 83 | margin: .5rem 0; 84 | cursor: pointer; 85 | } 86 | 87 | .edit-input { 88 | margin-bottom: 7px; 89 | } 90 | 91 | .edit-form-btn { 92 | margin-left: 10px; 93 | font-size: 1.25rem; 94 | border: 1px solid #51af33; 95 | width: 100%; 96 | background: #51af33; 97 | border-radius: 4px; 98 | padding: 0.6em 4em 0.6em; 99 | text-decoration: none; 100 | color: white; 101 | margin-top: 8px; 102 | transition: background-color 0.5s ease; 103 | margin-bottom: 10px; 104 | } 105 | 106 | .edit-form-btn:hover { 107 | background-color: #438e29; 108 | color: white; 109 | transition: background-color 0.5s ease; 110 | cursor: pointer; 111 | } 112 | 113 | .upload-photo-label { 114 | height: 215px; 115 | width: 175px; 116 | position: absolute; 117 | z-index: 1; 118 | opacity: 0; 119 | } 120 | 121 | .side-nav { 122 | list-style: none; 123 | color: #51af33; 124 | padding-left: 0; 125 | margin: 0; 126 | } 127 | 128 | .side-nav-item { 129 | padding-left: 40px; 130 | padding: 18px 40px; 131 | cursor: pointer; 132 | } 133 | 134 | .side-nav-item:hover { 135 | border-left: 5px solid gray; 136 | } 137 | 138 | .side-nav-link { 139 | text-decoration: none; 140 | color: #51af33; 141 | } 142 | 143 | .side-nav-link:hover { 144 | color: #438e29; 145 | 146 | } 147 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/booking_item.scss: -------------------------------------------------------------------------------- 1 | git.booking-item { 2 | min-width: 650px; 3 | border: 1px solid #dce0e6; 4 | border-radius: 4px; 5 | padding: 1em 1em; 6 | margin-bottom: 1rem; 7 | } 8 | 9 | .booking-result-figure { 10 | display: flex; 11 | flex-direction: row; 12 | justify-content: space-between; 13 | margin-bottom: 1rem; 14 | padding-bottom: 1rem; 15 | border-bottom: 1px solid #dce0e6; 16 | } 17 | 18 | .booking-picture { 19 | height: 35px; 20 | width: 35px; 21 | border-radius: 50%; 22 | object-fit: cover; 23 | } 24 | 25 | .booking-name { 26 | font-weight: 400; 27 | font-size: 1.75rem; 28 | } 29 | 30 | 31 | .booking-rate { 32 | float: right; 33 | } 34 | 35 | .booking-date-time-location { 36 | display: flex; 37 | font-weight: 200; 38 | justify-content: space-between; 39 | margin: 1.5rem 0; 40 | } 41 | 42 | .booking-date-container { 43 | display: flex; 44 | align-items: center; 45 | } 46 | .booking-date { 47 | margin-right: 1rem; 48 | padding-right: 1rem; 49 | border-right: 1px solid gray; 50 | font-weight: 400; 51 | } 52 | 53 | .booking-location-container { 54 | align-items: flex-end; 55 | display: flex; 56 | flex-direction: column; 57 | } 58 | 59 | .booking-content { 60 | display: flex; 61 | flex-direction: column; 62 | } 63 | 64 | .booking-item label { 65 | color: #67727e; 66 | font-weight: 200; 67 | font-size: .825rem; 68 | margin-bottom: .5rem; 69 | } 70 | 71 | .booking-complete-container { 72 | margin-top: 1rem; 73 | display: flex; 74 | flex-direction: column; 75 | align-items: center; 76 | } 77 | 78 | .complete-booking-button { 79 | font-size: 1.25rem; 80 | border: 1px solid #51af33; 81 | width: 50%; 82 | background: #51af33; 83 | border-radius: 4px; 84 | padding: 0.6em 4em 0.6em; 85 | text-decoration: none; 86 | color: white; 87 | margin-top: 8px; 88 | transition: background-color 0.5s ease; 89 | margin-bottom: 10px; 90 | } 91 | 92 | .complete-booking-button:hover { 93 | background-color: #438e29; 94 | color: white; 95 | transition: background-color 0.5s ease; 96 | cursor: pointer; 97 | } 98 | 99 | .booking-content { 100 | padding-top: 1rem; 101 | border-top: 1px solid #dce0e6 102 | } 103 | 104 | .positive-radio { 105 | padding-left: 0; 106 | } 107 | -------------------------------------------------------------------------------- /app/assets/stylesheets/completebooking.scss: -------------------------------------------------------------------------------- 1 | .feedback { 2 | margin-bottom: .5rem; 3 | } 4 | 5 | .positive-radio { 6 | list-style: none; 7 | } 8 | 9 | .review-description { 10 | border: 1px solid gray; 11 | border-radius: 4px; 12 | padding: .625em 1em; 13 | font-size: 1em; 14 | font-weight: 200; 15 | width: 92%; 16 | white-space: normal; 17 | min-height: 23px; 18 | margin-bottom: 10px; 19 | margin-top: 10px; 20 | } 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/confirm.scss: -------------------------------------------------------------------------------- 1 | .confirmation-container { 2 | padding: 0 2.5rem; 3 | } 4 | 5 | .confirm-result-top-row { 6 | display: flex; 7 | justify-content: space-between; 8 | margin: 1.2rem 0; 9 | } 10 | 11 | .submit-container { 12 | margin-top: 4rem; 13 | } 14 | 15 | .payment-sub { 16 | color: #545e69; 17 | font-size: .875rem; 18 | margin-bottom: 1em; 19 | } 20 | 21 | .payment-container { 22 | display: flex; 23 | flex-direction: column; 24 | color: #545e69; 25 | } 26 | 27 | .payment-input-row { 28 | display: flex; 29 | justify-content: space-between; 30 | font-size: .875rem; 31 | color: black; 32 | font-weight: 200; 33 | border: 1px 34 | } 35 | 36 | .payment-input-row span { 37 | margin-bottom: 3px; 38 | } 39 | 40 | .payment-input-box { 41 | display: flex; 42 | flex-direction: column; 43 | } 44 | 45 | .payment-input-box select { 46 | padding: 0.5em 0.75em; 47 | height: 41px; 48 | font-size: 1em; 49 | margin-right: 10px; 50 | } 51 | 52 | .mm { 53 | width: 60px; 54 | } 55 | 56 | .yyyy { 57 | width: 90px; 58 | } 59 | 60 | .payment-input-box input { 61 | border: 1px solid #b1b9c3; 62 | outline: none; 63 | border-radius: 4px; 64 | font-size: 1em; 65 | font-weight: normal; 66 | height: 22px; 67 | padding: 0.625em 1em; 68 | margin-right: 1em; 69 | } 70 | 71 | .col-lg-2 { 72 | width: 16.6666% 73 | } 74 | 75 | .col-lg-3 { 76 | width: 25% 77 | } 78 | 79 | .col-lg-4 { 80 | width: 33.3333% 81 | } 82 | 83 | .confirm-info { 84 | border-top: 1px solid #dce0e6; 85 | padding-top: 2rem; 86 | margin-top: 3rem; 87 | } 88 | 89 | .date-time-confirm { 90 | width: 50%; 91 | } 92 | 93 | .confirm-tutor-info { 94 | width: 50%; 95 | float: left; 96 | position: relative; 97 | display: flex; 98 | border-left: 1px solid #b1b9c3; 99 | 100 | } 101 | 102 | .confirm-tutor-pic { 103 | margin-left: 3rem; 104 | margin-right: 1.5em; 105 | } 106 | 107 | .confirm-picture { 108 | width: 36px; 109 | height: 36px; 110 | border-radius: 4px; 111 | margin-top: .4em; 112 | } 113 | 114 | .confirm-sub { 115 | color: #545e69; 116 | font-weight: 200; 117 | font-size: .875rem; 118 | } 119 | 120 | .confirmation-date-time { 121 | font-size: 1.125rem; 122 | margin-top: 0.5rem; 123 | font-weight: normal; 124 | margin-bottom: 1.5rem; 125 | } 126 | .confirm-tutor-name { 127 | display: flex; 128 | flex-direction: column; 129 | color: #545e69; 130 | margin-top: .25em; 131 | } 132 | 133 | .tutor-name { 134 | color: black; 135 | font-size: 1.125rem; 136 | } 137 | 138 | .confirm-button-sub-container { 139 | display: flex; 140 | justify-content: center; 141 | margin: 1rem 0; 142 | font-size: .8em; 143 | } 144 | 145 | .confirm-form { 146 | margin-bottom: 1rem; 147 | } 148 | 149 | .confirm-smallprint { 150 | margin: 0 auto; 151 | margin-top: 3rem; 152 | display: flex; 153 | flex-direction: column; 154 | justify-content: center; 155 | align-items: center; 156 | width: 720px; 157 | } 158 | 159 | .confirm-smallprint span { 160 | width: 720px; 161 | margin-bottom: 1.7rem; 162 | color: #545e69; 163 | font-weight: 100; 164 | } 165 | -------------------------------------------------------------------------------- /app/assets/stylesheets/dashboard.scss: -------------------------------------------------------------------------------- 1 | .dashboard-main-page { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .dashboard-container { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | margin-top: 1rem; 12 | } 13 | 14 | .row { 15 | min-width: 800px; 16 | } 17 | 18 | .greeting { 19 | margin-bottom: 1rem; 20 | display: flex; 21 | } 22 | 23 | .greeting h1 { 24 | font-weight: 300; 25 | } 26 | .greeting-picture { 27 | height: 75px; 28 | width: 75px; 29 | border-radius: 50%; 30 | margin-right: 3rem; 31 | object-fit: cover; 32 | } 33 | .form-pictures { 34 | width: 1200px; 35 | } 36 | 37 | .tile-container { 38 | min-height: 800px; 39 | display: flex; 40 | flex-wrap: wrap; 41 | } 42 | 43 | .tutor-link-tile { 44 | width: 33.333% 45 | } 46 | 47 | .tutor-link-title Link{ 48 | 49 | color: #438e29; 50 | } 51 | 52 | .biology { 53 | background: image-url('biology.jpg'); 54 | } 55 | 56 | .chemistry { 57 | background: image-url('chemistry.jpeg'); 58 | } 59 | 60 | .coding { 61 | background: image-url('coding.jpg'); 62 | } 63 | 64 | .history { 65 | background: image-url('history.jpg'); 66 | } 67 | 68 | .physics { 69 | background: image-url('physics.jpeg'); 70 | } 71 | 72 | .testprep { 73 | background: image-url('test-prep.jpeg'); 74 | } 75 | 76 | .tutor-link-tile { 77 | height: 380px; 78 | width: 380px; 79 | margin-right: 20px; 80 | background-repeat: no-repeat; 81 | background-size: 100%; 82 | display: inline-block; 83 | position: relative; 84 | } 85 | 86 | .flag-container { 87 | position: relative; 88 | z-index: 500; 89 | display: block; 90 | height: 50px; 91 | vertical-align: baseline; 92 | } 93 | .subject-title-sub { 94 | position: absolute; 95 | top: 18rem; 96 | padding: .6em 1.4em; 97 | background-color: rgba(255,255,255,0.75); 98 | color: black; 99 | font-weight: bold; 100 | transition: color 0.5s ease; 101 | transition: background-color 0.5s ease; 102 | } 103 | 104 | .no-booking-available { 105 | margin: 3rem 0; 106 | } 107 | 108 | .hover { 109 | background-color: white; 110 | transition: color 0.5s ease; 111 | transition: background-color 0.5s ease; 112 | color: #438e29; 113 | } 114 | 115 | .created-by { 116 | font-weight: 100; 117 | text-align: right; 118 | width: 100%; 119 | margin-right: 2rem; 120 | } 121 | 122 | .how-to-get-started-container { 123 | margin-bottom: 3rem; 124 | } 125 | 126 | .get-started-step { 127 | display: flex; 128 | } 129 | 130 | .get-started-figure { 131 | margin-right: 1.5em; 132 | margin-top: 1em; 133 | } 134 | 135 | .get-started-icon { 136 | background-color: #67727e; 137 | font-size: 1.25em; 138 | display: inline-block; 139 | height: 40px; 140 | width: 40px; 141 | border-radius: 50%; 142 | text-align: center; 143 | font-weight: 600; 144 | color: white; 145 | margin-top: 7px; 146 | align-items: center; 147 | justify-content: center; 148 | line-height: 43px; 149 | } 150 | 151 | .get-started-title { 152 | font-size: 2rem; 153 | line-height: 1.25; 154 | font-weight: normal; 155 | } 156 | 157 | .get-started-sub { 158 | margin: 0 0 1rem 0; 159 | color: #242a30; 160 | font-family: "Proxima", Arial, Helvetica, sans-serif; 161 | font-weight: 100; 162 | font-size: 1rem; 163 | line-height: 1.5; 164 | } 165 | 166 | .get-started-name { 167 | color: #67727e; 168 | line-height: 1.25; 169 | margin-bottom: .3em; 170 | font-weight: 400; 171 | } 172 | -------------------------------------------------------------------------------- /app/assets/stylesheets/fbgooglelogin.scss: -------------------------------------------------------------------------------- 1 | .loginBtn { 2 | box-sizing: border-box; 3 | position: relative; 4 | /* width: 13em; - apply for fixed size */ 5 | margin: 0.2em; 6 | padding: 0 15px 0 46px; 7 | border: none; 8 | text-align: left; 9 | line-height: 34px; 10 | white-space: nowrap; 11 | border-radius: 0.2em; 12 | font-size: 16px; 13 | color: #FFF; 14 | } 15 | .loginBtn:before { 16 | content: ""; 17 | box-sizing: border-box; 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | width: 34px; 22 | height: 100%; 23 | } 24 | .loginBtn:focus { 25 | outline: none; 26 | } 27 | .loginBtn:active { 28 | box-shadow: inset 0 0 0 32px rgba(0,0,0,0.1); 29 | } 30 | 31 | 32 | /* Facebook */ 33 | .loginBtn--facebook { 34 | background-color: #4C69BA; 35 | background-image: linear-gradient(#4C69BA, #3B55A0); 36 | /*font-family: "Helvetica neue", Helvetica Neue, Helvetica, Arial, sans-serif;*/ 37 | text-shadow: 0 -1px 0 #354C8C; 38 | } 39 | .loginBtn--facebook:before { 40 | border-right: #364e92 1px solid; 41 | background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/14082/icon_facebook.png') 6px 6px no-repeat; 42 | } 43 | .loginBtn--facebook:hover, 44 | .loginBtn--facebook:focus { 45 | background-color: #5B7BD5; 46 | background-image: linear-gradient(#5B7BD5, #4864B1); 47 | } 48 | 49 | 50 | /* Google */ 51 | .loginBtn--google { 52 | /*font-family: "Roboto", Roboto, arial, sans-serif;*/ 53 | background: #DD4B39; 54 | } 55 | .loginBtn--google:before { 56 | border-right: #BB3F30 1px solid; 57 | background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/14082/icon_google.png') 6px 6px no-repeat; 58 | } 59 | .loginBtn--google:hover, 60 | .loginBtn--google:focus { 61 | background: #E74B37; 62 | } 63 | -------------------------------------------------------------------------------- /app/assets/stylesheets/google.scss: -------------------------------------------------------------------------------- 1 | #map { 2 | height: 100%; 3 | } 4 | /* Optional: Makes the sample page fill the window. */ 5 | html, body { 6 | height: 100%; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | #description { 11 | font-family: Roboto; 12 | font-size: 15px; 13 | font-weight: 300; 14 | } 15 | 16 | #infowindow-content .title { 17 | font-weight: bold; 18 | } 19 | 20 | #infowindow-content { 21 | display: none; 22 | } 23 | 24 | #map #infowindow-content { 25 | display: inline; 26 | } 27 | 28 | .pac-card { 29 | margin: 10px 10px 0 0; 30 | border-radius: 2px 0 0 2px; 31 | box-sizing: border-box; 32 | -moz-box-sizing: border-box; 33 | outline: none; 34 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); 35 | background-color: #fff; 36 | font-family: Roboto; 37 | } 38 | 39 | #pac-container { 40 | padding-bottom: 12px; 41 | margin-right: 12px; 42 | } 43 | 44 | .pac-controls { 45 | display: inline-block; 46 | padding: 5px 11px; 47 | } 48 | 49 | .pac-controls label { 50 | font-family: Roboto; 51 | font-size: 13px; 52 | font-weight: 300; 53 | } 54 | 55 | #pac-input { 56 | background-color: #fff; 57 | font-family: Roboto; 58 | font-size: 15px; 59 | font-weight: 300; 60 | margin-left: 12px; 61 | padding: 0 11px 0 13px; 62 | text-overflow: ellipsis; 63 | width: 400px; 64 | } 65 | 66 | #pac-input:focus { 67 | border-color: #4d90fe; 68 | } 69 | 70 | #title { 71 | color: #fff; 72 | background-color: #4d90fe; 73 | font-size: 25px; 74 | font-weight: 500; 75 | padding: 6px 12px; 76 | } 77 | #target { 78 | width: 345px; 79 | } 80 | -------------------------------------------------------------------------------- /app/assets/stylesheets/login.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | } 4 | 5 | body { 6 | height: 100%; 7 | } 8 | 9 | .login-signup-page { 10 | } 11 | 12 | .login-background { 13 | background: image-url('loginpage.jpg'); 14 | background-size: cover; 15 | background-position: center center; 16 | background-repeat: no-repeat; 17 | position: absolute; 18 | top: 0; 19 | bottom: 0; 20 | left: 0; 21 | right: 0; 22 | } 23 | .login-signup-panel { 24 | z-index: 10; 25 | } 26 | .login-page-container { 27 | display: flex; 28 | flex-direction: column; 29 | justify-content: space-around; 30 | background: white; 31 | border: 1px solid #dce0e6; 32 | border-radius: 4px; 33 | margin-left: auto !important; 34 | margin-right: auto !important; 35 | position: relative; 36 | height: 450px; 37 | padding: 1.5rem 2rem; 38 | max-width: 400px; 39 | top: -2rem; 40 | margin-top: 75px; 41 | } 42 | 43 | .logo { 44 | height: 50px; 45 | width: 195px; 46 | margin-bottom: 10px; 47 | } 48 | 49 | .login-form { 50 | height: 60%; 51 | max-width: 375px; 52 | display: flex; 53 | flex-direction: column; 54 | justify-content: space-between; 55 | // justify-content: space-around; 56 | } 57 | 58 | .input-container { 59 | display: block; 60 | max-width: 375px; 61 | position: relative; 62 | } 63 | 64 | .input-container label { 65 | font-weight: 100; 66 | font-size: 15px; 67 | } 68 | 69 | .text-input { 70 | outline: none; 71 | border-style: none; 72 | display: flex; 73 | flex-direction: column; 74 | border-color: transparent; 75 | border: 1px solid #b1b9c3; 76 | min-height: 20px; 77 | vertical-align: middle; 78 | border-radius: 4px; 79 | margin-top: 5px; 80 | margin-right: 0; 81 | padding: 0.625em 10em; 82 | width: 97%; 83 | font-size: 1em; 84 | display: block; 85 | } 86 | 87 | .login-button:hover { 88 | background-color: #438e29; 89 | color: white; 90 | transition: background-color 0.5s ease; 91 | cursor: pointer; 92 | } 93 | 94 | .login-button { 95 | margin-left: 10px; 96 | font-size: 1.25rem; 97 | border: 1px solid #51af33; 98 | width: 100%; 99 | background: #51af33; 100 | border-radius: 4px; 101 | padding: 0.6em 4em 0.6em; 102 | text-decoration: none; 103 | color: white; 104 | margin-top: 8px; 105 | transition: background-color 0.5s ease; 106 | margin-bottom: 10px; 107 | } 108 | 109 | .login-signup-link span { 110 | text-decoration: none; 111 | float: right; 112 | color: #51af33; 113 | margin-left: .3rem; 114 | } 115 | 116 | .login-signup-link span:hover { 117 | color: #438e29; 118 | } 119 | 120 | .login-bottom-row { 121 | display: flex; 122 | justify-content: space-between; 123 | align-items: flex-end; 124 | } 125 | 126 | .login-signup-link { 127 | cursor: pointer; 128 | } 129 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modal.scss: -------------------------------------------------------------------------------- 1 | .signup-modal-title { 2 | font-size: 1.75rem; 3 | line-height: 1.428571429; 4 | margin-bottom: .5rem; 5 | font-weight: 600; 6 | } 7 | 8 | .signup-sub { 9 | margin-bottom: .6rem; 10 | font-weight: 300; 11 | font-size: 1.25rem; 12 | line-height: 1.6; 13 | } 14 | 15 | .modal-container { 16 | display: block; 17 | width: 100%; 18 | position: relative; 19 | } 20 | 21 | .modal-input { 22 | outline: none; 23 | border-style: none; 24 | display: flex; 25 | flex-direction: column; 26 | border-color: transparent; 27 | border: 1px solid #b1b9c3; 28 | min-height: 20px; 29 | vertical-align: middle; 30 | border-radius: 4px; 31 | margin-top: 5px; 32 | margin-bottom: 5px; 33 | padding: 0.625em 0; 34 | width: 91%; 35 | font-size: 1em; 36 | display: block; 37 | padding-left: 2.5em; 38 | } 39 | 40 | .name-container { 41 | display: flex; 42 | } 43 | 44 | .name-container label { 45 | color: #86919d; 46 | cursor: pointer; 47 | font-size: .875rem; 48 | font-weight: 200; 49 | } 50 | 51 | .modal-container label { 52 | color: #86919d; 53 | cursor: pointer; 54 | font-size: .875rem; 55 | font-weight: 200; 56 | } 57 | .name-input-container { 58 | width: 50%; 59 | margin-right: 1.5rem; 60 | position: relative; 61 | } 62 | 63 | .signup-login-link { 64 | cursor: pointer; 65 | margin-left: 0.5rem; 66 | } 67 | 68 | .switch-modal { 69 | display: flex; 70 | margin-top: 1rem; 71 | } 72 | 73 | .modal-icon { 74 | position: absolute; 75 | top: 1.35em; 76 | left: 0; 77 | color: #67727e; 78 | } 79 | 80 | .or { 81 | margin: 1.2rem 0; 82 | text-align: center; 83 | } 84 | 85 | .social-login-container { 86 | display: flex; 87 | justify-content: space-around; 88 | } 89 | 90 | .modal-top-row { 91 | display: flex; 92 | justify-content: space-between; 93 | } 94 | 95 | .modal-close { 96 | cursor: pointer; 97 | } 98 | -------------------------------------------------------------------------------- /app/assets/stylesheets/searchbar.scss: -------------------------------------------------------------------------------- 1 | #search-results { 2 | position: absolute; 3 | background: white; 4 | margin: 0; 5 | list-style: none; 6 | z-index: 200; 7 | border: 1px solid #dce0e6; 8 | border-bottom-left-radius: 4px; 9 | border-bottom-right-radius: 4px; 10 | box-shadow: 0 2px 4px 1px rgba(177,185,195,0.3); 11 | overflow: hidden; 12 | opacity: 1; 13 | padding-left: 0; 14 | } 15 | 16 | .active { 17 | display: block; 18 | } 19 | 20 | .hidden { 21 | display: none; 22 | } 23 | 24 | .search-result { 25 | cursor: pointer; 26 | min-width: 617px; 27 | color: #438e29; 28 | display: flex; 29 | opacity: 1; 30 | } 31 | 32 | .search-result:hover { 33 | background-color: #f9fafb; 34 | } 35 | 36 | .search-link { 37 | padding: .3em 1.4em; 38 | width: 100%; 39 | text-decoration: none; 40 | color: #438e29; 41 | } 42 | 43 | .search-result-row { 44 | display: flex; 45 | align-items: center; 46 | } 47 | 48 | .search-pic-container { 49 | margin-right: 1.5em; 50 | } 51 | 52 | .search-pic { 53 | height: 60px; 54 | width: 60px; 55 | border-radius: 50%; 56 | } 57 | 58 | .search-pic-Coding { 59 | background: image-url('coding.jpg'); 60 | background-size: cover; 61 | 62 | } 63 | 64 | .search-pic-Accounting { 65 | background: image_url('accounting.jpeg'); 66 | background-size: cover; 67 | 68 | } 69 | 70 | .search-pic-Physics { 71 | background: image-url('physics.jpeg'); 72 | background-size: cover; 73 | } 74 | -------------------------------------------------------------------------------- /app/assets/stylesheets/show.scss: -------------------------------------------------------------------------------- 1 | .recommendations-container { 2 | display: flex; 3 | margin-top: 2rem; 4 | width: 1200px; 5 | min-height: 800px; 6 | } 7 | 8 | .recommendations-filter-panel { 9 | padding: 1.5rem 2rem; 10 | background: white; 11 | border: 1px solid #dce0e6; 12 | border-radius: 4px; 13 | overflow: hidden; 14 | } 15 | 16 | .recommendations-filter-container { 17 | margin-right: 24px; 18 | max-width: 376px; 19 | min-width: 376px; 20 | flex: 1 1 auto; 21 | } 22 | 23 | .filter-dropdown { 24 | width: 100%; 25 | margin-top: .3rem; 26 | margin-bottom: .75rem; 27 | padding: 0.5em 0.75em; 28 | background-color: white; 29 | height: 46px; 30 | font-size: 1em; 31 | font-weight: 300; 32 | } 33 | 34 | .time-dropdown { 35 | width: 100%; 36 | margin-top: 1.5rem; 37 | margin-bottom: 1.75rem; 38 | padding: 0.5em 0.75em; 39 | background-color: white; 40 | height: 46px; 41 | font-size: 1em; 42 | font-weight: 300; 43 | } 44 | 45 | .recommendations-filter-title { 46 | margin-top: .5rem; 47 | margin-bottom: 1rem; 48 | } 49 | 50 | .datetime-window-container { 51 | display: flex; 52 | flex-direction: row; 53 | overflow: scroll; 54 | } 55 | 56 | .pick-date-window { 57 | min-width: 80px; 58 | height: 80px; 59 | margin: 2px; 60 | } 61 | 62 | .pick-date-window label { 63 | display: flex; 64 | height: 80px; 65 | flex-direction: column; 66 | justify-content: center; 67 | align-items: center; 68 | cursor: pointer; 69 | border: 1px solid #b1b9c3; 70 | border-radius: 4px; 71 | } 72 | 73 | .selected-date { 74 | background-color: #51af33; 75 | border-color: #51af33; 76 | color: white; 77 | cursor: pointer; 78 | border-radius: 4px; 79 | } 80 | 81 | .recommendation-result { 82 | border: 1px solid #dce0e6; 83 | border-radius: 4px; 84 | padding: 2rem; 85 | background: white; 86 | display: flex; 87 | margin-bottom: 2rem; 88 | } 89 | 90 | .recommendations-result-figure { 91 | display: flex; 92 | flex-direction: column; 93 | } 94 | 95 | .result-picture { 96 | width: 200px; 97 | height: 200px; 98 | object-fit: cover; 99 | } 100 | 101 | .recommendations-result-top-row { 102 | display: flex; 103 | justify-content: space-between; 104 | } 105 | 106 | .recommendations-result-content { 107 | display: flex; 108 | flex-direction: column; 109 | margin-left: 2rem; 110 | width: 100%; 111 | } 112 | 113 | .result-name { 114 | font-size: 2rem; 115 | font-weight: bold; 116 | margin-bottom: 0.5rem; 117 | } 118 | 119 | .result-rate { 120 | font-weight: 500; 121 | font-size: 1.5rem; 122 | } 123 | 124 | .blurb-title { 125 | padding-top: 1rem; 126 | border-top: 1px solid #dce0e6; 127 | font-size: 1rem; 128 | font-weight: 600; 129 | } 130 | 131 | .blurb { 132 | font-weight: 100; 133 | margin-top: 0.6rem; 134 | } 135 | 136 | .is-hidden { 137 | display: none; 138 | } 139 | 140 | .none-available { 141 | margin-left: 7rem; 142 | margin-top: 10rem; 143 | font-weight: bold; 144 | font-size: 23px; 145 | } 146 | 147 | .filter-subtitle { 148 | font-weight: 200; 149 | } 150 | 151 | .book-container { 152 | display: flex; 153 | flex-direction: column; 154 | align-items: center; 155 | } 156 | .booking-link { 157 | margin: 1rem 0; 158 | font-size: 1.125rem; 159 | border: 1px solid #51af33; 160 | background: #51af33; 161 | border-radius: 4px; 162 | padding: 0.5625em 1.125em .375em 1.125em; 163 | text-decoration: none; 164 | color: white; 165 | transition: background-color 0.5s ease; 166 | font-weight: 200; 167 | } 168 | 169 | .booking-link:hover { 170 | background-color: #438e29; 171 | color: white; 172 | transition: background-color 0.5s ease; 173 | cursor: pointer; 174 | } 175 | 176 | .result-info { 177 | list-style: none; 178 | position: relative; 179 | padding-left: 0; 180 | } 181 | 182 | .list-icon { 183 | position: absolute; 184 | bottom: 0; 185 | } 186 | 187 | .list-item { 188 | padding-left: 2.8rem; 189 | } 190 | -------------------------------------------------------------------------------- /app/assets/stylesheets/signup.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | } 4 | 5 | body { 6 | height: 100%; 7 | } 8 | 9 | .login-signup-page { 10 | } 11 | 12 | .login-background { 13 | background: image-url('loginpage.jpg'); 14 | background-size: cover; 15 | background-position: center center; 16 | background-repeat: no-repeat; 17 | position: absolute; 18 | top: 0; 19 | bottom: 0; 20 | left: 0; 21 | right: 0; 22 | height: 1000px; 23 | } 24 | .login-signup-panel { 25 | z-index: 10; 26 | } 27 | .signup-page-container { 28 | display: flex; 29 | flex-direction: column; 30 | justify-content: space-around; 31 | background: white; 32 | border: 1px solid #dce0e6; 33 | border-radius: 4px; 34 | margin-left: auto !important; 35 | margin-right: auto !important; 36 | position: relative; 37 | height: 650px; 38 | padding: 1.5rem 2rem; 39 | max-width: 400px; 40 | top: -2rem; 41 | margin-top: 75px; 42 | 43 | } 44 | 45 | .logo { 46 | height: 50px; 47 | width: 195px; 48 | margin-bottom: 10px; 49 | } 50 | 51 | .signup-form { 52 | height: 100%; 53 | max-width: 375px; 54 | display: flex; 55 | flex-direction: column; 56 | justify-content: space-around; 57 | } 58 | 59 | .input-container { 60 | display: block; 61 | max-width: 375px; 62 | } 63 | 64 | .input-container label { 65 | font-weight: 100; 66 | font-size: 15px; 67 | } 68 | 69 | .text-input { 70 | outline: none; 71 | border-style: none; 72 | display: flex; 73 | flex-direction: column; 74 | border-color: transparent; 75 | border: 1px solid #b1b9c3; 76 | min-height: 20px; 77 | vertical-align: middle; 78 | border-radius: 4px; 79 | margin-top: 5px; 80 | margin-right: 0; 81 | padding: 0.625em 2.5em; 82 | width: 84%; 83 | font-size: 1em; 84 | display: block; 85 | } 86 | 87 | .signup-button:hover { 88 | background-color: #438e29; 89 | color: white; 90 | transition: background-color 0.5s ease; 91 | cursor: pointer; 92 | } 93 | 94 | .signup-button { 95 | margin-left: 10px; 96 | font-size: 1.25rem; 97 | border: 1px solid #51af33; 98 | width: 100%; 99 | background: #51af33; 100 | border-radius: 4px; 101 | padding: 0.6em 4em 0.6em; 102 | text-decoration: none; 103 | color: white; 104 | margin-top: 8px; 105 | transition: background-color 0.5s ease; 106 | margin-bottom: 10px; 107 | } 108 | 109 | .error-msg { 110 | font-size: 10px; 111 | padding-top: 2px; 112 | padding-bottom: 4px; 113 | } 114 | 115 | .signup-login-link span { 116 | text-decoration: none; 117 | float: right; 118 | color: #51af33; 119 | } 120 | 121 | .signup-login-link span:hover { 122 | color: #438e29; 123 | } 124 | 125 | .error-input { 126 | border: 1px solid #f9c339 !important; 127 | } 128 | 129 | .error-msg { 130 | color: #f9c339; 131 | font-size: 10px; 132 | } 133 | 134 | .modal-button { 135 | margin-left: 0; 136 | } 137 | -------------------------------------------------------------------------------- /app/assets/stylesheets/socialicons.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'si'; 3 | src: image-url('socicon.eot'); 4 | src: image-url('socicon.eot?#iefix') format('embedded-opentype'), 5 | image-url('socicon.woff') format('woff'), 6 | image-url('socicon.ttf') format('truetype'), 7 | image-url('socicon.svg#icomoonregular') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | 11 | } 12 | 13 | @media screen and (-webkit-min-device-pixel-ratio:0) { 14 | @font-face { 15 | font-family:si; 16 | src: url(PATH_TO/socicon.svg) format(svg); 17 | } 18 | } 19 | 20 | .soc { 21 | overflow:hidden; 22 | margin:0; padding:0; 23 | list-style:none; 24 | height: 70px; 25 | } 26 | 27 | .soc li { 28 | display:inline-block; 29 | *display:inline; 30 | zoom:1; 31 | padding: 10px; 32 | } 33 | 34 | .soc li a { 35 | font-family:si!important; 36 | font-style:normal; 37 | font-weight:400; 38 | -webkit-font-smoothing:antialiased; 39 | -moz-osx-font-smoothing:grayscale; 40 | -webkit-box-sizing:border-box; 41 | -moz-box-sizing:border-box; 42 | -ms-box-sizing:border-box; 43 | -o-box-sizing:border-box; 44 | box-sizing:border-box; 45 | 46 | -o-transition:.1s; 47 | -ms-transition:.1s; 48 | -moz-transition:.1s; 49 | -webkit-transition:.1s; 50 | transition:.1s; 51 | -webkit-transition-property: transform; 52 | transition-property: transform; 53 | -webkit-transform: translateZ(0); 54 | transform: translateZ(0); 55 | 56 | overflow:hidden; 57 | text-decoration:none; 58 | text-align:center; 59 | display:block; 60 | position: relative; 61 | z-index: 1; 62 | width: 50px; 63 | height: 50px; 64 | line-height: 50px; 65 | font-size: 23px; 66 | -webkit-border-radius: 10px; 67 | -moz-border-radius: 10px; 68 | border-radius: 10px; 69 | margin-right: 25px; 70 | color: #b1b9c3; 71 | background-color: #ffffff; 72 | } 73 | 74 | .soc a:hover { 75 | z-index: 2; 76 | -webkit-transform: scale(1.1); 77 | transform: scale(1.1); 78 | } 79 | 80 | .soc-icon-last{ 81 | margin:0 !important; 82 | } 83 | 84 | .soc-github:before { 85 | content:'\e030'; 86 | } 87 | .soc-instagram:before { 88 | content:'\e057'; 89 | } 90 | .soc-linkedin:before { 91 | content:'\e049'; 92 | } 93 | -------------------------------------------------------------------------------- /app/assets/stylesheets/taskerform.scss: -------------------------------------------------------------------------------- 1 | .rate-input-container { 2 | margin: .8rem 0; 3 | display: flex; 4 | } 5 | 6 | .rate-input { 7 | border-radius: 4px; 8 | padding: 0.5em 0.75em; 9 | margin: 0 5px; 10 | border-width: 1px; 11 | font-size: 20px; 12 | } 13 | 14 | .rate-symbol { 15 | margin: 14px 0; 16 | } 17 | 18 | .fee { 19 | margin-bottom: 5rem; 20 | } 21 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/api/hired_tutors_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::HiredTutorsController < ApplicationController 2 | 3 | def index 4 | @bookings = User.find(params[:user_id]).hired_tutors 5 | 6 | render :index 7 | end 8 | 9 | def show 10 | end 11 | 12 | def create 13 | @booking = HiredTutor.create!(booking_params) 14 | end 15 | 16 | def update 17 | @booking = HiredTutor.find(params[:booking_id]) 18 | @booking.update({ completed: true }) 19 | end 20 | 21 | def booking_params 22 | params.permit(:user_id, :tutor_id, :subject_id, :rate, :description, :date, :time_period, :location) 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/api/reviews_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::ReviewsController < ApplicationController 2 | 3 | def index 4 | 5 | end 6 | 7 | def show 8 | end 9 | 10 | def create 11 | @review = Review.create!(review_params) 12 | end 13 | 14 | def review_params 15 | params.permit(:user_id, :author_id, :subject_id, :body, :positive) 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/api/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::SessionsController < ApplicationController 2 | def create 3 | @user = User.find_by_credentials( 4 | params[:user][:email], 5 | params[:user][:password] 6 | ) 7 | 8 | if @user 9 | login(@user) 10 | render "api/users/show" 11 | else 12 | render json: ["Invalid email/password combination"], status: 401 13 | end 14 | end 15 | 16 | def destroy 17 | @user = current_user 18 | if @user 19 | logout 20 | render "api/users/show" 21 | else 22 | render json: ["Nobody signed in"], status: 404 23 | end 24 | end 25 | 26 | def fblogin 27 | @user = User.find_by(email: params[:email]) 28 | 29 | if @user 30 | login(@user) 31 | render "api/users/show" 32 | else 33 | render json: ['No account linked to that Email!'], status: 401 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /app/controllers/api/subjects_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::SubjectsController < ApplicationController 2 | def index 3 | @subjects = Subject.all 4 | end 5 | 6 | def create 7 | 8 | end 9 | 10 | def show 11 | end 12 | 13 | private 14 | 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/api/tutors_for_hires_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::TutorsForHiresController < ApplicationController 2 | 3 | def index 4 | @tutors = Subject.find(params[:subject_id]).tutors_for_hire.select { |tutor| tutor[params[:ed_lvl]]} 5 | @tutors.map! do |tutor| 6 | user = User.find(tutor.user_id) 7 | { 8 | user: user, 9 | rate: tutor.rate, 10 | num_completed: tutor.num_completed, 11 | description: tutor.description, 12 | times: user.times.map { |time| time[:time] } 13 | } 14 | end 15 | render :index 16 | end 17 | 18 | def create 19 | tutor = TutorsForHire.where({user_id: params[:user_id], subject_id: params[:subject_id]}) 20 | if tutor.length > 0 21 | render json: ["You already have signed up to be a tutor for that Subject. To edit, go to your Account page"], status: 401 22 | else 23 | @tutor = TutorsForHire.create!(tutorParams) 24 | end 25 | end 26 | 27 | def show 28 | end 29 | 30 | def tutorParams 31 | params.permit(:user_id, :subject_id, :rate, :description, :first_tier, :second_tier, :third_tier) 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /app/controllers/api/users_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::UsersController < ApplicationController 2 | def new 3 | @user = User.new 4 | end 5 | 6 | def create 7 | @user = User.new(user_params) 8 | if @user.save 9 | login(@user) 10 | render :show 11 | else 12 | render json: @user.errors.full_messages, status: 422 13 | end 14 | end 15 | 16 | def update 17 | @user = User.find(params[:user][:id]) 18 | if @user.update(user_params) 19 | render :show 20 | else 21 | render json: @user.errors.full_messages, status: 422 22 | end 23 | end 24 | 25 | private 26 | def user_params 27 | params.require(:user).permit(:password, :fname, :lname, :email, :zipcode, :phone_num, :image) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | 4 | helper_method :current_user, :logged_in? 5 | 6 | private 7 | 8 | def current_user 9 | return nil unless session[:session_token] 10 | @current_user ||= User.find_by(session_token: session[:session_token]) 11 | end 12 | 13 | def logged_in? 14 | !!current_user 15 | end 16 | 17 | def login(user) 18 | user.reset_session_token! 19 | session[:session_token] = user.session_token 20 | @current_user = user 21 | end 22 | 23 | def logout 24 | current_user.reset_session_token! 25 | session[:session_token] = nil 26 | @current_user = nil 27 | end 28 | 29 | def require_logged_in 30 | unless current_user 31 | render json: { base: ['invalid credentials'] }, status: 401 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/static_pages_controller.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | def root 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/available_time.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: available_times 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # time_avl :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | class AvailableTime < ApplicationRecord 13 | validates :user_id, :time_avl, presence: true 14 | 15 | belongs_to :user 16 | belongs_to :time, 17 | foreign_key: 'time_avl', 18 | class_name: 'TimeOfDay' 19 | end 20 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/hired_tutor.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: hired_tutors 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # tutor_id :integer not null 8 | # subject_id :integer not null 9 | # rate :integer not null 10 | # description :text not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # date :string 14 | # time_period :string 15 | # location :string 16 | # completed :boolean default(FALSE) 17 | # 18 | 19 | class HiredTutor < ApplicationRecord 20 | validates :user_id, :tutor_id, :subject_id, :date, 21 | :time_period, :rate, :description, 22 | presence: true 23 | validates_inclusion_of :completed, in: [true, false] 24 | 25 | belongs_to :subject 26 | belongs_to :user 27 | belongs_to :tutor, 28 | foreign_key: 'tutor_id', 29 | class_name: 'User' 30 | 31 | end 32 | -------------------------------------------------------------------------------- /app/models/review.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: reviews 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # body :text not null 8 | # positive :boolean not null 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # author_id :integer 12 | # subject_id :integer 13 | # 14 | 15 | class Review < ApplicationRecord 16 | validates :user_id, :positive, presence: true 17 | 18 | belongs_to :user 19 | end 20 | -------------------------------------------------------------------------------- /app/models/subject.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: subjects 4 | # 5 | # id :integer not null, primary key 6 | # title :string not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # 10 | 11 | class Subject < ApplicationRecord 12 | validates :title, presence: true, uniqueness: true 13 | 14 | has_many :tutors_for_hire, 15 | foreign_key: "subject_id", 16 | class_name: 'TutorsForHire' 17 | 18 | has_many :hired_tutors 19 | 20 | end 21 | -------------------------------------------------------------------------------- /app/models/time_of_day.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: time_of_days 4 | # 5 | # id :integer not null, primary key 6 | # time :integer not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # 10 | 11 | class TimeOfDay < ApplicationRecord 12 | end 13 | -------------------------------------------------------------------------------- /app/models/tutors_for_hire.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: tutors_for_hires 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # subject_id :integer not null 8 | # num_completed :integer default(0), not null 9 | # rate :integer not null 10 | # description :text not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # first_tier :boolean not null 14 | # second_tier :boolean not null 15 | # third_tier :boolean not null 16 | # 17 | 18 | class TutorsForHire < ApplicationRecord 19 | validates :user_id, :subject_id, :num_completed, :rate, :description, presence: true 20 | validates_inclusion_of :first_tier, :second_tier, :third_tier, in: [true, false] 21 | belongs_to :user 22 | belongs_to :subject 23 | 24 | end 25 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # email :string not null 7 | # zipcode :string not null 8 | # password_digest :string not null 9 | # phone_num :string not null 10 | # session_token :string not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # fname :string not null 14 | # lname :string not null 15 | # image_file_name :string 16 | # image_content_type :string 17 | # image_file_size :integer 18 | # image_updated_at :datetime 19 | # 20 | 21 | class User < ApplicationRecord 22 | validates :fname, :lname, :email, :zipcode, :password_digest, :phone_num, :session_token, presence: true 23 | validates :email, :session_token, uniqueness: true 24 | validates :password, length: { minimum: 6, allow_nil: true} 25 | validates :email, :fname, :lname, length: { maximum: 45 } 26 | validates :zipcode, length: { is: 5 } 27 | validates :phone_num, length: { minimum: 7, maximum: 15 } 28 | 29 | has_attached_file :image, default_url: ("https://s3.us-east-2.amazonaws.com/app-taskable-pro/default.jpeg") 30 | validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/ 31 | 32 | after_initialize :ensure_session_token 33 | attr_reader :password 34 | 35 | has_many :tutors_for_hire, 36 | foreign_key: "user_id", 37 | class_name: 'TutorsForHire' 38 | 39 | has_many :reviews 40 | has_many :hired_tutors 41 | has_many :available_times 42 | has_many :times, 43 | through: :available_times, 44 | source: :time 45 | 46 | def self.find_by_credentials(email, password) 47 | user = User.find_by(email: email) 48 | return nil unless user 49 | user.is_password?(password) ? user : nil 50 | end 51 | 52 | def password=(password) 53 | @password = password 54 | self.password_digest = BCrypt::Password.create(password) 55 | end 56 | 57 | def is_password?(password) 58 | BCrypt::Password.new(self.password_digest).is_password?(password) 59 | end 60 | 61 | def reset_session_token! 62 | generate_unique_session_token 63 | save! 64 | self.session_token 65 | end 66 | 67 | private 68 | 69 | def ensure_session_token 70 | generate_unique_session_token unless self.session_token 71 | end 72 | 73 | def new_session_token 74 | SecureRandom.urlsafe_base64 75 | end 76 | 77 | def generate_unique_session_token 78 | self.session_token = new_session_token 79 | while User.find_by(session_token: self.session_token) 80 | self.session_token = new_session_token 81 | end 82 | self.session_token 83 | end 84 | 85 | end 86 | -------------------------------------------------------------------------------- /app/views/api/hired_tutors/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.array! @bookings do |booking| 2 | json.fname booking.tutor.fname 3 | json.lname booking.tutor.lname 4 | json.id booking.tutor.id 5 | json.rate booking.rate 6 | json.description booking.description 7 | json.date booking.date 8 | json.time_period booking.time_period 9 | json.location booking.location 10 | json.completed booking.completed 11 | json.image_url booking.tutor.image.url 12 | json.subject_id booking.subject_id 13 | json.booking_id booking.id 14 | end 15 | -------------------------------------------------------------------------------- /app/views/api/tutors_for_hires/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | 2 | json.array! @tutors do |tutor| 3 | json.id tutor[:user][:id] 4 | json.fname tutor[:user][:fname] 5 | json.lname tutor[:user][:lname] 6 | json.rate tutor[:rate] 7 | json.description tutor[:description] 8 | json.num_completed tutor[:num_completed] 9 | json.times tutor[:times] 10 | json.image_url asset_path(tutor[:user].image.url) 11 | end 12 | -------------------------------------------------------------------------------- /app/views/api/users/_user.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! user, :id, :fname, :lname, :email, :zipcode, :phone_num 2 | json.image_url asset_path(user.image.url) 3 | -------------------------------------------------------------------------------- /app/views/api/users/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.partial! "api/users/user", user: @user 2 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Taskable 5 | 6 | <%= csrf_meta_tags %> 7 | <%= favicon_link_tag 'favicon.ico' %> 8 | 10 | <%= stylesheet_link_tag 'application', media: 'all' %> 11 | <%= javascript_include_tag 'application' %> 12 | 13 | 14 | 15 | 16 | <%= yield %> 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/static_pages/root.html.erb: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # Install JavaScript dependencies if using Yarn 22 | # system('bin/yarn') 23 | 24 | 25 | # puts "\n== Copying sample files ==" 26 | # unless File.exist?('config/database.yml') 27 | # cp 'config/database.yml.sample', 'config/database.yml' 28 | # end 29 | 30 | puts "\n== Preparing database ==" 31 | system! 'bin/rails db:setup' 32 | 33 | puts "\n== Removing old logs and tempfiles ==" 34 | system! 'bin/rails log:clear tmp:clear' 35 | 36 | puts "\n== Restarting application server ==" 37 | system! 'bin/rails restart' 38 | end 39 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | VENDOR_PATH = File.expand_path('..', __dir__) 3 | Dir.chdir(VENDOR_PATH) do 4 | begin 5 | exec "yarnpkg #{ARGV.join(" ")}" 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module Taskable 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.1 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | 18 | 19 | config.paperclip_defaults = { 20 | :storage => :s3, 21 | s3_protocol: :https, 22 | :s3_credentials => { 23 | bucket: ENV["s3_bucket"], 24 | access_key_id: ENV["s3_access_key_id"], 25 | secret_access_key: ENV["s3_secret_access_key"], 26 | s3_host_name: "s3-#{ENV['s3_region']}.amazonaws.com", 27 | s3_region: ENV["s3_region"], 28 | :url => ":s3_host_name" 29 | } 30 | } 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | channel_prefix: Taskable_production 11 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 9.1 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see Rails configuration guide 21 | # http://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 23 | 24 | development: 25 | <<: *default 26 | database: Taskable_development 27 | 28 | # The specified database role being used to connect to postgres. 29 | # To create additional roles in postgres see `$ createuser --help`. 30 | # When left blank, postgres will use the default role. This is 31 | # the same name as the operating system user that initialized the database. 32 | #username: Taskable 33 | 34 | # The password associated with the postgres role (username). 35 | #password: 36 | 37 | # Connect on a TCP socket. Omitted by default since the client uses a 38 | # domain socket that doesn't need configuration. Windows does not have 39 | # domain sockets, so uncomment these lines. 40 | #host: localhost 41 | 42 | # The TCP port the server listens on. Defaults to 5432. 43 | # If your server runs on a different port number, change accordingly. 44 | #port: 5432 45 | 46 | # Schema search path. The server defaults to $user,public 47 | #schema_search_path: myapp,sharedapp,public 48 | 49 | # Minimum log levels, in increasing order: 50 | # debug5, debug4, debug3, debug2, debug1, 51 | # log, notice, warning, error, fatal, and panic 52 | # Defaults to warning. 53 | #min_messages: notice 54 | 55 | # Warning: The database defined as "test" will be erased and 56 | # re-generated from your development database when you run "rake". 57 | # Do not set this db to the same as development or production. 58 | test: 59 | <<: *default 60 | database: Taskable_test 61 | 62 | # As with config/secrets.yml, you never want to store sensitive information, 63 | # like your database password, in your source code. If your source code is 64 | # ever seen by anyone, they now have access to your database. 65 | # 66 | # Instead, provide the password as a unix environment variable when you boot 67 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 68 | # for a full rundown on how to provide these environment variables in a 69 | # production deployment. 70 | # 71 | # On Heroku and other platform providers, you may have a full connection URL 72 | # available as an environment variable. For example: 73 | # 74 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" 75 | # 76 | # You can use this database configuration with: 77 | # 78 | # production: 79 | # url: <%= ENV['DATABASE_URL'] %> 80 | # 81 | production: 82 | <<: *default 83 | database: Taskable_production 84 | username: Taskable 85 | password: <%= ENV['TASKABLE_DATABASE_PASSWORD'] %> 86 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | # Debug mode disables concatenation and preprocessing of assets. 41 | # This option may cause significant delays in view rendering with a large 42 | # number of complex assets. 43 | config.assets.debug = true 44 | 45 | # Suppress logger output for asset requests. 46 | config.assets.quiet = true 47 | 48 | # Raises error for missing translations 49 | # config.action_view.raise_on_missing_translations = true 50 | 51 | # Use an evented file watcher to asynchronously detect changes in source code, 52 | # routes, locales, etc. This feature depends on the listen gem. 53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 54 | end 55 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`. 18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or 19 | # `config/secrets.yml.key`. 20 | config.read_encrypted_secrets = true 21 | 22 | # Disable serving static files from the `/public` folder by default since 23 | # Apache or NGINX already handles this. 24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 25 | 26 | # Compress JavaScripts and CSS. 27 | config.assets.js_compressor = :uglifier 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 34 | 35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 36 | # config.action_controller.asset_host = 'http://assets.example.com' 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 41 | 42 | # Mount Action Cable outside main process or domain 43 | # config.action_cable.mount_path = nil 44 | # config.action_cable.url = 'wss://example.com/cable' 45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 46 | 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 48 | # config.force_ssl = true 49 | 50 | # Use the lowest log level to ensure availability of diagnostic information 51 | # when problems arise. 52 | config.log_level = :debug 53 | 54 | # Prepend all log lines with the following tags. 55 | config.log_tags = [ :request_id ] 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Use a real queuing backend for Active Job (and separate queues per environment) 61 | # config.active_job.queue_adapter = :resque 62 | # config.active_job.queue_name_prefix = "Taskable_#{Rails.env}" 63 | config.action_mailer.perform_caching = false 64 | 65 | # Ignore bad email addresses and do not raise email delivery errors. 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 67 | # config.action_mailer.raise_delivery_errors = false 68 | 69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 70 | # the I18n.default_locale when a translation cannot be found). 71 | config.i18n.fallbacks = true 72 | 73 | # Send deprecation notices to registered listeners. 74 | config.active_support.deprecation = :notify 75 | 76 | # Use default logging formatter so that PID and timestamp are not suppressed. 77 | config.log_formatter = ::Logger::Formatter.new 78 | 79 | # Use a different logger for distributed setups. 80 | # require 'syslog/logger' 81 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 82 | 83 | if ENV["RAILS_LOG_TO_STDOUT"].present? 84 | logger = ActiveSupport::Logger.new(STDOUT) 85 | logger.formatter = config.log_formatter 86 | config.logger = ActiveSupport::TaggedLogging.new(logger) 87 | end 88 | 89 | # Do not dump schema after migrations. 90 | config.active_record.dump_schema_after_migration = false 91 | end 92 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # If you are preloading your application and using Active Record, it's 36 | # recommended that you close any connections to the database before workers 37 | # are forked to prevent connection leakage. 38 | # 39 | # before_fork do 40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) 41 | # end 42 | 43 | # The code in the `on_worker_boot` will be called if you are using 44 | # clustered mode by specifying a number of `workers`. After each worker 45 | # process is booted, this block will be run. If you are using the `preload_app!` 46 | # option, you will want to use this block to reconnect to any threads 47 | # or connections that may have been created at application boot, as Ruby 48 | # cannot share connections between processes. 49 | # 50 | # on_worker_boot do 51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 52 | # end 53 | # 54 | 55 | # Allow puma to be restarted by `rails restart` command. 56 | plugin :tmp_restart 57 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 3 | root "static_pages#root" 4 | 5 | namespace :api, defaults: { format: :json } do 6 | resources :users, only: [:create, :show, :update, :destroy] do 7 | resources :hired_tutors, only: [:index, :show, :create, :update] 8 | resources :reviews, only: [:index, :show, :create] 9 | resources :available_times, only: [:index, :create] 10 | end 11 | resource :session, only: [:create, :destroy, :show] 12 | post 'fbsession', to: 'sessions#fblogin' 13 | resources :tutors_for_hires, only: [:index, :show, :create, :destroy] 14 | resources :subjects, only: [:index, :show] do 15 | resources :tutors_for_hires, only: [:index, :show, :create] 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | # Shared secrets are available across all environments. 14 | 15 | # shared: 16 | # api_key: a1B2c3D4e5F6 17 | 18 | # Environmental secrets are only available for that specific environment. 19 | 20 | development: 21 | secret_key_base: 1010d89ced40294e09b97e1e03ca00e8019f20be67269d6a3c8c57f730475ad528bf3ddafa280276c23060e36e7c9b872b9dacc1f9545e31d99f641a7b4ff9f7 22 | 23 | test: 24 | secret_key_base: 7e7a941af5fbdd24c40eb2db7a634df18bd59124080052add3b23c3284a83d42e2ea7dd3a76dc774ca42659599959218aad843702c278f313f40b0b075948dfe 25 | 26 | # Do not keep production secrets in the unencrypted secrets file. 27 | # Instead, either read values from the environment. 28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 29 | # and move the `production:` environment over there. 30 | 31 | production: 32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 33 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /db/migrate/20171024150133_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :users do |t| 4 | t.string :name, null: false 5 | t.string :email, null: false 6 | t.string :zipcode, null: false 7 | t.string :password_digest, null: false 8 | t.string :phone_num, null: false 9 | t.string :session_token, null: false 10 | 11 | t.timestamps 12 | end 13 | add_index :users, :name 14 | add_index :users, :email 15 | add_index :users, :session_token 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20171024150536_deleteusers.rb: -------------------------------------------------------------------------------- 1 | class Deleteusers < ActiveRecord::Migration[5.1] 2 | def change 3 | drop_table :users 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20171024150642_addnewusertable.rb: -------------------------------------------------------------------------------- 1 | class Addnewusertable < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :users do |t| 4 | t.string :name, null: false 5 | t.string :email, null: false, uniqueness: true 6 | t.string :zipcode, null: false 7 | t.string :password_digest, null: false 8 | t.string :phone_num, null: false 9 | t.string :session_token, null: false, uniqueness: true 10 | 11 | t.timestamps 12 | end 13 | add_index :users, :name 14 | add_index :users, :email 15 | add_index :users, :session_token 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20171024151058_deletenewusers.rb: -------------------------------------------------------------------------------- 1 | class Deletenewusers < ActiveRecord::Migration[5.1] 2 | def change 3 | drop_table :users 4 | 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20171024152237_newusers.rb: -------------------------------------------------------------------------------- 1 | class Newusers < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :users do |t| 4 | t.string :name, null: false 5 | t.string :email, null: false 6 | t.string :zipcode, null: false 7 | t.string :password_digest, null: false 8 | t.string :phone_num, null: false 9 | t.string :session_token, null: false 10 | 11 | t.timestamps 12 | end 13 | add_index :users, :name 14 | add_index :users, :email, unique: true 15 | add_index :users, :session_token, unique: true 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20171024170632_changenames.rb: -------------------------------------------------------------------------------- 1 | class Changenames < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_column :users, :name, :string 4 | add_column :users, :fname, :string, null: false 5 | add_column :users, :lname, :string, null: false 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20171024212555_create_subjects.rb: -------------------------------------------------------------------------------- 1 | class CreateSubjects < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :subjects do |t| 4 | t.string :title, null: false 5 | 6 | t.timestamps 7 | end 8 | add_index :subjects, :title 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20171024212841_create_tutors_for_hires.rb: -------------------------------------------------------------------------------- 1 | class CreateTutorsForHires < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :tutors_for_hires do |t| 4 | t.integer :user_id, null: false 5 | t.integer :subject_id, null: false 6 | t.integer :num_completed, null: false, default: 0 7 | t.integer :level, null: false 8 | t.integer :rate, null: false 9 | t.text :description, null: false 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20171024214306_create_available_times.rb: -------------------------------------------------------------------------------- 1 | class CreateAvailableTimes < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :available_times do |t| 4 | t.integer :user_id, null: false 5 | t.integer :time_avl, null: false 6 | 7 | t.timestamps 8 | end 9 | add_index :available_times, :user_id 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20171024214544_create_hired_tutors.rb: -------------------------------------------------------------------------------- 1 | class CreateHiredTutors < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :hired_tutors do |t| 4 | t.integer :user_id, null: false 5 | t.integer :tutor_id, null: false 6 | t.integer :subject_id, null: false 7 | t.date :date, null: false 8 | t.integer :time_period, null: false 9 | t.integer :rate, null: false 10 | t.boolean :completed, null: false, default: false 11 | t.text :description, null: false 12 | 13 | t.timestamps 14 | end 15 | add_index :hired_tutors, :user_id 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20171024214708_create_reviews.rb: -------------------------------------------------------------------------------- 1 | class CreateReviews < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :reviews do |t| 4 | t.integer :user_id, null: false 5 | t.text :body, null: false 6 | t.boolean :positive, null: false 7 | 8 | t.timestamps 9 | end 10 | add_index :reviews, :user_id 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20171024234541_deletethingsfromtutorstable.rb: -------------------------------------------------------------------------------- 1 | class Deletethingsfromtutorstable < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_column :tutors_for_hires, :level, :integer 4 | add_column :tutors_for_hires, :first_tier, :boolean 5 | add_column :tutors_for_hires, :second_tier, :boolean 6 | add_column :tutors_for_hires, :third_tier, :boolean 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20171024234943_updatebooleans.rb: -------------------------------------------------------------------------------- 1 | class Updatebooleans < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_column :tutors_for_hires, :first_tier, :boolean 4 | remove_column :tutors_for_hires, :second_tier, :boolean 5 | remove_column :tutors_for_hires, :third_tier, :boolean 6 | add_column :tutors_for_hires, :first_tier, :boolean, null: false 7 | add_column :tutors_for_hires, :second_tier, :boolean, null: false 8 | add_column :tutors_for_hires, :third_tier, :boolean, null: false 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20171027131139_add_attachment_image_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAttachmentImageToUsers < ActiveRecord::Migration[5.1] 2 | def self.up 3 | change_table :users do |t| 4 | t.attachment :image 5 | end 6 | end 7 | 8 | def self.down 9 | remove_attachment :users, :image 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20171027193700_create_time_of_days.rb: -------------------------------------------------------------------------------- 1 | class CreateTimeOfDays < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :time_of_days do |t| 4 | t.integer :time, null: false 5 | t.timestamps 6 | end 7 | add_index :time_of_days, :time 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20171029231855_changehiredtutors.rb: -------------------------------------------------------------------------------- 1 | class Changehiredtutors < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_column :hired_tutors, :date 4 | remove_column :hired_tutors, :time_period 5 | add_column :hired_tutors, :date, :string 6 | add_column :hired_tutors, :time_period, :string 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20171029235356_edit_hiredtutor.rb: -------------------------------------------------------------------------------- 1 | class EditHiredtutor < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_column :hired_tutors, :completed 4 | add_column :hired_tutors, :completed, :boolean 5 | add_column :hired_tutors, :location, :string 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20171029235954_add_default_completed.rb: -------------------------------------------------------------------------------- 1 | class AddDefaultCompleted < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_column :hired_tutors, :completed 4 | add_column :hired_tutors, :completed, :boolean, default: false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20171101134706_addsubjecttoreview.rb: -------------------------------------------------------------------------------- 1 | class Addsubjecttoreview < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :reviews, :subject_id, :integer 4 | add_column :reviews, :author_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20171101134706) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "available_times", force: :cascade do |t| 19 | t.integer "user_id", null: false 20 | t.integer "time_avl", null: false 21 | t.datetime "created_at", null: false 22 | t.datetime "updated_at", null: false 23 | t.index ["user_id"], name: "index_available_times_on_user_id" 24 | end 25 | 26 | create_table "hired_tutors", force: :cascade do |t| 27 | t.integer "user_id", null: false 28 | t.integer "tutor_id", null: false 29 | t.integer "subject_id", null: false 30 | t.integer "rate", null: false 31 | t.text "description", null: false 32 | t.datetime "created_at", null: false 33 | t.datetime "updated_at", null: false 34 | t.string "date" 35 | t.string "time_period" 36 | t.string "location" 37 | t.boolean "completed", default: false 38 | t.index ["user_id"], name: "index_hired_tutors_on_user_id" 39 | end 40 | 41 | create_table "reviews", force: :cascade do |t| 42 | t.integer "user_id", null: false 43 | t.text "body", null: false 44 | t.boolean "positive", null: false 45 | t.datetime "created_at", null: false 46 | t.datetime "updated_at", null: false 47 | t.integer "subject_id" 48 | t.integer "author_id" 49 | t.index ["user_id"], name: "index_reviews_on_user_id" 50 | end 51 | 52 | create_table "subjects", force: :cascade do |t| 53 | t.string "title", null: false 54 | t.datetime "created_at", null: false 55 | t.datetime "updated_at", null: false 56 | t.index ["title"], name: "index_subjects_on_title" 57 | end 58 | 59 | create_table "time_of_days", force: :cascade do |t| 60 | t.integer "time", null: false 61 | t.datetime "created_at", null: false 62 | t.datetime "updated_at", null: false 63 | t.index ["time"], name: "index_time_of_days_on_time" 64 | end 65 | 66 | create_table "tutors_for_hires", force: :cascade do |t| 67 | t.integer "user_id", null: false 68 | t.integer "subject_id", null: false 69 | t.integer "num_completed", default: 0, null: false 70 | t.integer "rate", null: false 71 | t.text "description", null: false 72 | t.datetime "created_at", null: false 73 | t.datetime "updated_at", null: false 74 | t.boolean "first_tier", null: false 75 | t.boolean "second_tier", null: false 76 | t.boolean "third_tier", null: false 77 | end 78 | 79 | create_table "users", force: :cascade do |t| 80 | t.string "email", null: false 81 | t.string "zipcode", null: false 82 | t.string "password_digest", null: false 83 | t.string "phone_num", null: false 84 | t.string "session_token", null: false 85 | t.datetime "created_at", null: false 86 | t.datetime "updated_at", null: false 87 | t.string "fname", null: false 88 | t.string "lname", null: false 89 | t.string "image_file_name" 90 | t.string "image_content_type" 91 | t.integer "image_file_size" 92 | t.datetime "image_updated_at" 93 | t.index ["email"], name: "index_users_on_email", unique: true 94 | t.index ["session_token"], name: "index_users_on_session_token", unique: true 95 | end 96 | 97 | end 98 | -------------------------------------------------------------------------------- /frontend/actions/session_actions.js: -------------------------------------------------------------------------------- 1 | import * as SessionApiUtil from '../util/session_api_util'; 2 | 3 | export const RECEIVE_CURRENT_USER = 'RECEIVE_CURRENT_USER'; 4 | export const RECEIVE_SESSION_ERRORS = 'RECEIVE_SESSION_ERRORS'; 5 | export const RECEIVE_AVL_TIMES = 'RECEIVE_AVL_TIMES'; 6 | export const CLEAR_ERRORS = 'CLEAR_ERRORS' 7 | 8 | 9 | export const receiveCurrentUser = current_user => { 10 | return ({ 11 | type: RECEIVE_CURRENT_USER, 12 | current_user 13 | }); 14 | }; 15 | 16 | export const receiveErrors = errors => { 17 | return ({ 18 | type: RECEIVE_SESSION_ERRORS, 19 | errors 20 | }); 21 | }; 22 | 23 | export const clearSessionErrors = () => { 24 | return ({ 25 | type: CLEAR_ERRORS 26 | }); 27 | }; 28 | 29 | export const receiveAvlTimes = avlTimes => { 30 | return ({ 31 | type: RECEIVE_AVL_TIMES, 32 | avlTimes 33 | }); 34 | }; 35 | 36 | export const createAvlTimes = (avlTimes) => dispatch => { 37 | return SessionApiUtil.createAvlTimes(avlTimes).then( 38 | (times) => dispatch(receiveAvlTimes(times)) 39 | ); 40 | }; 41 | 42 | export const updateUser = (formData, userId) => dispatch => { 43 | return SessionApiUtil.updateUser(formData, userId).then( 44 | (user) => dispatch(receiveCurrentUser(user)) 45 | ); 46 | }; 47 | 48 | export const signup = user => dispatch => { 49 | return SessionApiUtil.signup(user).then( 50 | (user) => dispatch(receiveCurrentUser(user)), 51 | (errors) => dispatch(receiveErrors(errors))); 52 | }; 53 | 54 | 55 | export const login = user => dispatch => { 56 | return SessionApiUtil.login(user).then( 57 | (user) => dispatch(receiveCurrentUser(user)), 58 | (errors) => dispatch(receiveErrors(errors))); 59 | }; 60 | 61 | export const logout = () => dispatch => { 62 | return SessionApiUtil.logout().then( 63 | () => dispatch(receiveCurrentUser(null)), 64 | (errors) => dispatch(receiveErrors(errors))); 65 | }; 66 | 67 | export const fblogin = email => dispatch => { 68 | return SessionApiUtil.fblogin(email).then( 69 | (user) => dispatch(receiveCurrentUser(user)), 70 | (errors) => dispatch(receiveErrors(errors))); 71 | }; 72 | 73 | export const clearErrors = () => dispatch => { 74 | return dispatch(clearSessionErrors()); 75 | } 76 | -------------------------------------------------------------------------------- /frontend/actions/tutor_actions.js: -------------------------------------------------------------------------------- 1 | import * as TutorApiUtil from '../util/tutor_api_util'; 2 | 3 | export const RECEIVE_TUTORS = 'RECEIVE_TUTORS'; 4 | export const RECEIVE_TUTOR = 'RECEIVE_TUTOR'; 5 | export const RECEIVE_BOOKING = 'RECEIVE_BOOKING'; 6 | export const RECEIVE_HIRED_TUTORS = 'RECEIVE_HIRED_TUTORS'; 7 | export const UPDATE_BOOKING = 'UPDATE_BOOKING'; 8 | 9 | export const receiveTutors = (tutors, params) => { 10 | return ({ 11 | type: RECEIVE_TUTORS, 12 | tutors, 13 | params 14 | }); 15 | }; 16 | 17 | export const receiveTutor = (tutor) => { 18 | return ({ 19 | type: RECEIVE_TUTOR, 20 | tutor 21 | }); 22 | }; 23 | 24 | export const receiveBooking = booking => { 25 | return ({ 26 | type: RECEIVE_BOOKING, 27 | booking 28 | }); 29 | }; 30 | 31 | export const receiveHiredTutors = hiredTutors => { 32 | return ({ 33 | type: RECEIVE_HIRED_TUTORS, 34 | hiredTutors 35 | }); 36 | }; 37 | 38 | export const fetchTutors = (params) => dispatch => { 39 | return TutorApiUtil.fetchTutors(params).then( 40 | tutors => dispatch(receiveTutors(tutors, params)) 41 | ); 42 | }; 43 | 44 | export const createBooking = (params) => dispatch => { 45 | return TutorApiUtil.createBooking(params); 46 | }; 47 | 48 | export const fetchHiredTutors = (userId) => dispatch => { 49 | return TutorApiUtil.fetchHiredTutors(userId).then( 50 | hiredTutors => dispatch(receiveHiredTutors(hiredTutors)) 51 | ); 52 | }; 53 | 54 | export const createReview = (params) => dispatch => { 55 | return TutorApiUtil.createReview(params); 56 | } 57 | 58 | export const updateBooking = params => dispatch => { 59 | return TutorApiUtil.updateBooking(params); 60 | } 61 | 62 | export const createTutor = params => dispatch => { 63 | return TutorApiUtil.createTutor(params); 64 | } 65 | -------------------------------------------------------------------------------- /frontend/actions/ui_actions.js: -------------------------------------------------------------------------------- 1 | export const RECEIVE_CURRENT_SUBJECT = "RECEIVE_CURRENT_SUBJECT"; 2 | export const UPDATE_FORM = "UPDATE_FORM"; 3 | 4 | export const receiveCurrentSubject = currentSubject => { 5 | return ({ 6 | type: RECEIVE_CURRENT_SUBJECT, 7 | currentSubject 8 | }); 9 | }; 10 | 11 | export const receiveForm = form => { 12 | return ({ 13 | type: UPDATE_FORM, 14 | form 15 | }); 16 | }; 17 | 18 | export const updateSubject = subject => dispatch => { 19 | return dispatch(receiveCurrentSubject(subject)); 20 | }; 21 | 22 | export const updateForm = form => dispatch => { 23 | return dispatch(receiveForm(form)); 24 | }; 25 | -------------------------------------------------------------------------------- /frontend/components/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import LoginFormContainer from './login_form/login_form_container'; 3 | import SignupFormContainer from './signup_form/signup_form_container'; 4 | import { Route } from 'react-router-dom'; 5 | import { Switch } from 'react-router'; 6 | import { AuthRoute, ProtectedRoute } from '../util/auth_route'; 7 | import GreetingContainer from './greeting/greeting_container'; 8 | import HomepageContainer from './homepage/homepage_container'; 9 | import TutorFormContainer from './tutor_form/tutor_form_container'; 10 | import DashboardContainer from './dashboard/dashboard_container'; 11 | import TutorShowContainer from './tutor_form/tutor_show_container'; 12 | import TutorConfirmContainer from './tutor_form/tutor_confirm_container'; 13 | import AccountContainer from './account/account_container'; 14 | import TaskerFormContainer from './becometasker/tasker_form_container'; 15 | 16 | const App = () => ( 17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | ); 34 | 35 | export default App; 36 | -------------------------------------------------------------------------------- /frontend/components/account/account.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | import React from 'react'; 3 | import { Route } from 'react-router-dom'; 4 | import { Switch } from 'react-router'; 5 | import AccountInfoContainer from './account_info_container'; 6 | import AccountEditFormContainer from './account_edit_form_container'; 7 | import AccountHeader from '../shared/header' 8 | 9 | class Account extends React.Component{ 10 | 11 | 12 | render() { 13 | return ( 14 |
15 | 16 | 17 | 18 |
19 |

20 | Your Account 21 |

22 |
23 |
24 |
25 |
26 |
    27 |
  • Profile
  • 28 |
  • Become a Tasker
  • 29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 |
37 | 38 |
39 |
40 |
41 | ); 42 | } 43 | } 44 | 45 | 46 | 47 | export default Account; 48 | -------------------------------------------------------------------------------- /frontend/components/account/account_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import Account from './account'; 3 | import { logout } from '../../actions/session_actions'; 4 | 5 | 6 | const mapStateToProps = (state) => { 7 | return { currentUser: state.session.currentUser }; 8 | }; 9 | 10 | const mapDispatchToProps = (dispatch) => { 11 | return ({ 12 | logout: () => dispatch(logout()), 13 | }); 14 | }; 15 | 16 | export default connect( 17 | mapStateToProps, 18 | mapDispatchToProps 19 | )(Account); 20 | -------------------------------------------------------------------------------- /frontend/components/account/account_edit_form.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | import React from 'react'; 3 | 4 | class AccountEditForm extends React.Component { 5 | constructor(props){ 6 | super(props); 7 | let user = props.currentUser; 8 | this.state = { 9 | email: user.email, 10 | fname: user.fname, 11 | lname: user.lname, 12 | zipcode: user.zipcode, 13 | phone_num: user.phone_num, 14 | imageFile: null, 15 | imageUrl: user.image_url 16 | }; 17 | this.handleChange = this.handleChange.bind(this); 18 | this.handleSubmit = this.handleSubmit.bind(this); 19 | this.updateFile = this.updateFile.bind(this); 20 | } 21 | 22 | handleChange(field) { 23 | return (e) => { 24 | this.setState({ [field]: e.target.value }); 25 | }; 26 | } 27 | 28 | handleSubmit(e) { 29 | const file = this.state.imageFile; 30 | const userId = this.props.currentUser.id; 31 | e.preventDefault(); 32 | const user = Object.assign({}, this.state); 33 | const formData = new FormData(); 34 | formData.append("user[fname]", this.state.fname); 35 | formData.append("user[lname]", this.state.lname); 36 | formData.append("user[zipcode]", this.state.zipcode); 37 | formData.append("user[email]", this.state.email); 38 | formData.append("user[phone_num]", this.state.phone_num); 39 | formData.append("user[id]", userId); 40 | if (file) formData.append("user[image]", file); 41 | this.props.updateUser(formData, userId).then(() => this.props.history.push('/account')); 42 | } 43 | 44 | updateFile(e) { 45 | var file = e.currentTarget.files[0]; 46 | var fileReader = new FileReader(); 47 | fileReader.onloadend = () => { 48 | this.setState({ imageFile: file, imageUrl: fileReader.result }); 49 | }; 50 | 51 | if (file) { 52 | fileReader.readAsDataURL(file); 53 | } 54 | } 55 | 56 | render() { 57 | let user = this.props.currentUser; 58 | 59 | return ( 60 |
61 |
62 |
63 | Account 64 |
65 | Cancel 66 |
67 |
68 |
69 | 70 | 71 | Upload a new photo 72 |
73 |
74 |
75 |
76 |
77 | 78 | 79 |
80 |
81 | 82 | 83 |
84 |
85 | 86 | 87 |
88 |
89 | 90 | 91 |
92 |
93 | 94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
102 | ); 103 | } 104 | } 105 | 106 | export default AccountEditForm; 107 | -------------------------------------------------------------------------------- /frontend/components/account/account_edit_form_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import AccountEditForm from './account_edit_form'; 3 | import { updateUser } from '../../actions/session_actions'; 4 | 5 | const mapStateToProps = (state) => { 6 | return { currentUser: state.session.currentUser }; 7 | }; 8 | 9 | const mapDispatchToProps = dispatch => { 10 | return { updateUser: (params) => dispatch(updateUser(params)) }; 11 | }; 12 | 13 | export default connect( 14 | mapStateToProps, 15 | mapDispatchToProps 16 | )(AccountEditForm); 17 | -------------------------------------------------------------------------------- /frontend/components/account/account_edit_form_container.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/frontend/components/account/account_edit_form_container.jsx -------------------------------------------------------------------------------- /frontend/components/account/account_info.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | import React from 'react'; 3 | 4 | class AccountInfo extends React.Component { 5 | 6 | 7 | render() { 8 | let user = this.props.currentUser; 9 | 10 | return ( 11 |
12 |
13 |
14 | Account 15 |
16 | Edit 17 |
18 |
19 |
20 | 21 |
22 |
23 |
    24 |
  • 25 | person 26 | {user.fname.charAt(0).toUpperCase() + user.fname.slice(1)} {user.lname.slice(0,1).toUpperCase() + user.lname.slice(1)} 27 |
  • 28 |
  • 29 | mail_outline 30 | {user.email} 31 |
  • 32 |
  • 33 | phone 34 | {user.phone_num} 35 |
  • 36 |
  • 37 | location_on 38 | {user.zipcode} 39 |
  • 40 |
41 |
42 |
43 |
44 | ); 45 | } 46 | } 47 | 48 | export default AccountInfo; 49 | -------------------------------------------------------------------------------- /frontend/components/account/account_info_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import AccountInfo from './account_info'; 3 | 4 | const mapStateToProps = (state) => { 5 | return { currentUser: state.session.currentUser }; 6 | }; 7 | 8 | export default connect( 9 | mapStateToProps, 10 | null 11 | )(AccountInfo); 12 | -------------------------------------------------------------------------------- /frontend/components/becometasker/tasker_form_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import TaskerForm from './tasker_form'; 3 | import { createTutor } from '../../actions/tutor_actions'; 4 | import { logout } from '../../actions/session_actions'; 5 | 6 | 7 | const mapStateToProps = (state) => { 8 | return { currentUser: state.session.currentUser }; 9 | }; 10 | 11 | const mapDispatchToProps = (dispatch) => { 12 | return ({ 13 | logout: () => dispatch(logout()), 14 | createTutor: (params) => dispatch(createTutor(params)) 15 | }); 16 | }; 17 | 18 | export default connect( 19 | mapStateToProps, 20 | mapDispatchToProps 21 | )(TaskerForm); 22 | -------------------------------------------------------------------------------- /frontend/components/dashboard/booking_item.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class BookingItem extends React.Component { 4 | 5 | 6 | 7 | render() { 8 | let booking = this.props.booking; 9 | return ( 10 |
11 |
12 |
13 | 14 |
{booking.fname.charAt(0).toUpperCase() + booking.fname.slice(1)} {booking.lname.slice(0,1).toUpperCase()}.
15 |
${booking.rate}/hr
16 |
17 |
18 |
19 | {booking.date} 20 | {booking.time_period} 21 |
22 |
23 | 24 | {booking.location} 25 |
26 |
27 |
28 | 29 | {booking.description} 30 |
31 |
32 | 37 |
38 |
39 |
40 | ); 41 | } 42 | } 43 | 44 | export default BookingItem; 45 | -------------------------------------------------------------------------------- /frontend/components/dashboard/complete_booking.jsx: -------------------------------------------------------------------------------- 1 | import Modal from 'react-modal'; 2 | import React from 'react'; 3 | import { withRouter } from 'react-router-dom'; 4 | 5 | class CompleteBooking extends React.Component { 6 | 7 | constructor(props){ 8 | super(props); 9 | this.handleSubmit = this.handleSubmit.bind(this); 10 | this.handleChange = this.handleChange.bind(this); 11 | this.state = { 12 | body: "", 13 | positive: true, 14 | author_id: props.author 15 | }; 16 | } 17 | 18 | handleChange(field) { 19 | return (e) => { 20 | this.setState({ [field]: e.target.value }); 21 | }; 22 | } 23 | 24 | handleSubmit(e){ 25 | e.preventDefault(); 26 | let params = { 27 | user_id: parseInt(localStorage.getItem('reviewUserId')), 28 | author_id: this.state.author_id, 29 | positive: this.state.positive, 30 | body: this.state.body, 31 | subject_id: parseInt(localStorage.getItem('reviewSubjectId')), 32 | booking_id: parseInt(localStorage.getItem('bookingId')) 33 | } 34 | this.props.updateBooking(params).then(this.props.createReview(params).then(()=>this.props.history.push('/'))); 35 | } 36 | 37 | render() { 38 | const customStyles = { 39 | overlay : { 40 | position : 'fixed', 41 | top : 0, 42 | left : 0, 43 | right : 0, 44 | bottom : 0, 45 | background: 'rgba(0, 0, 0, 0.5)' 46 | }, 47 | content : { 48 | top : '50%', 49 | left : '50%', 50 | right : 'auto', 51 | bottom : 'auto', 52 | marginRight : '-50%', 53 | transform : 'translate(-50%, -50%)', 54 | width: '475px', 55 | height: '400px', 56 | borderRadius : '10px' 57 | } 58 | }; 59 | 60 | let booking = this.props.booking; 61 | return ( 62 | 68 |
Please leave a review.
69 |
70 |
71 | 72 | 73 |
74 | 86 | 87 |
88 |
89 | ); 90 | } 91 | } 92 | 93 | export default withRouter(CompleteBooking); 94 | -------------------------------------------------------------------------------- /frontend/components/dashboard/complete_booking_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import CompleteBooking from './complete_booking'; 3 | import { createReview, updateBooking } from '../../actions/tutor_actions'; 4 | 5 | 6 | const mapStateToProps = (state) => { 7 | return { currentUser: state.session.currentUser }; 8 | }; 9 | 10 | const mapDispatchToProps = (dispatch) => { 11 | return ({ 12 | createReview: (userId) => dispatch(createReview(userId)), 13 | updateBooking: (bookingId) => dispatch(updateBooking(bookingId)) 14 | }); 15 | }; 16 | 17 | export default connect( 18 | mapStateToProps, 19 | mapDispatchToProps 20 | )(CompleteBooking); 21 | -------------------------------------------------------------------------------- /frontend/components/dashboard/dashboard_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import Dashboard from './dashboard'; 3 | import { logout } from '../../actions/session_actions'; 4 | import { updateSubject } from '../../actions/ui_actions'; 5 | import { fetchHiredTutors } from '../../actions/tutor_actions'; 6 | 7 | 8 | const mapStateToProps = (state) => { 9 | return { currentUser: state.session.currentUser, hiredTutors: state.tutors.hiredTutors }; 10 | }; 11 | 12 | const mapDispatchToProps = (dispatch) => { 13 | return ({ 14 | logout: () => dispatch(logout()), 15 | updateSubject: id => dispatch(updateSubject(id)), 16 | fetchHiredTutors: (userId) => dispatch(fetchHiredTutors(userId)) 17 | }); 18 | }; 19 | 20 | export default connect( 21 | mapStateToProps, 22 | mapDispatchToProps 23 | )(Dashboard); 24 | -------------------------------------------------------------------------------- /frontend/components/dashboard/how_to_get_started.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class HowToGetStarted extends React.Component { 4 | 5 | render(){ 6 | return( 7 |
8 |

How to Get Started

9 |

We're excited to help! Here's how it works:

10 |
11 |
12 |
1
13 |
14 |
15 |

16 | Pick a Subject 17 |

18 |

19 | Choose from a list of popular subjects and areas of education 20 |

21 |
22 |
23 |
24 |
25 |
2
26 |
27 |
28 |

29 | Get Matched 30 |

31 |

32 | We'll connect you with a skilled Tutor within minutes of your request 33 |

34 |
35 |
36 |
37 |
38 |
3
39 |
40 |
41 |

42 | Get Help Now! 43 |

44 |

45 | Choose when and where to meet with your selected tutor 46 |

47 |
48 |
49 |
50 | ) 51 | } 52 | } 53 | 54 | export default HowToGetStarted; 55 | -------------------------------------------------------------------------------- /frontend/components/greeting/greeting.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | class Greeting extends React.Component{ 4 | 5 | render(){ 6 | const currentUser = this.props.currentUser; 7 | if (currentUser) { 8 | return( 9 |
10 |

Welcome to Taskable, {currentUser.fname}!

11 | 12 |
13 | ); 14 | } else { 15 | return (
); 16 | } 17 | } 18 | 19 | } 20 | 21 | export default Greeting; 22 | -------------------------------------------------------------------------------- /frontend/components/greeting/greeting_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import Greeting from './greeting'; 3 | import { logout } from '../../actions/session_actions'; 4 | 5 | const mapStateToProps = (state) => { 6 | return { currentUser: state.session.currentUser }; 7 | }; 8 | 9 | const mapDispatchToProps = (dispatch) => { 10 | return { logout: () => dispatch(logout()) }; 11 | }; 12 | 13 | export default connect( 14 | mapStateToProps, 15 | mapDispatchToProps 16 | )(Greeting); 17 | -------------------------------------------------------------------------------- /frontend/components/homepage/header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | class Header extends React.Component { 5 | 6 | render(){ 7 | return( 8 |
9 |
10 |
11 | 12 |
13 | 14 | Log in 15 | 16 | 17 | Sign up 18 | 19 |
20 |
21 |
22 |
23 | ) 24 | } 25 | } 26 | 27 | export default Header; 28 | -------------------------------------------------------------------------------- /frontend/components/homepage/homepage_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import Homepage from './homepage'; 3 | import { updateSubject } from '../../actions/ui_actions'; 4 | 5 | const mapStateToProps = (state) => { 6 | return { currentUser: state.session.currentUser }; 7 | }; 8 | 9 | const mapDispatchToProps = dispatch => ({ 10 | updateSubject: id => dispatch(updateSubject(id)) 11 | }); 12 | 13 | export default connect( 14 | mapStateToProps, 15 | mapDispatchToProps 16 | )(Homepage); 17 | -------------------------------------------------------------------------------- /frontend/components/homepage/how_it_works.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | class HowItWorks extends React.Component { 5 | 6 | render(){ 7 | return( 8 |
9 |
10 |
11 |

How it Works

12 |
13 |
14 | 15 |
16 |
1
17 |
Tell us where you're struggling
18 |
19 | Choose from a variety of subjects and select the day and time you'd like to meet with a qualified Tutor. Give us the details and we'll find you the help. 20 |
21 |
22 |
23 |
24 |
25 |
2
26 |
Get matched
27 |
28 | Select from a list of qualified and fully vetted tutors. Find the right fit based on qualifications and hourly rates. Find an expert who suits your needs and learning style. 29 |
30 |
31 | 32 |
33 |
34 | 35 |
36 |
3
37 |
Get real results!
38 |
39 | Just like that, your tutor arrives and helps you towards your goal!. When your session is complete, payment will happen seamlessly and securely through the app. 40 |
41 |
42 |
43 |
44 |
45 | 46 |
47 |
48 | 49 |
50 |
The Taskable Satisfaction Pledge
51 |
Trust and safety are our top priority. All Tutors must undergo extensive background and identity checks. Always have peace of mind with our money back guarantee.
52 |
53 |
54 |
55 |
56 |
57 | ) 58 | } 59 | } 60 | 61 | export default HowItWorks; 62 | -------------------------------------------------------------------------------- /frontend/components/homepage/results.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | 5 | class Results extends React.Component { 6 | 7 | 8 | render() { 9 | return ( 10 |
  • 11 | 12 |
    13 |
    14 |
    15 |
    16 |
    {`${this.props.subject[0]}`}
    17 |
    18 | 19 |
  • 20 | ) 21 | } 22 | } 23 | 24 | 25 | export default Results; 26 | -------------------------------------------------------------------------------- /frontend/components/login_form/login_form_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import LoginForm from './login_form'; 3 | import { login, fblogin, clearErrors } from '../../actions/session_actions'; 4 | 5 | const mapStateToProps = (state, ownProps) => { 6 | return ( 7 | { loggedIn: Boolean(state.session.currentUser), 8 | errors: state.errors.session, 9 | } 10 | ); 11 | }; 12 | 13 | const mapDispatchToProps = (dispatch, ownProps) => ({ 14 | login: user => dispatch(login(user)), 15 | fblogin: email => dispatch(fblogin(email)), 16 | clearErrors: () => dispatch(clearErrors()) 17 | }); 18 | 19 | 20 | export default connect( 21 | mapStateToProps, 22 | mapDispatchToProps 23 | )(LoginForm); 24 | -------------------------------------------------------------------------------- /frontend/components/root.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { HashRouter } from 'react-router-dom'; 4 | import App from './App'; 5 | 6 | const Root = ({ store }) => ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | 14 | export default Root; 15 | -------------------------------------------------------------------------------- /frontend/components/shared/error_handler.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/frontend/components/shared/error_handler.jsx -------------------------------------------------------------------------------- /frontend/components/shared/footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | const Footer = () => { 5 | return ( 6 | 41 | ); 42 | }; 43 | 44 | 45 | export default Footer; 46 | -------------------------------------------------------------------------------- /frontend/components/shared/header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | class AccountHeader extends React.Component{ 5 | 6 | constructor(props) { 7 | super(props) 8 | } 9 | 10 | render() { 11 | return ( 12 |
    13 |
    14 |
    15 | 16 |
    17 | 18 | Dashboard 19 | 20 | 21 | Account 22 | 23 | Logout 24 |
    25 |
    26 |
    27 |
    28 | ) 29 | 30 | } 31 | 32 | } 33 | 34 | 35 | export default AccountHeader; 36 | -------------------------------------------------------------------------------- /frontend/components/shared/trust_banner.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | class TrustBanner extends React.Component { 5 | 6 | render() { 7 | let description; 8 | if (this.props.formType === 'form') { 9 | description = 'Satisfaction guaranteed for every tutoring session.'; 10 | } else if (this.props.formType === 'show') { 11 | description = 'All Tutors are fully vetted & background checked.'; 12 | } else if (this.props.formType === 'confirm') { 13 | description = "You don't pay until your session is completed."; 14 | } 15 | return ( 16 |
    17 |
    18 |
    19 | Trust & Safety Guarantee: 20 |
    {description}
    21 |
    22 |
    23 | ); 24 | } 25 | } 26 | 27 | 28 | export default TrustBanner; 29 | -------------------------------------------------------------------------------- /frontend/components/signup_form/signup_form_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import SignupForm from './signup_form'; 3 | import { signup, clearErrors } from '../../actions/session_actions'; 4 | 5 | const mapStateToProps = (state, ownProps) => { 6 | return ( 7 | { loggedIn: Boolean(state.session.currentUser), 8 | errors: state.errors.session, 9 | } 10 | ); 11 | }; 12 | 13 | const mapDispatchToProps = (dispatch, ownProps) => ({ 14 | login: user => dispatch(signup(user)), 15 | clearErrors: () => dispatch(clearErrors()) 16 | }); 17 | 18 | 19 | export default connect( 20 | mapStateToProps, 21 | mapDispatchToProps 22 | )(SignupForm); 23 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/date_box_item.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class DateBoxItem extends React.Component { 4 | constructor(props){ 5 | super(props); 6 | } 7 | 8 | render() { 9 | let date = this.props.date; 10 | return ( 11 |
    12 | 13 | 19 |
    20 | ); 21 | } 22 | } 23 | 24 | 25 | export default DateBoxItem; 26 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/google/google_autocomplete.js: -------------------------------------------------------------------------------- 1 | export const initAutocomplete = () => { 2 | return new google.maps.places.Autocomplete( 3 | (document.getElementById('autocomplete')), {types: ['geocode', 'establishment']}); 4 | }; 5 | 6 | export const geolocate = (autocomplete) => { 7 | return (autocomplete) => { 8 | if (navigator.geolocation) { 9 | navigator.geolocation.getCurrentPosition((position) => { 10 | const geolocation = { 11 | lat: position.coords.latitude, 12 | lng: position.coords.longitude 13 | }; 14 | const circle = new google.maps.Circle({ 15 | center: geolocation, 16 | radius: position.coords.accuracy 17 | }); 18 | autocomplete.setBounds(circle.getBounds()); 19 | }); 20 | } 21 | }; 22 | }; 23 | 24 | 25 | export const addAptNumToAddress = (address, apt_num) => { 26 | address_components = address.split(","); 27 | 28 | if(apt_num !== "") { 29 | address_components = address_components 30 | .slice(0, 1) 31 | .concat(apt_num, address_components.slice(-3)); 32 | } 33 | return address_components.join(","); 34 | }; 35 | 36 | export const getLocalityAndAddress = () => { 37 | let place = this.getPlace(); 38 | let locality = ""; 39 | let componentForm = { locality: 'long_name' }; 40 | 41 | for (var i = 0; i < place.address_components.length; i++) { 42 | let addressType = place.address_components[i].types[0]; 43 | if (componentForm[addressType]) { 44 | locality = place.address_components[i][componentForm[addressType]]; 45 | console.log(locality); 46 | } 47 | } 48 | 49 | return callback({ locality: locality, address: place.formatted_address }); 50 | } 51 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/modals/login_modal.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Modal from 'react-modal'; 3 | import { FacebookLogin } from 'react-facebook-login-component'; 4 | import { GoogleLogin } from 'react-google-login-component'; 5 | 6 | const customStyles = { 7 | overlay : { 8 | position : 'fixed', 9 | top : 0, 10 | left : 0, 11 | right : 0, 12 | bottom : 0, 13 | background: 'rgba(0, 0, 0, 0.5)' 14 | }, 15 | content : { 16 | top : '50%', 17 | left : '50%', 18 | right : 'auto', 19 | bottom : 'auto', 20 | marginRight : '-50%', 21 | transform : 'translate(-50%, -50%)', 22 | width: '475px', 23 | height: '400px', 24 | borderRadius : '10px' 25 | } 26 | }; 27 | 28 | class LoginModal extends React.Component { 29 | constructor(props) { 30 | super(props); 31 | this.responseFacebook = this.responseFacebook.bind(this); 32 | this.responseGoogle = this.responseGoogle.bind(this); 33 | } 34 | 35 | responseFacebook (response) { 36 | this.props.fblogin(response.email); 37 | } 38 | 39 | responseGoogle (googleUser) { 40 | googleUser = gapi.auth2.getAuthInstance().currentUser.get(); 41 | const profile = googleUser.getBasicProfile(); 42 | var email = profile.getEmail(); 43 | this.props.fblogin(email); 44 | } 45 | 46 | render() { 47 | return ( 48 | 54 |
    55 |
    Please log in to continue.
    56 | close 57 |
    58 |
    59 |
    60 | 61 | mail_outline 62 | 63 |
    {this.props.errors.includes('Invalid email/password combination') ? "Invalid Email/Password Combination" : ""}
    64 |
    65 |
    66 | 67 | vpn_key 68 | 69 |
    70 | 71 |
    72 | Don't have an account? 73 |
    74 | Sign up 75 |
    76 |
    77 |
    78 | Demo Login 79 |
    80 |
    81 |
    82 |
    Or
    83 |
    84 | 93 | 99 |
    100 |
    101 |
    102 | ) 103 | } 104 | } 105 | 106 | export default LoginModal; 107 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/modals/login_modal_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import LoginModal from './login_modal'; 3 | import { fblogin } from '../../../actions/session_actions'; 4 | 5 | const mapStateToProps = (state, ownProps) => { 6 | return ( 7 | { loggedIn: Boolean(state.session.currentUser), 8 | errors: state.errors.session, 9 | } 10 | ); 11 | }; 12 | 13 | const mapDispatchToProps = (dispatch, ownProps) => ({ 14 | fblogin: email => dispatch(fblogin(email)) 15 | }); 16 | 17 | 18 | export default connect( 19 | mapStateToProps, 20 | mapDispatchToProps 21 | )(LoginModal); 22 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/modals/signup_modal.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Modal from 'react-modal'; 3 | 4 | 5 | const signupStyles = { 6 | overlay : { 7 | position : 'fixed', 8 | top : 0, 9 | left : 0, 10 | right : 0, 11 | bottom : 0, 12 | background: 'rgba(0, 0, 0, 0.5)' 13 | }, 14 | content : { 15 | top : '50%', 16 | left : '50%', 17 | right : 'auto', 18 | bottom : 'auto', 19 | marginRight : '-50%', 20 | transform : 'translate(-50%, -50%)', 21 | maxHeight : '640px', 22 | width : '475px', 23 | borderRadius : '10px' 24 | } 25 | }; 26 | 27 | class SignupModal extends React.Component { 28 | 29 | 30 | render() { 31 | return ( 32 | 38 |
    39 |
    Create an Account
    40 | close 41 |
    42 |
    You'll be able to review everything before booking
    43 |
    44 |
    45 |
    46 | 47 | person_outline 48 | 49 | {this.props.fnameError} 50 |
    51 |
    52 | 53 | 54 | {this.props.lnameError} 55 |
    56 |
    57 |
    58 | 59 | mail_outline 60 | 61 | {this.props.emailError} 62 |
    63 |
    64 | 65 | vpn_key 66 | 67 | {this.props.passwordError} 68 |
    69 |
    70 | 71 | place 72 | 73 | {this.props.zipcodeError} 74 |
    75 |
    76 | 77 | phone 78 | 79 | {this.props.phoneNumError} 80 |
    81 | 82 |
    83 | Have an account? 84 |
    85 | Log in 86 |
    87 |
    88 |
    89 |
    90 | ) 91 | } 92 | } 93 | 94 | export default SignupModal; 95 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/sorting_container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | class SortingContainer extends React.Component { 5 | 6 | render() { 7 | return ( 8 |
    9 |
    10 |
    11 |

    12 | Sorted By: 13 |

    14 | 21 |
    22 |
    23 |

    24 | Task Date & Time: 25 |

    26 |
    27 |
    28 | {this.props.dateBoxes} 29 |
    30 |
    31 | 37 |
    38 |
    39 | You can agree later on an exact start time with your selected Tutor. 40 |
    41 |
    42 |
    43 |
    44 |
    45 | ) 46 | } 47 | } 48 | 49 | export default SortingContainer; 50 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/tutor_confirm_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import TutorConfirm from './tutor_confirm'; 3 | import { fetchTutors, createBooking, fetchHiredTutors } from '../../actions/tutor_actions'; 4 | 5 | const mapStateToProps = (state, ownProps) => { 6 | return ( 7 | { loggedIn: Boolean(state.session.currentUser), 8 | errors: state.errors.session, 9 | currentSubject: state.ui.currentSubject, 10 | params: state.tutors.params, 11 | tutors: state.tutors.tutors, 12 | formType: state.ui.formType, 13 | currentUser: state.session.currentUser 14 | } 15 | ); 16 | }; 17 | 18 | const mapDispatchToProps = (dispatch, ownProps) => ({ 19 | fetchTutors: params => dispatch(fetchTutors(params)), 20 | createBooking: params => dispatch(createBooking(params)), 21 | fetchHiredTutors: userId => dispatch(fetchHiredTutors(userId)) 22 | }); 23 | 24 | 25 | export default connect( 26 | mapStateToProps, 27 | mapDispatchToProps 28 | )(TutorConfirm); 29 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/tutor_form_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import TutorForm from './tutor_form'; 3 | import { fetchTutors } from '../../actions/tutor_actions'; 4 | import { updateForm } from '../../actions/ui_actions'; 5 | 6 | const mapStateToProps = (state, ownProps) => { 7 | return ( 8 | { loggedIn: Boolean(state.session.currentUser), 9 | errors: state.errors.session, 10 | currentSubject: state.ui.currentSubject, 11 | params: state.tutors.params, 12 | formType: state.ui.formType 13 | } 14 | ); 15 | }; 16 | 17 | const mapDispatchToProps = (dispatch, ownProps) => ({ 18 | fetchTutors: params => dispatch(fetchTutors(params)), 19 | updateForm: form => dispatch(updateForm(form)) 20 | }); 21 | 22 | 23 | export default connect( 24 | mapStateToProps, 25 | mapDispatchToProps 26 | )(TutorForm); 27 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/tutor_list_item.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link, withRouter } from 'react-router-dom'; 3 | export const SUBJECTS = { 4 | '1': 'Math', 5 | '2': 'Chemistry', 6 | '3': 'Biology', 7 | '4': 'English', 8 | '5': 'History', 9 | '6': 'Test Prep', 10 | '7': 'Coding', 11 | '8': 'Accounting', 12 | '9': 'Physics' 13 | }; 14 | 15 | class TutorListItem extends React.Component { 16 | constructor(props) { 17 | super(props); 18 | } 19 | render(){ 20 | let tutor = this.props.tutor; 21 | return ( 22 |
    23 |
    24 | 25 |
    26 |
    27 | Select & Continue 28 |
    29 | 30 |
    31 |
    32 |
    33 |
    34 |
    {tutor.fname.charAt(0).toUpperCase() + tutor.fname.slice(1)} {tutor.lname.slice(0,1).toUpperCase()}.
    35 |
    ${tutor.rate}/hr
    36 |
    37 | 40 |
    41 | How I can help: 42 |
    43 |
    {tutor.description}
    44 | 45 |
    46 |
    47 | ); 48 | } 49 | 50 | } 51 | 52 | 53 | 54 | export default TutorListItem; 55 | -------------------------------------------------------------------------------- /frontend/components/tutor_form/tutor_show_container.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import TutorShow from './tutor_show'; 3 | import { fetchTutors } from '../../actions/tutor_actions'; 4 | import { login, signup, clearErrors } from '../../actions/session_actions'; 5 | 6 | const mapStateToProps = (state, ownProps) => { 7 | return ( 8 | { loggedIn: Boolean(state.session.currentUser), 9 | errors: state.errors.session, 10 | currentSubject: state.ui.currentSubject, 11 | params: state.tutors.params, 12 | tutors: state.tutors.tutors, 13 | formType: state.ui.formType 14 | } 15 | ); 16 | }; 17 | 18 | const mapDispatchToProps = (dispatch, ownProps) => ({ 19 | fetchTutors: params => dispatch(fetchTutors(params)), 20 | login: user => dispatch(login(user)), 21 | signup: user => dispatch(signup(user)), 22 | clearErrors: () => dispatch(clearErrors()) 23 | }); 24 | 25 | 26 | export default connect( 27 | mapStateToProps, 28 | mapDispatchToProps 29 | )(TutorShow); 30 | -------------------------------------------------------------------------------- /frontend/reducers/errors_reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import SessionErrorsReducer from './session_errors_reducer'; 3 | 4 | const ErrorsReducer = combineReducers({ 5 | session: SessionErrorsReducer 6 | }); 7 | 8 | export default ErrorsReducer; 9 | -------------------------------------------------------------------------------- /frontend/reducers/root_reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import ErrorsReducer from './errors_reducer'; 3 | import SessionReducer from './session_reducer'; 4 | import UiReducer from './ui_reducer.js'; 5 | import TutorsReducer from './tutors_reducer'; 6 | 7 | const RootReducer = combineReducers({ 8 | session: SessionReducer, 9 | errors: ErrorsReducer, 10 | ui: UiReducer, 11 | tutors: TutorsReducer 12 | }); 13 | 14 | 15 | export default RootReducer; 16 | -------------------------------------------------------------------------------- /frontend/reducers/session_errors_reducer.js: -------------------------------------------------------------------------------- 1 | import { RECEIVE_SESSION_ERRORS, RECEIVE_CURRENT_USER, CLEAR_ERRORS } from '../actions/session_actions'; 2 | import merge from 'lodash/merge'; 3 | 4 | 5 | const SessionErrorsReducer = (state = [], action) => { 6 | Object.freeze(state); 7 | switch (action.type) { 8 | case RECEIVE_SESSION_ERRORS: 9 | return action.errors.responseJSON; 10 | case RECEIVE_CURRENT_USER: 11 | return []; 12 | case CLEAR_ERRORS: 13 | return []; 14 | default: 15 | return state; 16 | } 17 | }; 18 | 19 | export default SessionErrorsReducer; 20 | -------------------------------------------------------------------------------- /frontend/reducers/session_reducer.js: -------------------------------------------------------------------------------- 1 | import { RECEIVE_CURRENT_USER, RECEIVE_AVL_TIMES } from '../actions/session_actions'; 2 | import merge from 'lodash/merge'; 3 | const defaultState = { currentUser: null, avlTimes: null }; 4 | 5 | 6 | const SessionReducer = (state = defaultState, action) => { 7 | Object.freeze(state); 8 | switch (action.type) { 9 | case RECEIVE_CURRENT_USER: 10 | return merge({}, state, { currentUser: action.current_user }); 11 | case RECEIVE_AVL_TIMES: 12 | return merge({}, state, { avlTimes: action.avlTimes }) 13 | default: 14 | return state; 15 | } 16 | }; 17 | 18 | export default SessionReducer; 19 | -------------------------------------------------------------------------------- /frontend/reducers/tutors_reducer.js: -------------------------------------------------------------------------------- 1 | import { RECEIVE_CURRENT_USER } from '../actions/session_actions'; 2 | import { RECEIVE_TUTORS, RECEIVE_HIRED_TUTORS } from '../actions/tutor_actions'; 3 | import merge from 'lodash/merge'; 4 | 5 | let defaultState = {tutors: null, params: {address: null, ed_lvl: null, description: null}, hiredTutors: null}; 6 | 7 | const TutorsReducer = (state = defaultState, action) => { 8 | Object.freeze(state); 9 | switch (action.type) { 10 | case RECEIVE_TUTORS: 11 | return merge({}, state, {params: action.params}, {tutors: action.tutors}); 12 | case RECEIVE_HIRED_TUTORS: 13 | return merge({}, state, {hiredTutors: action.hiredTutors}); 14 | case RECEIVE_CURRENT_USER: 15 | return { hiredTutors: [] }; 16 | default: 17 | return state; 18 | } 19 | }; 20 | 21 | export default TutorsReducer; 22 | -------------------------------------------------------------------------------- /frontend/reducers/ui_reducer.js: -------------------------------------------------------------------------------- 1 | import { RECEIVE_CURRENT_SUBJECT, UPDATE_FORM } from '../actions/ui_actions'; 2 | import merge from 'lodash/merge'; 3 | 4 | const UiReducer = (state = { currentSubject: 0, formType: 'form' }, action) => { 5 | Object.freeze(state); 6 | switch(action.type) { 7 | case RECEIVE_CURRENT_SUBJECT: 8 | return merge({}, state, { currentSubject: action.currentSubject }); 9 | case UPDATE_FORM: 10 | return merge({}, state, { formType: action.form }); 11 | default: return state; 12 | } 13 | }; 14 | 15 | export default UiReducer; 16 | -------------------------------------------------------------------------------- /frontend/store/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import RootReducer from '../reducers/root_reducer'; 3 | import logger from 'redux-logger'; 4 | import thunk from 'redux-thunk'; 5 | 6 | const middlewares = [thunk]; 7 | 8 | if (process.env.NODE_ENV !== 'production') { 9 | // must use 'require' (import only allowed at top of file) 10 | const { logger } = require('redux-logger'); 11 | middlewares.push(logger); 12 | } 13 | 14 | 15 | const configureStore = (preloadedState = {}) => ( 16 | createStore( 17 | RootReducer, 18 | preloadedState, 19 | applyMiddleware(...middlewares) 20 | ) 21 | ); 22 | 23 | export default configureStore; 24 | -------------------------------------------------------------------------------- /frontend/taskable.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { signup, login, logout } from './actions/session_actions'; 4 | import configureStore from './store/store'; 5 | import Root from './components/root'; 6 | 7 | document.addEventListener('DOMContentLoaded', () => { 8 | 9 | 10 | let store; 11 | if (window.currentUser) { 12 | const preloadedState = { session: { currentUser: window.currentUser } }; 13 | store = configureStore(preloadedState); 14 | delete window.currentUser; 15 | } else { 16 | store = configureStore(); 17 | } 18 | 19 | const root = document.getElementById('root'); 20 | ReactDOM.render(, root); 21 | }); 22 | -------------------------------------------------------------------------------- /frontend/util/auth_route.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Redirect, withRouter } from 'react-router-dom'; 3 | import { connect } from 'react-redux'; 4 | 5 | const Auth = ({component: Component, path, loggedIn}) => ( 6 | ( 7 | !loggedIn ? ( 8 | 9 | ) : ( 10 | 11 | ) 12 | )}/> 13 | ); 14 | 15 | const Protected = ({component: Component, path, loggedIn}) => ( 16 | ( 17 | loggedIn ? ( 18 | 19 | ) : ( 20 | 21 | ) 22 | )}/> 23 | ); 24 | 25 | const Times = ({component: Component, path, loggedIn}) => ( 26 | ( 27 | loggedIn ? ( 28 | 29 | ) : ( 30 | 31 | ) 32 | )}/> 33 | ); 34 | 35 | 36 | 37 | const mapStateToProps = state => { 38 | return {loggedIn: Boolean(state.session.currentUser), 39 | avlTimes: Boolean(state.session.avlTimes) 40 | }; 41 | }; 42 | 43 | export const AuthRoute = withRouter(connect(mapStateToProps, null)(Auth)); 44 | 45 | export const ProtectedRoute = withRouter(connect(mapStateToProps, null)(Protected)); 46 | 47 | export const TimesRoute = withRouter(connect(mapStateToProps, null)(Times)); 48 | -------------------------------------------------------------------------------- /frontend/util/session_api_util.js: -------------------------------------------------------------------------------- 1 | export const login = user => ( 2 | $.ajax({ 3 | method: 'POST', 4 | url: '/api/session', 5 | data: {user: user} 6 | }) 7 | ); 8 | 9 | export const signup = user => ( 10 | $.ajax({ 11 | method: 'POST', 12 | url: '/api/users', 13 | data: {user: user} 14 | }) 15 | ); 16 | 17 | export const logout = () => ( 18 | $.ajax({ 19 | method: 'DELETE', 20 | url: '/api/session' 21 | }) 22 | ); 23 | 24 | export const fblogin = email => ( 25 | $.ajax({ 26 | method: 'POST', 27 | url: '/api/fbsession', 28 | data: {email: email} 29 | }) 30 | ); 31 | 32 | export const updateUser = (formData, userId) => ( 33 | $.ajax({ 34 | method: 'PATCH', 35 | url: `/api/users/${userId}`, 36 | processData: false, 37 | contentType: false, 38 | data: formData 39 | }) 40 | ); 41 | 42 | export const createAvlTimes = avlTimes => ( 43 | $.ajax({ 44 | method: 'post', 45 | // TODO: interpolate user id 46 | url: '/api/users/:user_id/available_times', 47 | data: avlTimes 48 | }) 49 | ); 50 | -------------------------------------------------------------------------------- /frontend/util/tutor_api_util.js: -------------------------------------------------------------------------------- 1 | export const fetchTutors = params => ( 2 | $.ajax({ 3 | method: 'get', 4 | url: `/api/subjects/${params.currentSubject}/tutors_for_hires/`, 5 | data: params 6 | }) 7 | ); 8 | 9 | export const createBooking = params => ( 10 | $.ajax({ 11 | method: 'post', 12 | url: `/api/users/${params.user_id}/hired_tutors`, 13 | data: params 14 | }) 15 | ); 16 | 17 | export const fetchHiredTutors = userId => ( 18 | $.ajax({ 19 | method: 'get', 20 | url: `/api/users/${userId}/hired_tutors` 21 | }) 22 | ); 23 | 24 | export const createReview = params => ( 25 | $.ajax({ 26 | method: 'post', 27 | url: `/api/users/${params.author_id}/reviews`, 28 | data: params 29 | }) 30 | ) 31 | 32 | export const updateBooking = params => ( 33 | $.ajax({ 34 | method: 'patch', 35 | url: `/api/users/${params.user_id}/hired_tutors/${params.booking_id}`, 36 | data: params 37 | }) 38 | ) 39 | 40 | export const createTutor = params => ( 41 | $.ajax({ 42 | method: 'post', 43 | url: `/api/subjects/${params.subject_id}/tutors_for_hires`, 44 | data: params 45 | }) 46 | ) 47 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/lib/tasks/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Taskable", 3 | "private": true, 4 | "engines": { 5 | "node": "6.10.1", 6 | "npm": "3.10.10" 7 | }, 8 | "dependencies": { 9 | "babel-core": "^6.26.0", 10 | "babel-loader": "^7.1.2", 11 | "babel-preset-env": "^1.6.1", 12 | "babel-preset-es2015": "^6.24.1", 13 | "babel-preset-react": "^6.24.1", 14 | "google-maps-react": "^1.1.0", 15 | "material-design-icons": "^3.0.1", 16 | "react": "^16.0.0", 17 | "react-dom": "^16.0.0", 18 | "react-facebook-login-component": "^0.9.1", 19 | "react-google-autocomplete": "^1.0.15", 20 | "react-google-login-component": "^0.9.2", 21 | "react-list": "^0.8.8", 22 | "react-modal": "^3.1.0", 23 | "react-places-autocomplete": "^5.4.3", 24 | "react-redux": "^5.0.6", 25 | "react-router-dom": "^4.2.2", 26 | "react-router-with-props": "^0.1.1", 27 | "react-transition-group": "^2.2.1", 28 | "redux": "^3.7.2", 29 | "redux-logger": "^3.0.6", 30 | "redux-thunk": "^2.2.0", 31 | "typed.js": "^2.0.6", 32 | "webpack": "^3.8.1" 33 | }, 34 | "description": "This README would normally document whatever steps are necessary to get the application up and running.", 35 | "version": "1.0.0", 36 | "main": "webpack.config.js", 37 | "directories": { 38 | "test": "test" 39 | }, 40 | "scripts": { 41 | "test": "echo \"Error: no test specified\" && exit 1", 42 | "postinstall": "webpack" 43 | }, 44 | "keywords": [], 45 | "author": "", 46 | "license": "ISC", 47 | "devDependencies": { 48 | "inline-environment-variables-webpack-plugin": "^1.2.1" 49 | }, 50 | "repository": { 51 | "type": "git", 52 | "url": "git+https://github.com/victorwu3/Taskable.git" 53 | }, 54 | "bugs": { 55 | "url": "https://github.com/victorwu3/Taskable/issues" 56 | }, 57 | "homepage": "https://github.com/victorwu3/Taskable#readme" 58 | } 59 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

    You may have mistyped the address or the page may have moved.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

    Maybe you tried to change something you didn't have access to.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

    If you are the application owner check the logs for more information.

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/test/controllers/.keep -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/available_times.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: available_times 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # time_avl :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 13 | 14 | one: 15 | user_id: 1 16 | time_avl: 1 17 | 18 | two: 19 | user_id: 1 20 | time_avl: 1 21 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/fixtures/hired_tutors.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: hired_tutors 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # tutor_id :integer not null 8 | # subject_id :integer not null 9 | # rate :integer not null 10 | # description :text not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # date :string 14 | # time_period :string 15 | # location :string 16 | # completed :boolean default(FALSE) 17 | # 18 | 19 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 20 | 21 | one: 22 | user_id: 1 23 | tutor_id: 1 24 | subject_id: 1 25 | date: 2017-10-24 26 | time_period: 1 27 | rate: 1 28 | completed: false 29 | description: MyText 30 | 31 | two: 32 | user_id: 1 33 | tutor_id: 1 34 | subject_id: 1 35 | date: 2017-10-24 36 | time_period: 1 37 | rate: 1 38 | completed: false 39 | description: MyText 40 | -------------------------------------------------------------------------------- /test/fixtures/reviews.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: reviews 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # body :text not null 8 | # positive :boolean not null 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # author_id :integer 12 | # subject_id :integer 13 | # 14 | 15 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 16 | 17 | one: 18 | user_id: 1 19 | body: MyText 20 | positive: false 21 | 22 | two: 23 | user_id: 1 24 | body: MyText 25 | positive: false 26 | -------------------------------------------------------------------------------- /test/fixtures/subjects.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: subjects 4 | # 5 | # id :integer not null, primary key 6 | # title :string not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # 10 | 11 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 12 | 13 | one: 14 | title: MyString 15 | 16 | two: 17 | title: MyString 18 | -------------------------------------------------------------------------------- /test/fixtures/time_of_days.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: time_of_days 4 | # 5 | # id :integer not null, primary key 6 | # time :integer not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # 10 | 11 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 12 | 13 | # This model initially had no columns defined. If you add columns to the 14 | # model remove the '{}' from the fixture names and add the columns immediately 15 | # below each fixture, per the syntax in the comments below 16 | # 17 | one: {} 18 | # column: value 19 | # 20 | two: {} 21 | # column: value 22 | -------------------------------------------------------------------------------- /test/fixtures/tutors_for_hires.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: tutors_for_hires 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # subject_id :integer not null 8 | # num_completed :integer default(0), not null 9 | # rate :integer not null 10 | # description :text not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # first_tier :boolean not null 14 | # second_tier :boolean not null 15 | # third_tier :boolean not null 16 | # 17 | 18 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 19 | 20 | # This model initially had no columns defined. If you add columns to the 21 | # model remove the '{}' from the fixture names and add the columns immediately 22 | # below each fixture, per the syntax in the comments below 23 | # 24 | one: {} 25 | # column: value 26 | # 27 | two: {} 28 | # column: value 29 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # email :string not null 7 | # zipcode :string not null 8 | # password_digest :string not null 9 | # phone_num :string not null 10 | # session_token :string not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # fname :string not null 14 | # lname :string not null 15 | # image_file_name :string 16 | # image_content_type :string 17 | # image_file_size :integer 18 | # image_updated_at :datetime 19 | # 20 | 21 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 22 | 23 | one: 24 | name: MyString 25 | email: MyString 26 | zipcode: MyString 27 | password_digest: MyString 28 | phone_num: MyString 29 | session_token: MyString 30 | 31 | two: 32 | name: MyString 33 | email: MyString 34 | zipcode: MyString 35 | password_digest: MyString 36 | phone_num: MyString 37 | session_token: MyString 38 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/test/models/.keep -------------------------------------------------------------------------------- /test/models/available_time_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: available_times 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # time_avl :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | require 'test_helper' 13 | 14 | class AvailableTimeTest < ActiveSupport::TestCase 15 | # test "the truth" do 16 | # assert true 17 | # end 18 | end 19 | -------------------------------------------------------------------------------- /test/models/hired_tutor_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: hired_tutors 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # tutor_id :integer not null 8 | # subject_id :integer not null 9 | # rate :integer not null 10 | # description :text not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # date :string 14 | # time_period :string 15 | # location :string 16 | # completed :boolean default(FALSE) 17 | # 18 | 19 | require 'test_helper' 20 | 21 | class HiredTutorTest < ActiveSupport::TestCase 22 | # test "the truth" do 23 | # assert true 24 | # end 25 | end 26 | -------------------------------------------------------------------------------- /test/models/review_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: reviews 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # body :text not null 8 | # positive :boolean not null 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # author_id :integer 12 | # subject_id :integer 13 | # 14 | 15 | require 'test_helper' 16 | 17 | class ReviewTest < ActiveSupport::TestCase 18 | # test "the truth" do 19 | # assert true 20 | # end 21 | end 22 | -------------------------------------------------------------------------------- /test/models/subject_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: subjects 4 | # 5 | # id :integer not null, primary key 6 | # title :string not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # 10 | 11 | require 'test_helper' 12 | 13 | class SubjectTest < ActiveSupport::TestCase 14 | # test "the truth" do 15 | # assert true 16 | # end 17 | end 18 | -------------------------------------------------------------------------------- /test/models/time_of_day_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: time_of_days 4 | # 5 | # id :integer not null, primary key 6 | # time :integer not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # 10 | 11 | require 'test_helper' 12 | 13 | class TimeOfDayTest < ActiveSupport::TestCase 14 | # test "the truth" do 15 | # assert true 16 | # end 17 | end 18 | -------------------------------------------------------------------------------- /test/models/tutors_for_hire_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: tutors_for_hires 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # subject_id :integer not null 8 | # num_completed :integer default(0), not null 9 | # rate :integer not null 10 | # description :text not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # first_tier :boolean not null 14 | # second_tier :boolean not null 15 | # third_tier :boolean not null 16 | # 17 | 18 | require 'test_helper' 19 | 20 | class TutorsForHireTest < ActiveSupport::TestCase 21 | # test "the truth" do 22 | # assert true 23 | # end 24 | end 25 | -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # email :string not null 7 | # zipcode :string not null 8 | # password_digest :string not null 9 | # phone_num :string not null 10 | # session_token :string not null 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # fname :string not null 14 | # lname :string not null 15 | # image_file_name :string 16 | # image_content_type :string 17 | # image_file_size :integer 18 | # image_updated_at :datetime 19 | # 20 | 21 | require 'test_helper' 22 | 23 | class UserTest < ActiveSupport::TestCase 24 | # test "the truth" do 25 | # assert true 26 | # end 27 | end 28 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/test/system/.keep -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../config/environment', __FILE__) 2 | require 'rails/test_help' 3 | 4 | class ActiveSupport::TestCase 5 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 6 | fixtures :all 7 | 8 | # Add more helper methods to be used by all tests here... 9 | end 10 | -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/vendor/.keep -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require("webpack"); 3 | 4 | var plugins = []; // if using any plugins for both dev and production 5 | var devPlugins = []; // if using any plugins for development 6 | 7 | var prodPlugins = [ 8 | new webpack.DefinePlugin({ 9 | 'process.env': { 10 | 'NODE_ENV': JSON.stringify('production') 11 | } 12 | }), 13 | new webpack.optimize.UglifyJsPlugin({ 14 | compress: { 15 | warnings: true 16 | } 17 | }) 18 | ]; 19 | 20 | plugins = plugins.concat( 21 | process.env.NODE_ENV === 'production' ? prodPlugins : devPlugins 22 | ); 23 | 24 | 25 | module.exports = { 26 | entry: './frontend/taskable.jsx', 27 | output: { 28 | filename: './app/assets/javascripts/bundle.js', 29 | }, 30 | plugins: plugins, 31 | module: { 32 | loaders: [ 33 | { 34 | test: [/\.jsx?$/], 35 | exclude: /(node_modules)/, 36 | loader: 'babel-loader', 37 | query: { 38 | presets: ['env', 'react'] 39 | } 40 | } 41 | ] 42 | }, 43 | devtool: 'source-map', 44 | resolve: { 45 | extensions: ['.js', '.jsx', '*'] 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /wireframe/homepage:signup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/wireframe/homepage:signup.png -------------------------------------------------------------------------------- /wireframe/login:taskform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/wireframe/login:taskform.png -------------------------------------------------------------------------------- /wireframe/taskshow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorwu3/Taskable/06e826e3edd0982683390a5bf3ce7f36bc7d8a9f/wireframe/taskshow.png --------------------------------------------------------------------------------