diff --git a/.gitignore b/.gitignore index 8838121..21ff5ca 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ # Ignore master key for decrypting credentials and more. /config/master.key + +.env \ No newline at end of file diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..059ca47 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +ruby 3.1.0 diff --git a/AI/app.py b/AI/app.py new file mode 100644 index 0000000..b719b44 --- /dev/null +++ b/AI/app.py @@ -0,0 +1,16 @@ +from flask import Flask, request, jsonify + +app = Flask(__name) + +# Add chatbot routes + +@app.route('/match', methods=['POST']) +def match_users(): + user_data = request.json + # Implement your AI matching logic here + matched_mentor = {} # Replace this with actual matching results + return jsonify(matched_mentor) + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/Gemfile b/Gemfile index b1e47fd..5b8bee3 100644 --- a/Gemfile +++ b/Gemfile @@ -22,7 +22,7 @@ gem "puma", "~> 5.0" # gem "kredis" # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] -# gem "bcrypt", "~> 3.1.7" +gem "bcrypt", "~> 3.1.7" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] @@ -34,7 +34,7 @@ gem "bootsnap", require: false # gem "image_processing", "~> 1.2" # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible -# gem "rack-cors" +gem "rack-cors" group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem @@ -48,3 +48,7 @@ end gem "active_model_serializers", "~> 0.10.14" + +gem 'dotenv-rails' + +gem 'faker' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index f9b34e3..ccf3d5c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -71,6 +71,7 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) + bcrypt (3.1.19) bootsnap (1.16.0) msgpack (~> 1.2) builder (3.2.4) @@ -82,7 +83,13 @@ GEM debug (1.8.0) irb (>= 1.5.0) reline (>= 0.3.1) + dotenv (2.8.1) + dotenv-rails (2.8.1) + dotenv (= 2.8.1) + railties (>= 3.2) erubi (1.12.0) + faker (3.2.1) + i18n (>= 1.8.11, < 2) globalid (1.2.1) activesupport (>= 6.1) i18n (1.14.1) @@ -115,6 +122,8 @@ GEM net-smtp (0.4.0) net-protocol nio4r (2.5.9) + nokogiri (1.15.4-arm64-darwin) + racc (~> 1.4) nokogiri (1.15.4-x86_64-linux) racc (~> 1.4) pg (1.5.4) @@ -124,6 +133,8 @@ GEM nio4r (~> 2.0) racc (1.7.1) rack (2.2.8) + rack-cors (2.0.1) + rack (>= 2.0.0) rack-test (2.1.0) rack (>= 1.3) rails (7.0.8) @@ -170,14 +181,19 @@ GEM zeitwerk (2.6.12) PLATFORMS + arm64-darwin-23 x86_64-linux DEPENDENCIES active_model_serializers (~> 0.10.14) + bcrypt (~> 3.1.7) bootsnap debug + dotenv-rails + faker pg (~> 1.1) puma (~> 5.0) + rack-cors rails (~> 7.0.8) tzinfo-data diff --git a/app/controllers/ai_controller.rb b/app/controllers/ai_controller.rb new file mode 100644 index 0000000..acbe09f --- /dev/null +++ b/app/controllers/ai_controller.rb @@ -0,0 +1,60 @@ +class AiController < ApplicationController + skip_before_action :authorized_user + + def chat_prompt_data + + render json: current_user, serializer: UserSerializer, status: :ok + end + + def match_prompt_data + + # current user (id, match criteria only) + # all mentor records (id, match criteria only) + + # mentors = Mentor.select(:id).includes(:skills, :interests, :genders, :races) + + @mentors = Mentor.all + + mentors = [] + + @mentors.each do |mentor| + user = mentor.user + mentor_obj = { + skills: user.skills, + interests: user.interests, + genders: user.genders, + races: user.races + } + + mentors << mentor_obj + end + + match_data = { + mentee_id: current_user.id, + skills: current_user.skills, + interests: current_user.interests, + mentors: mentors + } + + render json: match_data, status: :ok + end + + def get_cards + # render array of cards (max 5) + end + + private + + def gender_prompt(user) + gender = user.genders[0] + prompt = "I would prefer to be matched with a mentor that has experience as a #{gender} person." + prompt + end + + def race_prompt(user) + race = user.races[0] + prompt = "I would prefer to be matched with a mentor that has experience as a #{race} person." + prompt + end + +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index cf7d2fd..5f76cee 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,33 +1,64 @@ + + class ApplicationController < ActionController::API - # rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response - # rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response - # rescue_from ActiveRecord::ConnectionNotEstablished, with: :render_connection_not_established_response - # include ActionController::Cookies + rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response + rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response + rescue_from ActiveRecord::ConnectionNotEstablished, with: :render_connection_not_established_response + include ActionController::Cookies + + before_action :authorized_user + + # PassageClient = Passage::Client.new( + # app_id: Rails.application.config.passage_app_id + # api_key: Rails.application.config.passage_api_key + # ) + + def current_user + user = User.find_by(id: session[:user_id]) + + if !user && session[:psg_user_id] + passage_user = PassageClient.user.get(user_id: @user_id) + @email = passage_user[:email] + user = User.find_by(email: @email) + + User.create!(email: @email) unless user + user + end + + user + end + + def authorized_user + render json: { error: "Not Authorized" }, status: :unauthorized unless current_user + end - # before_action :authorized_user + def authorize! + begin + request.to_hash() + @user_id = Passage.auth.authenticate_request(request) + session[:psg_user_id] = @user_id - # def current_user - # user = User.find_by(id: session[:user_id]) - # user - # end + render json: current_user, status: :ok + rescue Exception => e + # unauthorized + redirect_to "/unauthorized" + end + end - # def authorized_user - # render json: { error: "Not Authorized" }, status: :unauthorized unless current_user - # end - # private + private - # def render_not_found_response(error) - # render json: { error: "#{error.model} not found."}, status: :not_found - # end + def render_not_found_response(error) + render json: { error: "#{error.model} not found."}, status: :not_found + end - # def render_unprocessable_entity_response(error) - # render json: { error: invalid.record.errors.full_messages }, status: :unprocessable_entity - # end + def render_unprocessable_entity_response(error) + render json: { error: invalid.record.errors.full_messages }, status: :unprocessable_entity + end - # def render_connection_not_established_response(error) - # render json: { error: error}, status: :service_unavailable - # end + def render_connection_not_established_response(error) + render json: { error: error}, status: :service_unavailable + end end diff --git a/app/controllers/career_fields_controller.rb b/app/controllers/career_fields_controller.rb new file mode 100644 index 0000000..c5465cd --- /dev/null +++ b/app/controllers/career_fields_controller.rb @@ -0,0 +1,7 @@ +class CareerFieldsController < ApplicationController + skip_before_action :authorized_user + + def index + render json: CareerField.all, status: :ok + end +end diff --git a/app/controllers/career_titles_controller.rb b/app/controllers/career_titles_controller.rb new file mode 100644 index 0000000..85e67d5 --- /dev/null +++ b/app/controllers/career_titles_controller.rb @@ -0,0 +1,8 @@ +class CareerTitlesController < ApplicationController + skip_before_action :authorized_user + + def index + render json: CareerTitle.all, status: :ok + end + +end diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb new file mode 100644 index 0000000..2365579 --- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,7 @@ +class CategoriesController < ApplicationController + skip_before_action :authorized_user + + def index + render json: Category.all, status: :ok + end +end diff --git a/app/controllers/category_interests_controller.rb b/app/controllers/category_interests_controller.rb new file mode 100644 index 0000000..c018e25 --- /dev/null +++ b/app/controllers/category_interests_controller.rb @@ -0,0 +1,7 @@ +class CategoryInterestsController < ApplicationController + skip_before_action :authorized_user + + def index + render json: CategoryInterest.all, status: :ok + end +end diff --git a/app/controllers/category_skills_controller.rb b/app/controllers/category_skills_controller.rb new file mode 100644 index 0000000..b20e057 --- /dev/null +++ b/app/controllers/category_skills_controller.rb @@ -0,0 +1,7 @@ +class CategorySkillsController < ApplicationController + skip_before_action :authorized_user + + def index + render json: CategorySkill.all, status: :ok + end +end diff --git a/app/controllers/genders_controller.rb b/app/controllers/genders_controller.rb new file mode 100644 index 0000000..ab190d7 --- /dev/null +++ b/app/controllers/genders_controller.rb @@ -0,0 +1,7 @@ +class GendersController < ApplicationController + skip_before_action :authorized_user + + def index + render json: Gender.all, status: :ok + end +end diff --git a/app/controllers/interests_controller.rb b/app/controllers/interests_controller.rb new file mode 100644 index 0000000..81dbd8c --- /dev/null +++ b/app/controllers/interests_controller.rb @@ -0,0 +1,8 @@ +class InterestsController < ApplicationController + skip_before_action :authorized_user + + def index + render json: Interest.all, status: :ok + end + +end diff --git a/app/controllers/matches_controller.rb b/app/controllers/matches_controller.rb new file mode 100644 index 0000000..a2ef55f --- /dev/null +++ b/app/controllers/matches_controller.rb @@ -0,0 +1,7 @@ +class MatchesController < ApplicationController + skip_before_action :authorized_user + + def index + render json: Match.all, status: :ok + end +end diff --git a/app/controllers/mentees_controller.rb b/app/controllers/mentees_controller.rb new file mode 100644 index 0000000..ca862f5 --- /dev/null +++ b/app/controllers/mentees_controller.rb @@ -0,0 +1,7 @@ +class MenteesController < ApplicationController + skip_before_action :authorized_user + + def index + render json: Mentee.all, status: :ok + end +end diff --git a/app/controllers/mentors_controller.rb b/app/controllers/mentors_controller.rb new file mode 100644 index 0000000..8044d86 --- /dev/null +++ b/app/controllers/mentors_controller.rb @@ -0,0 +1,8 @@ +class MentorsController < ApplicationController + skip_before_action :authorized_user + + def index + render json: Mentor.all, status: :ok + end + +end diff --git a/app/controllers/races_controller.rb b/app/controllers/races_controller.rb new file mode 100644 index 0000000..2bb87db --- /dev/null +++ b/app/controllers/races_controller.rb @@ -0,0 +1,7 @@ +class RacesController < ApplicationController + skip_before_action :authorized_user + + def index + render json: Race.all, status: :ok + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..12cd79b --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,49 @@ +class SessionsController < ApplicationController + + skip_before_action :authorized_user, only: [:create, :destroy] + + def show + render json: current_user, status: :ok + end + + # This is the standard password login without Passage + def create + + puts "Requested email: " + user = User.find_by(email: params[:email]) + + # Handle successful login with the password + # if user&.authenticate(params[:password]) + if user + puts "Actually logging in the user" + session[:user_id] = user.id + render json: user, status: :created + + # # Handle no password set due to user originally registering with Passage + # elsif !user.password + # render json: { error: "No password was set for this account", email: params[:email]}, status: :unprocessable_entity + + # Handle incorrect username or password + else + + # Temporary code while the auth issue is being fixed + mock_user = User.first + + if mock_user + puts "Pretending to log in the user" + session[:user_id] = mock_user.id + render json: mock_user, status: :ok + else + render json:{ error: "Invalid username or password", }, status: :unauthorized + end + # End temporary code + end + end + + # This is the logout regardless of the auth method + def destroy + session.delete :user_id + session.delete :psg_user_id + head :no_content + end +end diff --git a/app/controllers/skills_controller.rb b/app/controllers/skills_controller.rb new file mode 100644 index 0000000..abe042c --- /dev/null +++ b/app/controllers/skills_controller.rb @@ -0,0 +1,7 @@ +class SkillsController < ApplicationController + skip_before_action :authorized_user + + def index + render json: Skill.all, status: :ok + end +end diff --git a/app/controllers/user_careers_controller.rb b/app/controllers/user_careers_controller.rb new file mode 100644 index 0000000..750af38 --- /dev/null +++ b/app/controllers/user_careers_controller.rb @@ -0,0 +1,9 @@ +class UserCareersController < ApplicationController + skip_before_action :authorized_user + + def index + @user = User.find(params[:id]) + + render json: UserCareer.where!(user: @user), status: :ok + end +end diff --git a/app/controllers/user_genders_controller.rb b/app/controllers/user_genders_controller.rb new file mode 100644 index 0000000..0d810c8 --- /dev/null +++ b/app/controllers/user_genders_controller.rb @@ -0,0 +1,9 @@ +class UserGendersController < ApplicationController + skip_before_action :authorized_user + + def index + @user = User.find(params[:id]) + + render json: UserGender.where!(user: @user), status: :ok + end +end diff --git a/app/controllers/user_interests_controller.rb b/app/controllers/user_interests_controller.rb new file mode 100644 index 0000000..361d71d --- /dev/null +++ b/app/controllers/user_interests_controller.rb @@ -0,0 +1,9 @@ +class UserInterestsController < ApplicationController + skip_before_action :authorized_user + + def index + @user = User.find(params[:id]) + + render json: UserInterest.where!(user: @user), status: :ok + end +end diff --git a/app/controllers/user_races_controller.rb b/app/controllers/user_races_controller.rb new file mode 100644 index 0000000..3cbec01 --- /dev/null +++ b/app/controllers/user_races_controller.rb @@ -0,0 +1,9 @@ +class UserRacesController < ApplicationController + skip_before_action :authorized_user + + def index + @user = User.find(params[:id]) + + render json: UserRace.where!(user: @user), status: :ok + end +end diff --git a/app/controllers/user_skills_controller.rb b/app/controllers/user_skills_controller.rb new file mode 100644 index 0000000..276b8c7 --- /dev/null +++ b/app/controllers/user_skills_controller.rb @@ -0,0 +1,9 @@ +class UserSkillsController < ApplicationController + skip_before_action :authorized_user + + def index + @user = User.find(params[:id]) + + render json: UserSkill.where!(user: @user), status: :ok + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..3ca42b1 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,40 @@ +class UsersController < ApplicationController + skip_before_action :authorized_user, only: [:create] + + def index + render json: User.all, status: :ok + end + + def show + render json: User.find(params[:id]), status: :ok + end + + # This is the login route using traditional password registration + # Passage registration and PostgreSQL entity creations are currently handled by application#current_user + # Will improve dual registration handling in a future version + def create + render json: User.create!(user_params), status: :created + end + + def update + render json: User.update!(user_params), status: :accepted + end + + def destroy + user = User.find_by(id: params[:id]) + if user&.authenticate(params[:password]) + user.destroy + head :no_content + else + render json:{ errors: "Invalid password"}, status: :unauthorized + end + end + + private + + def user_params + params.require(:user).permit(:username, :password, :email, :phone_number, :first_name, :middle_name, :last_name, :suffix, :date_of_birth, :city, :state, :country, :zip_code, :timezone) + end + + +end diff --git a/app/models/ai.rb b/app/models/ai.rb new file mode 100644 index 0000000..ae100f4 --- /dev/null +++ b/app/models/ai.rb @@ -0,0 +1,8 @@ +class AI < ApplicationRecord + + def ai_mentor_info + user = current_user.id + + render json: user, serializer: CurrentUserSerializer, status: :ok + end +end diff --git a/app/models/career_field.rb b/app/models/career_field.rb new file mode 100644 index 0000000..73fb419 --- /dev/null +++ b/app/models/career_field.rb @@ -0,0 +1,2 @@ +class CareerField < ApplicationRecord +end diff --git a/app/models/career_title.rb b/app/models/career_title.rb new file mode 100644 index 0000000..4849fe7 --- /dev/null +++ b/app/models/career_title.rb @@ -0,0 +1,3 @@ +class CareerTitle < ApplicationRecord + belongs_to :career_field +end diff --git a/app/models/category.rb b/app/models/category.rb new file mode 100644 index 0000000..565eda1 --- /dev/null +++ b/app/models/category.rb @@ -0,0 +1,12 @@ +class Category < ApplicationRecord + + has_many :category_interests + has_many :interests, through: :category_interests + + has_many :category_skills + has_many :skills, through: :category_skills + + validates :name, presence: :true, uniqueness: true + + +end diff --git a/app/models/category_interest.rb b/app/models/category_interest.rb new file mode 100644 index 0000000..db464b8 --- /dev/null +++ b/app/models/category_interest.rb @@ -0,0 +1,4 @@ +class CategoryInterest < ApplicationRecord + belongs_to :category + belongs_to :interest +end diff --git a/app/models/category_skill.rb b/app/models/category_skill.rb new file mode 100644 index 0000000..32c99d9 --- /dev/null +++ b/app/models/category_skill.rb @@ -0,0 +1,4 @@ +class CategorySkill < ApplicationRecord + belongs_to :category + belongs_to :skill +end diff --git a/app/models/gender.rb b/app/models/gender.rb new file mode 100644 index 0000000..ee5d437 --- /dev/null +++ b/app/models/gender.rb @@ -0,0 +1,7 @@ +class Gender < ApplicationRecord + + has_many :user_genders + has_many :users, through: :user_genders + + +end diff --git a/app/models/interest.rb b/app/models/interest.rb new file mode 100644 index 0000000..b68b0a1 --- /dev/null +++ b/app/models/interest.rb @@ -0,0 +1,8 @@ +class Interest < ApplicationRecord + + has_many :category_interests + has_many :categories, through: :categories + + validates :name, presence: :true + +end diff --git a/app/models/match.rb b/app/models/match.rb new file mode 100644 index 0000000..9335112 --- /dev/null +++ b/app/models/match.rb @@ -0,0 +1,4 @@ +class Match < ApplicationRecord + belongs_to :mentor + belongs_to :mentee +end diff --git a/app/models/mentee.rb b/app/models/mentee.rb new file mode 100644 index 0000000..6876fb7 --- /dev/null +++ b/app/models/mentee.rb @@ -0,0 +1,3 @@ +class Mentee < ApplicationRecord + belongs_to :user +end diff --git a/app/models/mentor.rb b/app/models/mentor.rb new file mode 100644 index 0000000..7448e57 --- /dev/null +++ b/app/models/mentor.rb @@ -0,0 +1,3 @@ +class Mentor < ApplicationRecord + belongs_to :user +end diff --git a/app/models/race.rb b/app/models/race.rb new file mode 100644 index 0000000..2962ad3 --- /dev/null +++ b/app/models/race.rb @@ -0,0 +1,5 @@ +class Race < ApplicationRecord + + has_many :user_races + has_many :users, through: :user_races +end diff --git a/app/models/skill.rb b/app/models/skill.rb new file mode 100644 index 0000000..a945040 --- /dev/null +++ b/app/models/skill.rb @@ -0,0 +1,7 @@ +class Skill < ApplicationRecord + + has_many :category_skills + has_many :categories, through: :categories + + validates :name, presence: :true +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..f605f59 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,45 @@ +class User < ApplicationRecord + has_secure_password + + has_many :user_genders + has_many :genders, through: :user_genders + + has_many :user_races + has_many :races, through: :user_races + + has_many :user_skills + has_many :skills, through: :user_skills + + has_many :user_interests + has_many :interests, through: :user_interests + + has_many :user_careers + + has_one :mentor + has_one :mentee + + validates :email, presence: true, uniqueness: true + validates :date_of_birth, presence: true + # validates :age, numericality: { greater_than_or_equal_to: 13 } + + def full_name + "#{self.first_name} #{self.last_name}" + end + + def age + "?" + # age = Date.current.year - self.date_of_birth.year + # age -= 1 if Date.today < self.date_of_birth + age.years + # age + end + + def is_adult? + self.age >= 18 + end + + def gender + self.genders[0] + end + + +end diff --git a/app/models/user_career.rb b/app/models/user_career.rb new file mode 100644 index 0000000..c9e78ed --- /dev/null +++ b/app/models/user_career.rb @@ -0,0 +1,4 @@ +class UserCareer < ApplicationRecord + belongs_to :user + belongs_to :career_title +end diff --git a/app/models/user_gender.rb b/app/models/user_gender.rb new file mode 100644 index 0000000..91a9f82 --- /dev/null +++ b/app/models/user_gender.rb @@ -0,0 +1,4 @@ +class UserGender < ApplicationRecord + belongs_to :user + belongs_to :gender +end diff --git a/app/models/user_interest.rb b/app/models/user_interest.rb new file mode 100644 index 0000000..b3f18e6 --- /dev/null +++ b/app/models/user_interest.rb @@ -0,0 +1,4 @@ +class UserInterest < ApplicationRecord + belongs_to :user + belongs_to :interest +end diff --git a/app/models/user_race.rb b/app/models/user_race.rb new file mode 100644 index 0000000..c1b407f --- /dev/null +++ b/app/models/user_race.rb @@ -0,0 +1,4 @@ +class UserRace < ApplicationRecord + belongs_to :user + belongs_to :race +end diff --git a/app/models/user_skill.rb b/app/models/user_skill.rb new file mode 100644 index 0000000..24f31cf --- /dev/null +++ b/app/models/user_skill.rb @@ -0,0 +1,4 @@ +class UserSkill < ApplicationRecord + belongs_to :user + belongs_to :skill +end diff --git a/app/serializers/career_field_serializer.rb b/app/serializers/career_field_serializer.rb new file mode 100644 index 0000000..800f82a --- /dev/null +++ b/app/serializers/career_field_serializer.rb @@ -0,0 +1,3 @@ +class CareerFieldSerializer < ActiveModel::Serializer + attributes :id, :name +end diff --git a/app/serializers/career_title_serializer.rb b/app/serializers/career_title_serializer.rb new file mode 100644 index 0000000..1a42583 --- /dev/null +++ b/app/serializers/career_title_serializer.rb @@ -0,0 +1,4 @@ +class CareerTitleSerializer < ActiveModel::Serializer + attributes :id, :name + has_one :career_field +end diff --git a/app/serializers/category_interest_serializer.rb b/app/serializers/category_interest_serializer.rb new file mode 100644 index 0000000..7067104 --- /dev/null +++ b/app/serializers/category_interest_serializer.rb @@ -0,0 +1,5 @@ +class CategoryInterestSerializer < ActiveModel::Serializer + attributes :id, :name + has_one :category + has_one :interest +end diff --git a/app/serializers/category_serializer.rb b/app/serializers/category_serializer.rb new file mode 100644 index 0000000..438cd85 --- /dev/null +++ b/app/serializers/category_serializer.rb @@ -0,0 +1,3 @@ +class CategorySerializer < ActiveModel::Serializer + attributes :id, :name +end diff --git a/app/serializers/category_skill_serializer.rb b/app/serializers/category_skill_serializer.rb new file mode 100644 index 0000000..fd5d605 --- /dev/null +++ b/app/serializers/category_skill_serializer.rb @@ -0,0 +1,5 @@ +class CategorySkillSerializer < ActiveModel::Serializer + attributes :id + has_one :category + has_one :skill +end diff --git a/app/serializers/current_user_serializer.rb b/app/serializers/current_user_serializer.rb new file mode 100644 index 0000000..c3df15d --- /dev/null +++ b/app/serializers/current_user_serializer.rb @@ -0,0 +1,9 @@ +class CurrentUserSerializer < ActiveModel::Serializer + attributes :username, :first_name, :middle_name, :last_name, :suffix, :date_of_birth, :city, :state, :country, :zip_code, :timezone, :age, :is_adult + + has_many :genders + has_many :races + has_many :user_careers + has_many :user_skills + has_many :user_interests +end diff --git a/app/serializers/gender_serializer.rb b/app/serializers/gender_serializer.rb new file mode 100644 index 0000000..4240512 --- /dev/null +++ b/app/serializers/gender_serializer.rb @@ -0,0 +1,3 @@ +class GenderSerializer < ActiveModel::Serializer + attributes :id, :name +end diff --git a/app/serializers/interest_serializer.rb b/app/serializers/interest_serializer.rb new file mode 100644 index 0000000..e09d946 --- /dev/null +++ b/app/serializers/interest_serializer.rb @@ -0,0 +1,3 @@ +class InterestSerializer < ActiveModel::Serializer + attributes :id, :name +end diff --git a/app/serializers/match_game_serializer.rb b/app/serializers/match_game_serializer.rb new file mode 100644 index 0000000..88ec0da --- /dev/null +++ b/app/serializers/match_game_serializer.rb @@ -0,0 +1,3 @@ +class MatchGameSerializer < ActiveModel::Serializer + attributes :id +end diff --git a/app/serializers/match_serializer.rb b/app/serializers/match_serializer.rb new file mode 100644 index 0000000..88be96e --- /dev/null +++ b/app/serializers/match_serializer.rb @@ -0,0 +1,5 @@ +class MatchSerializer < ActiveModel::Serializer + attributes :id + has_one :mentor + has_one :mentee +end diff --git a/app/serializers/mentee_serializer.rb b/app/serializers/mentee_serializer.rb new file mode 100644 index 0000000..0e6fa61 --- /dev/null +++ b/app/serializers/mentee_serializer.rb @@ -0,0 +1,4 @@ +class MenteeSerializer < ActiveModel::Serializer + attributes :id + has_one :user +end diff --git a/app/serializers/mentor_serializer.rb b/app/serializers/mentor_serializer.rb new file mode 100644 index 0000000..2f71386 --- /dev/null +++ b/app/serializers/mentor_serializer.rb @@ -0,0 +1,8 @@ +class MentorSerializer < ActiveModel::Serializer + attributes :id, :user + has_one :user + + def user + User.find(self.id) + end +end diff --git a/app/serializers/race_serializer.rb b/app/serializers/race_serializer.rb new file mode 100644 index 0000000..bb8b023 --- /dev/null +++ b/app/serializers/race_serializer.rb @@ -0,0 +1,3 @@ +class RaceSerializer < ActiveModel::Serializer + attributes :id, :name +end diff --git a/app/serializers/skill_serializer.rb b/app/serializers/skill_serializer.rb new file mode 100644 index 0000000..d7b6411 --- /dev/null +++ b/app/serializers/skill_serializer.rb @@ -0,0 +1,3 @@ +class SkillSerializer < ActiveModel::Serializer + attributes :id, :name +end diff --git a/app/serializers/user_career_serializer.rb b/app/serializers/user_career_serializer.rb new file mode 100644 index 0000000..c8476c6 --- /dev/null +++ b/app/serializers/user_career_serializer.rb @@ -0,0 +1,5 @@ +class UserCareerSerializer < ActiveModel::Serializer + attributes :id, :company, :start_date, :end_date, :is_current, :description + has_one :user + has_one :career_title +end diff --git a/app/serializers/user_gender_serializer.rb b/app/serializers/user_gender_serializer.rb new file mode 100644 index 0000000..1a3cc94 --- /dev/null +++ b/app/serializers/user_gender_serializer.rb @@ -0,0 +1,5 @@ +class UserGenderSerializer < ActiveModel::Serializer + attributes :id + has_one :user + has_one :gender +end diff --git a/app/serializers/user_interest_serializer.rb b/app/serializers/user_interest_serializer.rb new file mode 100644 index 0000000..08f55b4 --- /dev/null +++ b/app/serializers/user_interest_serializer.rb @@ -0,0 +1,5 @@ +class UserInterestSerializer < ActiveModel::Serializer + attributes :id + has_one :user + has_one :interest +end diff --git a/app/serializers/user_race_serializer.rb b/app/serializers/user_race_serializer.rb new file mode 100644 index 0000000..2e7f0f3 --- /dev/null +++ b/app/serializers/user_race_serializer.rb @@ -0,0 +1,5 @@ +class UserRaceSerializer < ActiveModel::Serializer + attributes :id + has_one :user + has_one :race +end diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb new file mode 100644 index 0000000..c1c30ed --- /dev/null +++ b/app/serializers/user_serializer.rb @@ -0,0 +1,5 @@ +class UserSerializer < ActiveModel::Serializer + attributes :id, :username, :first_name, :middle_name, :last_name, :suffix, :date_of_birth, :city, :state, :country, :zip_code, :timezone, :gender + # has_one :gender + # has_one :race +end diff --git a/app/serializers/user_skill_serializer.rb b/app/serializers/user_skill_serializer.rb new file mode 100644 index 0000000..37093e6 --- /dev/null +++ b/app/serializers/user_skill_serializer.rb @@ -0,0 +1,5 @@ +class UserSkillSerializer < ActiveModel::Serializer + attributes :id + has_one :user + has_one :skill +end diff --git a/client/package-lock.json b/client/package-lock.json index 028587c..54b331c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,6 +8,9 @@ "name": "client", "version": "0.1.0", "dependencies": { + "@dicebear/collection": "^7.0.1", + "@dicebear/core": "^7.0.1", + "@reduxjs/toolkit": "^1.9.6", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -18,6 +21,8 @@ "react": "^18.2.0", "react-chat-elements": "^12.0.10", "react-dom": "^18.2.0", + "react-nice-avatar": "^1.4.1", + "react-redux": "^8.1.3", "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", "react-scroll": "^1.9.0", @@ -2277,6 +2282,395 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@dicebear/adventurer": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/adventurer/-/adventurer-7.0.1.tgz", + "integrity": "sha512-eqbHHAQO8HjG8YNMl8xgklxphC7HvfDtqVr1rkJWP98e7r2AdQpu0cPYIOZPV4uv9gxl1ncaErQjdjvIvFRGiA==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/adventurer-neutral": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/adventurer-neutral/-/adventurer-neutral-7.0.1.tgz", + "integrity": "sha512-dZfyaUFS8qQv7Lv+OXNTHVkercDCh+VqGSJU8jIf3FFbtFbFF79FXZJwJ8V3+pr0xKcZWa8i+8hXLtU3gqZ18g==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/avataaars": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/avataaars/-/avataaars-7.0.1.tgz", + "integrity": "sha512-U7JJLDFJsbVyQl3j1SqtTxi5h+I5JXL8CGfwAOPtQTnk/tKQFXM9WF/zdHegtxbxYAxQaYJtyprdwTJHx5ELnw==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/avataaars-neutral": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/avataaars-neutral/-/avataaars-neutral-7.0.1.tgz", + "integrity": "sha512-e3XwK3xup4ifJ/BUNjR5rcrw9982SC75UTJlPsKuuOM/Lwx3MtUe3+dqeDSyYbrC7KoWespX70oDZK1+2dBQFw==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/big-ears": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/big-ears/-/big-ears-7.0.1.tgz", + "integrity": "sha512-ITI0IQCwdn5s5/kUrNdO488TQvZdiCljnzKpqbQ1hqfsxZ0C+eZs+cudZ0bqLftYxM+WBvmaJwrh3pXNAz1h+w==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/big-ears-neutral": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/big-ears-neutral/-/big-ears-neutral-7.0.1.tgz", + "integrity": "sha512-2QK9HVmApoGFLi3ONW9mh0Tk/PPyHx9rvzUvcT5H/mb80ooBqIVMPYYq4rVlGVP6wAtsNHdoxzzlKja0DG+vvQ==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/big-smile": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/big-smile/-/big-smile-7.0.1.tgz", + "integrity": "sha512-hVAhUMZ0LUhMFvtmUDR8GU7v2ufl5pOcVPiVSC3oV8nyywFp7s1ZqYGhi6rBCEG3qsMR54JfMFWkjV88j4Yrmg==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/bottts": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/bottts/-/bottts-7.0.1.tgz", + "integrity": "sha512-k0adSvnT9+gFDO7/Cmts9TM3CSWYrZrxZe1WpELjTvwe4QOqdn3LgrYR9JXU/2hRz3GaXtP02SHNd85CkadYVw==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/bottts-neutral": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/bottts-neutral/-/bottts-neutral-7.0.1.tgz", + "integrity": "sha512-1T1NEKAEvqyGlUprkO1Q1btITZnMBiCP5YeCy6wYyM7qJsPVDSySsjASJ1j/+IZFi8ePgWReFIbigFiHdo7iLA==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/collection": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/collection/-/collection-7.0.1.tgz", + "integrity": "sha512-Y5xzjU2hiklYUkqsSr5VBpVPG++iUUTm1UDJLPM+iXg3lMF3PQuifXoaAxcuoBvvnKfJKNHf5wP1Bq6nRUl4NA==", + "dependencies": { + "@dicebear/adventurer": "7.0.1", + "@dicebear/adventurer-neutral": "7.0.1", + "@dicebear/avataaars": "7.0.1", + "@dicebear/avataaars-neutral": "7.0.1", + "@dicebear/big-ears": "7.0.1", + "@dicebear/big-ears-neutral": "7.0.1", + "@dicebear/big-smile": "7.0.1", + "@dicebear/bottts": "7.0.1", + "@dicebear/bottts-neutral": "7.0.1", + "@dicebear/croodles": "7.0.1", + "@dicebear/croodles-neutral": "7.0.1", + "@dicebear/fun-emoji": "7.0.1", + "@dicebear/icons": "7.0.1", + "@dicebear/identicon": "7.0.1", + "@dicebear/initials": "7.0.1", + "@dicebear/lorelei": "7.0.1", + "@dicebear/lorelei-neutral": "7.0.1", + "@dicebear/micah": "7.0.1", + "@dicebear/miniavs": "7.0.1", + "@dicebear/notionists": "7.0.1", + "@dicebear/notionists-neutral": "7.0.1", + "@dicebear/open-peeps": "7.0.1", + "@dicebear/personas": "7.0.1", + "@dicebear/pixel-art": "7.0.1", + "@dicebear/pixel-art-neutral": "7.0.1", + "@dicebear/rings": "7.0.1", + "@dicebear/shapes": "7.0.1", + "@dicebear/thumbs": "7.0.1" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/converter": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/converter/-/converter-7.0.1.tgz", + "integrity": "sha512-CEIF6ZKi1FAE9kW10FvuPUjA6HLi+LcuB/GRFct/Bv28llzTel9xwbmfOEa1aIM8Nnp8BuT4U7tBIytksf+ptw==", + "dependencies": { + "@types/json-schema": "^7.0.11", + "tmp-promise": "^3.0.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@resvg/resvg-js": "^2.4.1", + "exiftool-vendored": "^22.0.0", + "sharp": "^0.32.1" + }, + "peerDependenciesMeta": { + "@resvg/resvg-js": { + "optional": true + }, + "exiftool-vendored": { + "optional": true + }, + "sharp": { + "optional": true + } + } + }, + "node_modules/@dicebear/core": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/core/-/core-7.0.1.tgz", + "integrity": "sha512-jaJG693c+myLocgG3kKXdHa+WJ+S6OcD31SEr9Oby7hhOzALQYD+LcJ15oBWwI7SLHJcGPYTOLyx2eDr8YhXCQ==", + "dependencies": { + "@dicebear/converter": "7.0.1", + "@types/json-schema": "^7.0.11" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@dicebear/croodles": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/croodles/-/croodles-7.0.1.tgz", + "integrity": "sha512-uauBTUvKFvsiaT+LWYKCEboEeOJy2Pk055nsdczi13UgHHfj+Qvy0/ky/uzYn+WC/1gewqQ6w/yS1WfpgPtIpg==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/croodles-neutral": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/croodles-neutral/-/croodles-neutral-7.0.1.tgz", + "integrity": "sha512-u09YylowZcbSAVyKJ4I8BCo1ehluqg3onYCclx++8mOWcEo+XGsGKIeN7osayaflNY/qtA9Jt2JsPgiS8KpQ5A==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/fun-emoji": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/fun-emoji/-/fun-emoji-7.0.1.tgz", + "integrity": "sha512-oJj5sb4rakro4e0lZTCkcKkiClHxDWr6+NWTwoU5L1HYRkXV6ngk4s7xSdOrYBQpYjLhdu+Lpx1VHYNpLUu2vg==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/icons": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/icons/-/icons-7.0.1.tgz", + "integrity": "sha512-juHS4feScGCz4YdiwjxR60RJ2G7Z6W+tdUqNHN9ufMvY/FpJTfrQvzvrJfJfc84QZwIrqI/96WV3JIBEIO2AwQ==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/identicon": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/identicon/-/identicon-7.0.1.tgz", + "integrity": "sha512-9W9pqqhvpMsZmOkjuLwlw0iift56A3VFq7eNpJPB1mm6gytfqgxozgOVLDFgug9VXgUVI2Jrk/XnXGIFVIeVQA==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/initials": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/initials/-/initials-7.0.1.tgz", + "integrity": "sha512-zCI6fky4odM5ezl/GlhcSdnu+oNfmBbIghFB5NzgB/wV5nHmw2okONRC+Mgmxv8P8EpFb9z5hEOnh8xwW8htow==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/lorelei": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/lorelei/-/lorelei-7.0.1.tgz", + "integrity": "sha512-3pyI2JF70PlqZUZEs5pVxmQWDJ2/bWmGG/iFtwsEh9HivtF8Zon4Er0NrsEoiKDvScyY4VGwl4LyUBc8JvNb9w==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/lorelei-neutral": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/lorelei-neutral/-/lorelei-neutral-7.0.1.tgz", + "integrity": "sha512-4XaqE5v1dhE4TYrKSGG/VNUFqA31ADlqOnr6bd27E5MnaJLlY8ZAm3sue7EI9kEJ/i5KYov+Q4uS7JNDA5+cag==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/micah": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/micah/-/micah-7.0.1.tgz", + "integrity": "sha512-zHnEewRaREZGNTqnlZiSoha/wNFxEsVQ3E5QYpe9KB3rcLW4CVUgFAHjb449vniG6NfsAWzyAkOfhy4N6Zzw0g==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/miniavs": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/miniavs/-/miniavs-7.0.1.tgz", + "integrity": "sha512-v0n2JT0N1I7vAGoi4NQ98IKtn4JgjwD2Gkqq7l5QAy0jzl1v289FfTng0cOrthroMGBQ5jPS0wUyI0TluoFZRw==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/notionists": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/notionists/-/notionists-7.0.1.tgz", + "integrity": "sha512-uEYBywouoUmvWtWARyeqAoQWX1DpvKL33dVxZ5K/ulYd/nXu9WHeFCPaP4tqE5II1XPS4khwneimFN6F1HA5NQ==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/notionists-neutral": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/notionists-neutral/-/notionists-neutral-7.0.1.tgz", + "integrity": "sha512-jRA7u2UU1I9EXzqBZL3vwI/V7pdDT60yB3bBjyD5J4TznT7bMwt7qEm1eV31U37mn3H+LTFiPD9/4G6whiU3nQ==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/open-peeps": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/open-peeps/-/open-peeps-7.0.1.tgz", + "integrity": "sha512-z1gXzd7XXLzSZpOrDPZmnJDXySCUEKmunRdRuWBSRrfIcVkgStZM0y8uuSrs3LpR8U2xcNJN9yO2wNRRWKmFEw==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/personas": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/personas/-/personas-7.0.1.tgz", + "integrity": "sha512-6/nsrN7JIlMqdH7UwhrACVoCEM3IVHkpMq2I0A1JbhmYp240TI8kM5xYSF0KRdOyAPbyDH/TEB8Uld4LKE+3wQ==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/pixel-art": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/pixel-art/-/pixel-art-7.0.1.tgz", + "integrity": "sha512-9f17Ze4533CbHp23E+gRSSZdCUAB5/PieRq6/ZtVOnPI/PfglhhKMKSxQIm/H267gE2Y+VVhHpUTwGlbAgh1Lg==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/pixel-art-neutral": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/pixel-art-neutral/-/pixel-art-neutral-7.0.1.tgz", + "integrity": "sha512-+9RS0ohGDbPu+W2eGGk3LyzvFbM5qsuhCQR4qO7YIcvmODyNFPJ7eW9g/MHFVPLQXq60SCEUF5CEKY0xs4baUQ==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/rings": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/rings/-/rings-7.0.1.tgz", + "integrity": "sha512-6wsLE4kbkBGeaaEA/afIV0eNYYfIVXo60XgApJA7JdcwyvdTa9LE5Wcp2VBEsZYXdsT9Ml7BC4er/QyMqCayUw==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/shapes": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/shapes/-/shapes-7.0.1.tgz", + "integrity": "sha512-/ol+SazDlJYYe5pYaqKcnYDBjux+2Ny57hIrkHhonV0z4ny3Pq6c4Lq+hN3MnTBpKJszCXLrSP3uCbSQpjnkOg==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, + "node_modules/@dicebear/thumbs": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@dicebear/thumbs/-/thumbs-7.0.1.tgz", + "integrity": "sha512-eQYVJ8NN9buPfbd2Va0fY8sHRq9n1d7FJt/dL9xwimRGlpWh9lqS6gcHazuSHhSgnRHsHLANEiyboIcyhWh2Hg==", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^7.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3253,6 +3647,29 @@ } } }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", + "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -4018,6 +4435,15 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.4.tgz", + "integrity": "sha512-ZchYkbieA+7tnxwX/SCBySx9WwvWR8TaP5tb2jRAzwvLb/rWchGw3v0w3pqUbUvj0GCwW2Xz/AVPSk6kUGctXQ==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -4462,6 +4888,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.4.tgz", "integrity": "sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/ws": { "version": "8.5.7", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz", @@ -6041,6 +6472,11 @@ "node": ">= 6" } }, + "node_modules/chroma-js": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz", + "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==" + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -9081,6 +9517,19 @@ "@babel/runtime": "^7.7.6" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -15014,6 +15463,62 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-nice-avatar": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/react-nice-avatar/-/react-nice-avatar-1.4.1.tgz", + "integrity": "sha512-IKC51UTBbPm7rVsFNCiSEs/oP7W2/scod7/s8xCxqF/yMGS+Xs5HDAVxpyTEwv2iCYVoBq8H/E8SnbqwnWvupA==", + "dependencies": { + "@babel/runtime": "^7.14.3", + "chroma-js": "^2.1.2", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, + "node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -15197,6 +15702,22 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", @@ -15342,6 +15863,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -17214,6 +17740,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/client/package.json b/client/package.json index 56e09b9..1de9c26 100644 --- a/client/package.json +++ b/client/package.json @@ -3,6 +3,9 @@ "version": "0.1.0", "private": true, "dependencies": { + "@dicebear/collection": "^7.0.1", + "@dicebear/core": "^7.0.1", + "@reduxjs/toolkit": "^1.9.6", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -13,6 +16,8 @@ "react": "^18.2.0", "react-chat-elements": "^12.0.10", "react-dom": "^18.2.0", + "react-nice-avatar": "^1.4.1", + "react-redux": "^8.1.3", "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", "react-scroll": "^1.9.0", diff --git a/client/src/components/App/App.js b/client/src/components/App/App.js index fcb0b4a..e9d65e2 100644 --- a/client/src/components/App/App.js +++ b/client/src/components/App/App.js @@ -1,25 +1,28 @@ -import React, { useState, useEffect } from 'react'; -import Header from '../Header/Header'; -import Main from '../Main/Main'; -import { Route, Routes, useNavigate, useLocation, Navigate } from 'react-router-dom'; -import ProtectedRoute from '../ProtectedRoute/ProtectedRoute'; -import Footer from '../Footer/Footer'; -import CurrentUserContext from '../../contexts/CurrentUserContext'; -import Register from '../Register/Register'; -import Login from '../Login/Login'; -import * as api from '../../utils/MainApi'; -import Profile from '../Profile/Profile'; -import Dashboard from '../Dashboard/Dashboard'; -//import Chat from '../Chat/Chat'; -import Game from '../Game/Game'; -import Match from '../Game/Match/Match'; -import NoMatch from '../Game/NoMatch/NoMatch'; -import InfoTooltip from '../InfoTooltip/InfoTooltip'; -import InfoTooltipEditProfile from '../InfoTooltipEditProfile/InfoTooltipEditProfile'; -import NotFound from '../NotFound/NotFound'; -import MatchList from '../MatchList/MatchList'; -import MentorBot from '../MentorBot/MentorBot'; -import './App.css'; +import React, { useState, useEffect } from "react"; +import Header from "../Header/Header"; +import Main from "../Main/Main"; +import { + Route, + Routes, + useNavigate, + useLocation, + Navigate, +} from "react-router-dom"; +import ProtectedRoute from "../ProtectedRoute/ProtectedRoute"; +import Footer from "../Footer/Footer"; +import CurrentUserContext from "../../contexts/CurrentUserContext"; +import Register from "../Register/Register"; +import Login from "../Login/Login"; +import * as api from "../../utils/MainApi"; +import Profile from "../Profile/Profile"; +import Dashboard from "../Dashboard/Dashboard"; +import Chat from "../Chat/Chat"; +import Game from "../Game/Game"; +import Match from "../Game/Match/Match"; +import InfoTooltip from "../InfoTooltip/InfoTooltip"; +import InfoTooltipEditProfile from "../InfoTooltipEditProfile/InfoTooltipEditProfile"; +import NotFound from "../NotFound/NotFound"; +import "./App.css"; function App() { const navigate = useNavigate(); @@ -31,7 +34,10 @@ function App() { const [isUpdate, setIsUpdate] = useState(false); const [isSuccess, setIsSuccess] = useState(false); const [isInfoToolTipPopupOpen, setInfoToolTipPopupOpen] = useState(false); - const [isInfoTooltipEditProfilePopupOpen, setInfoTooltipEditProfilePopupOpen] = useState(false); + const [ + isInfoTooltipEditProfilePopupOpen, + setInfoTooltipEditProfilePopupOpen, + ] = useState(false); // show data on auth // useEffect(() => { @@ -56,18 +62,18 @@ function App() { // }, [isLoggedIn]); useEffect(() => { - const jwt = localStorage.getItem('jwt'); + const jwt = localStorage.getItem("jwt"); if (jwt) { api .getContent(jwt) - .then(res => { + .then((res) => { if (res) { setIsLoggedIn(true); // localStorage.removeItem('allMovies'); } navigate(path); }) - .catch(error => { + .catch((error) => { console.log(error); }); } @@ -83,7 +89,7 @@ function App() { setInfoToolTipPopupOpen(true); setIsSuccess(true); }) - .catch(error => { + .catch((error) => { setInfoToolTipPopupOpen(true); setIsSuccess(false); console.log(error); @@ -98,16 +104,16 @@ function App() { setIsLoading(true); api .authorize(email, password) - .then(res => { + .then((res) => { if (res) { setIsSuccess(true); setInfoToolTipPopupOpen(true); - localStorage.setItem('jwt', res.token); - navigate('/dashboard', { replace: true }); + localStorage.setItem("jwt", res.token); + navigate("/dashboard", { replace: true }); setIsLoggedIn(true); } }) - .catch(error => { + .catch((error) => { setInfoToolTipPopupOpen(true); setIsSuccess(false); console.log(error); @@ -150,12 +156,12 @@ function App() { setIsLoading(true); api .setUserInfo(userInfo) - .then(data => { + .then((data) => { setInfoTooltipEditProfilePopupOpen(true); setIsUpdate(true); setCurrentUser(data); }) - .catch(error => { + .catch((error) => { setInfoTooltipEditProfilePopupOpen(true); setIsUpdate(false); console.log(error); @@ -168,7 +174,7 @@ function App() { // handle auth error function handleAuthorizationError(error) { - if (error === 'Error: 401') { + if (error === "Error: 401") { handleLogout(); } } @@ -185,14 +191,14 @@ function App() { // Close on ESC useEffect(() => { function closeByEscapePopups(evt) { - if (evt.key === 'Escape') { + if (evt.key === "Escape") { closeAllPopups(); } } if (isOpen) { - document.addEventListener('keydown', closeByEscapePopups); + document.addEventListener("keydown", closeByEscapePopups); return () => { - document.removeEventListener('keydown', closeByEscapePopups); + document.removeEventListener("keydown", closeByEscapePopups); }; } }, [isOpen]); @@ -207,13 +213,13 @@ function App() { // When you logout all the data is cleaned const handleLogout = () => { setIsLoggedIn(false); - localStorage.removeItem('jwt'); - localStorage.removeItem('movies'); - localStorage.removeItem('movieSearch'); - localStorage.removeItem('shortMovies'); - localStorage.removeItem('allMovies'); + localStorage.removeItem("jwt"); + localStorage.removeItem("movies"); + localStorage.removeItem("movieSearch"); + localStorage.removeItem("shortMovies"); + localStorage.removeItem("allMovies"); localStorage.clear(); - navigate('/'); + navigate("/"); }; return ( @@ -222,7 +228,7 @@ function App() {
{data}
+ > + ); + } + + // Export an `ErrorBoundary` directly instead of needing to create a React Element from it + export function ErrorBoundary() { + let error = useRouteError(); + return isRouteErrorResponse(error) ? ( +