├── log └── .keep ├── storage └── .keep ├── tmp └── .keep ├── vendor └── .keep ├── lib ├── assets │ └── .keep └── tasks │ ├── .keep │ ├── company.rake │ ├── format.rake │ └── graphql.rake ├── public ├── favicon.ico ├── apple-touch-icon.png ├── apple-touch-icon-precomposed.png ├── images │ └── avatar.jpg └── robots.txt ├── .ruby-version ├── app ├── assets │ ├── images │ │ ├── .keep │ │ └── dummy │ │ │ ├── photos │ │ │ ├── Alana.jpg │ │ │ └── Cassie.jpg │ │ │ └── logos │ │ │ ├── drag.svg │ │ │ └── drag-black.svg │ └── config │ │ └── manifest.js ├── graphql │ ├── types │ │ ├── .keep │ │ ├── base_input_object.rb │ │ ├── role_type.rb │ │ ├── current_user_type.rb │ │ ├── user_list_type.rb │ │ ├── task_section_type.rb │ │ ├── user_details_type.rb │ │ ├── company_users_type.rb │ │ ├── search_company_users_type.rb │ │ ├── file_base_type.rb │ │ ├── validation_error.rb │ │ ├── form_error_type.rb │ │ ├── search_companies_type.rb │ │ ├── task_message_alert_type.rb │ │ ├── team_type.rb │ │ ├── subscription_type.rb │ │ ├── user_for_password_reset_type.rb │ │ ├── task_attributes.rb │ │ ├── roles_user_type.rb │ │ ├── task_message_type.rb │ │ ├── user_type.rb │ │ └── list_type.rb │ ├── mutations │ │ ├── .keep │ │ ├── delete_task_message.rb │ │ ├── delete_lists.rb │ │ ├── delete_tasks.rb │ │ ├── sign_out_user.rb │ │ ├── update_task_message.rb │ │ ├── send_reset_password_instructions.rb │ │ ├── create_team.rb │ │ ├── update_task.rb │ │ ├── update_user_data.rb │ │ ├── create_task.rb │ │ └── remove_team_member.rb │ └── prepdd_schema.rb ├── models │ ├── concerns │ │ └── .keep │ ├── file_label.rb │ ├── file_message.rb │ ├── file_message_alert.rb │ ├── task_section.rb │ ├── user_notification_scope.rb │ ├── file_base.rb │ ├── subscription.rb │ ├── user_notification_frequency.rb │ ├── teams_user.rb │ ├── file_task.rb │ ├── users_company.rb │ ├── file_version_task.rb │ ├── task_message_alert.rb │ ├── task_owner.rb │ ├── lists_user.rb │ ├── broker_company.rb │ ├── file_version.rb │ ├── parent_company.rb │ ├── task_message.rb │ ├── roles_user.rb │ ├── team.rb │ ├── role.rb │ ├── list.rb │ └── application_record.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── welcome_controller.rb │ ├── application_controller.rb │ ├── api_controller.rb │ ├── api │ │ ├── tasks_controller.rb │ │ ├── file_versions_controller.rb │ │ ├── companies_controller.rb │ │ └── users_controller.rb │ └── graphql_controller.rb ├── views │ ├── layouts │ │ ├── mailer.text.erb │ │ ├── mailer.html.erb │ │ └── application.html.erb │ ├── welcome │ │ └── index.html.erb │ └── devise │ │ └── mailer │ │ ├── password_change.html.erb │ │ ├── confirmation_instructions.html.erb │ │ ├── unlock_instructions.html.erb │ │ ├── email_changed.html.erb │ │ └── reset_password_instructions.html.erb ├── helpers │ └── application_helper.rb ├── jobs │ └── application_job.rb ├── javascript │ ├── src │ │ ├── modules │ │ │ ├── layout │ │ │ │ ├── index.tsx │ │ │ │ └── components │ │ │ │ │ ├── TopBar │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── components │ │ │ │ │ │ └── StyledBadge.tsx │ │ │ │ │ └── SideBar │ │ │ │ │ └── index.tsx │ │ │ ├── list │ │ │ │ ├── ListPage │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── components │ │ │ │ │ │ └── styled │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── StyledTabs.tsx │ │ │ │ │ │ ├── StyledTableCell.tsx │ │ │ │ │ │ ├── StyledTableRow.tsx │ │ │ │ │ │ ├── StyledButton.tsx │ │ │ │ │ │ └── StyledTab.tsx │ │ │ │ └── CreateListPage │ │ │ │ │ ├── components │ │ │ │ │ ├── Header │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── CreateListStep │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── components │ │ │ │ │ │ │ └── Alert.tsx │ │ │ │ │ ├── CreateTemplateStep │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── SelectTemplateStep │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── components │ │ │ │ │ │ ├── MAPane.tsx │ │ │ │ │ │ ├── LegalPane.tsx │ │ │ │ │ │ └── FinancePane.tsx │ │ │ │ │ └── index.tsx │ │ │ ├── task │ │ │ │ ├── TaskPage │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── components │ │ │ │ │ │ ├── Message │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── SidePanel │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── TaskTable │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── components │ │ │ │ │ │ │ └── StyledBadge.tsx │ │ │ │ │ │ └── TaskToolbar │ │ │ │ │ │ └── index.tsx │ │ │ │ └── CreateTaskPage │ │ │ │ │ ├── components │ │ │ │ │ ├── Body │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── Header │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── CreateTaskPage.tsx │ │ │ ├── user │ │ │ │ ├── index.tsx │ │ │ │ ├── components │ │ │ │ │ ├── ProfilePane │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── NotificationPane │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── components │ │ │ │ │ │ └── Dropdown.tsx │ │ │ │ │ │ └── NotificationPane.tsx │ │ │ │ ├── __tests__ │ │ │ │ │ └── UserProfile.test.tsx │ │ │ │ └── UserProfile.tsx │ │ │ ├── file │ │ │ │ └── FilesPage │ │ │ │ │ ├── components │ │ │ │ │ ├── Body │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── Body.tsx │ │ │ │ │ └── Header │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── FilesPage.tsx │ │ │ ├── team │ │ │ │ ├── index.tsx │ │ │ │ └── components │ │ │ │ │ ├── Searchbar │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── DetailPane │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── TableHeader │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── TableHeader.tsx │ │ │ │ │ ├── TableToolbar │ │ │ │ │ └── index.tsx │ │ │ │ │ └── styled │ │ │ │ │ ├── StyledTableCell.tsx │ │ │ │ │ └── StyledTableRow.tsx │ │ │ ├── company │ │ │ │ ├── index.tsx │ │ │ │ └── components │ │ │ │ │ ├── FormPanel │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── components │ │ │ │ │ │ ├── StyledTableRow.tsx │ │ │ │ │ │ └── StyledTableCell.tsx │ │ │ │ │ └── UploadPanel │ │ │ │ │ └── index.tsx │ │ │ ├── dashboard │ │ │ │ └── index.tsx │ │ │ ├── common │ │ │ │ ├── NotAvailable.tsx │ │ │ │ ├── NotFoundPage.tsx │ │ │ │ ├── LoadingFallback.tsx │ │ │ │ ├── DefaultUserImage.tsx │ │ │ │ └── ConfirmModal.tsx │ │ │ └── route │ │ │ │ ├── team.tsx │ │ │ │ ├── user.tsx │ │ │ │ ├── list.tsx │ │ │ │ ├── task.tsx │ │ │ │ ├── company.tsx │ │ │ │ └── app.tsx │ │ ├── __mocks__ │ │ │ └── emptyAsset.ts │ │ ├── constants │ │ │ ├── keys.ts │ │ │ ├── third_party_types │ │ │ │ └── react-linkedin-login-oauth2.ts │ │ │ ├── theme.ts │ │ │ └── types.ts │ │ ├── setupEnzyme.ts │ │ ├── graphql │ │ │ ├── queries │ │ │ │ ├── AllRoles.ts │ │ │ │ ├── UserLists.ts │ │ │ │ ├── AllTemplates.ts │ │ │ │ ├── UserForPasswordReset.ts │ │ │ │ ├── __generated__ │ │ │ │ │ ├── AllRoles.ts │ │ │ │ │ ├── UserForPasswordReset.ts │ │ │ │ │ ├── publicTaskMessages.ts │ │ │ │ │ ├── UserLists.ts │ │ │ │ │ ├── privateTaskMessages.ts │ │ │ │ │ ├── AllTemplates.ts │ │ │ │ │ └── UserDetails.ts │ │ │ │ ├── PublicTaskMessages.ts │ │ │ │ ├── PrivateTaskMessages.ts │ │ │ │ ├── UserDetails.ts │ │ │ │ ├── CompanySettings.ts │ │ │ │ ├── CurrentUser.ts │ │ │ │ ├── CompanyUsers.ts │ │ │ │ └── UserTasks.ts │ │ │ ├── mutations │ │ │ │ ├── SignOutUser.ts │ │ │ │ ├── DeleteTasks.ts │ │ │ │ ├── CreateTask.ts │ │ │ │ ├── CreateCompany.ts │ │ │ │ ├── UpdateUserPassword.ts │ │ │ │ ├── __generated__ │ │ │ │ │ ├── deleteTasks.ts │ │ │ │ │ ├── SignOutUser.ts │ │ │ │ │ ├── CreateTask.ts │ │ │ │ │ ├── AddListOwner.ts │ │ │ │ │ ├── UpdateUserPassword.ts │ │ │ │ │ ├── UpdateUserData.ts │ │ │ │ │ ├── CreateCompany.ts │ │ │ │ │ ├── SendPasswordResetInstructions.ts │ │ │ │ │ ├── CreatePublicTaskMessage.ts │ │ │ │ │ ├── CreatePrivateTaskMessage.ts │ │ │ │ │ ├── InviteNewCompanyToList.ts │ │ │ │ │ ├── CreateList.ts │ │ │ │ │ └── SignInUser.ts │ │ │ │ ├── SendPasswordResetInstructions.ts │ │ │ │ ├── AddListOwner.ts │ │ │ │ ├── UpdateUserData.ts │ │ │ │ ├── CreatePrivateTaskMessage.ts │ │ │ │ ├── CreateList.ts │ │ │ │ ├── CreatePublicTaskMessage.ts │ │ │ │ ├── SignInUser.ts │ │ │ │ ├── InviteNewCompanyToList.ts │ │ │ │ ├── RemoveTeamMember.ts │ │ │ │ ├── CreateAssociatedCompany.ts │ │ │ │ ├── RemoveCompanyMember.ts │ │ │ │ ├── UpdateTeamMember.ts │ │ │ │ ├── AddTeamMember.ts │ │ │ │ ├── UpdateTask.ts │ │ │ │ ├── AddTaskOwners.ts │ │ │ │ └── SignUpUser.ts │ │ │ ├── __generated__ │ │ │ │ └── globalTypes.ts │ │ │ └── graphqlHelpers.ts │ │ ├── helpers │ │ │ ├── queries.ts │ │ │ ├── roleHelpers.ts │ │ │ └── __generated__ │ │ │ │ └── SearchCompanyUsers.ts │ │ └── application.tsx │ └── packs │ │ └── application_pack.tsx ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── mailers │ └── application_mailer.rb ├── workers │ └── company │ │ ├── company_s3_bucket_creation_worker.rb │ │ ├── company_kms_creation_worker.rb │ │ └── remove_un_used_companies_worker.rb └── policies │ └── application_policy.rb ├── .browserslistrc ├── .rspec ├── db ├── data_schema.rb ├── migrate │ ├── 20190819034701_rename_role_title.rb │ ├── 20190812095935_add_company_id_to_teams.rb │ ├── 20190903193053_remove_task_section_field.rb │ ├── 20190913200833_add_list_number_to_tasks.rb │ ├── 20190915022826_add_company_id_to_task_messages.rb │ ├── 20190708203246_create_roles.rb │ ├── 20190903100322_add_section_field_in_task.rb │ ├── 20190817122452_add_title_to_subcription.rb │ ├── 20190904160018_add_section_id_in_task.rb │ ├── 20190904191854_adteam_id_in_lists_user.rb │ ├── 20190819141140_add_team_id_to_user_roles.rb │ ├── 20190910220419_add_reviewer_type_task_owner.rb │ ├── 20190702001204_add_fields_to_user.rb │ ├── 20190821153008_add_last_company_id_to_user.rb │ ├── 20190903193648_create_task_sections.rb │ ├── 20190913165628_add_is_public_to_task.rb │ ├── 20190911184105_add_rank_on_list.rb │ ├── 20190916060852_create_file_version_tasks.rb │ ├── 20190717200222_create_teams.rb │ ├── 20190708203644_create_roles_users.rb │ ├── 20190717200930_create_teams_users.rb │ ├── 20190904104719_create_lists_users.rb │ ├── 20190911145904_create_user_notification_scopes.rb │ ├── 20190911051521_create_file_bases.rb │ ├── 20190814110824_create_users_companies.rb │ ├── 20190911052855_create_file_tasks.rb │ ├── 20190911153424_create_user_notification_frequencies.rb │ ├── 20190817111651_add_company_settings.rb │ ├── 20190712213325_add_uuid_to_users_for_omani_auth.rb │ ├── 20190824162739_create_parent_companies.rb │ ├── 20190730211803_add_s3_bucket_location_to_company.rb │ ├── 20190824161311_create_broker_companies.rb │ ├── 20190909181608_create_task_owners.rb │ ├── 20190911154027_create_task_messages.rb │ ├── 20190710202528_add_user_association_with_company.rb │ ├── 20190911052002_create_file_message_alerts.rb │ ├── 20190911051917_create_file_labels.rb │ ├── 20190911051944_create_file_messages.rb │ ├── 20190911155050_create_task_message_alerts.rb │ ├── 20190706032837_add_full_name_to_user.rb │ ├── 20190731170005_add_sessions_table.rb │ ├── 20190708151108_create_companies.rb │ ├── 20190911052305_create_file_versions.rb │ ├── 20190830185711_create_tasks.rb │ ├── 20190830182008_create_lists.rb │ ├── 20190726063616_create_subscriptions.rb │ ├── 20190715093652_add_bio_and_notification_fields_to_user.rb │ └── 20190822034212_create_active_storage_tables.active_storage.rb └── data │ └── 20190708204424_add_basic_roles.rb ├── spec ├── factories │ ├── file_tasks.rb │ ├── teams_users.rb │ ├── subscriptions.rb │ ├── task_messages.rb │ ├── users_companies.rb │ ├── task_message_alerts.rb │ ├── user_notification_frequencies.rb │ ├── companies.rb │ ├── teams.rb │ ├── file_bases.rb │ ├── file_message_alerts.rb │ ├── roles.rb │ ├── users.rb │ ├── file_messages.rb │ ├── file_labels.rb │ └── file_versions.rb ├── support │ ├── factory_bot.rb │ └── graphql_spec_helper.rb ├── models │ ├── team_spec.rb │ ├── file_base_spec.rb │ ├── file_label_spec.rb │ ├── file_task_spec.rb │ ├── teams_user_spec.rb │ ├── file_message_spec.rb │ ├── file_version_spec.rb │ ├── subscription_spec.rb │ ├── task_message_spec.rb │ ├── users_company_spec.rb │ ├── file_message_alert_spec.rb │ ├── task_message_alert_spec.rb │ ├── user_notification_frequency_spec.rb │ ├── user_spec.rb │ └── company_spec.rb ├── workers │ └── company │ │ ├── company_kms_creation_worker_spec.rb │ │ ├── remove_un_used_companies_worker_spec.rb │ │ └── company_s3_bucket_creation_worker_spec.rb ├── controllers │ └── welcome_controller_spec.rb └── graphql │ ├── types │ ├── current_user_type_spec.rb │ ├── form_error_type_spec.rb │ └── team_type_spec.rb │ └── mutations │ ├── create_company_spec.rb │ ├── sign_up_user_spec.rb │ ├── update_user_data.rb │ ├── update_company_spec.rb │ └── create_team_spec.rb ├── Procfile ├── Procfile.dev ├── .prettierrc ├── bin ├── bundle ├── rake ├── rails ├── yarn ├── webpack ├── webpack-dev-server ├── spring ├── update └── setup ├── config ├── spring.rb ├── initializers │ ├── session_store.rb │ ├── sidekiq.rb │ ├── mime_types.rb │ ├── aws.rb │ ├── filter_parameter_logging.rb │ ├── application_controller_renderer.rb │ ├── cookies_serializer.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── assets.rb │ ├── inflections.rb │ └── content_security_policy.rb ├── environment.rb ├── webpack │ ├── test.js │ ├── development.js │ ├── production.js │ ├── environment.js │ └── loaders │ │ └── typescript.js ├── boot.rb ├── cable.yml ├── storage.yml ├── credentials.yml.enc ├── database.yml ├── routes.rb ├── locales │ └── en.yml └── application.rb ├── config.ru ├── postcss.config.js ├── Rakefile ├── jest.config.js ├── tsconfig.json ├── app.json └── .gitignore /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /storage/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.6.3 -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/graphql/types/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /app/graphql/mutations/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | -------------------------------------------------------------------------------- /app/views/welcome/index.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper; end 2 | -------------------------------------------------------------------------------- /app/models/file_label.rb: -------------------------------------------------------------------------------- 1 | class FileLabel < ApplicationRecord; end 2 | -------------------------------------------------------------------------------- /app/models/file_message.rb: -------------------------------------------------------------------------------- 1 | class FileMessage < ApplicationRecord; end 2 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base; end 2 | -------------------------------------------------------------------------------- /app/models/file_message_alert.rb: -------------------------------------------------------------------------------- 1 | class FileMessageAlert < ApplicationRecord; end 2 | -------------------------------------------------------------------------------- /db/data_schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | DataMigrate::Data.define(version: 20190708204424) 3 | -------------------------------------------------------------------------------- /app/models/task_section.rb: -------------------------------------------------------------------------------- 1 | class TaskSection < ApplicationRecord 2 | has_many :tasks 3 | end 4 | -------------------------------------------------------------------------------- /app/models/user_notification_scope.rb: -------------------------------------------------------------------------------- 1 | class UserNotificationScope < ApplicationRecord; end 2 | -------------------------------------------------------------------------------- /app/models/file_base.rb: -------------------------------------------------------------------------------- 1 | class FileBase < ApplicationRecord 2 | has_one_attached :template 3 | end 4 | -------------------------------------------------------------------------------- /app/models/subscription.rb: -------------------------------------------------------------------------------- 1 | class Subscription < ApplicationRecord 2 | has_many :companies 3 | end 4 | -------------------------------------------------------------------------------- /app/models/user_notification_frequency.rb: -------------------------------------------------------------------------------- 1 | class UserNotificationFrequency < ApplicationRecord; end 2 | -------------------------------------------------------------------------------- /public/images/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/PrepDD/HEAD/public/images/avatar.jpg -------------------------------------------------------------------------------- /spec/factories/file_tasks.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :file_task do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/factories/teams_users.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :teams_user do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/support/factory_bot.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure { |config| config.include FactoryBot::Syntax::Methods } 2 | -------------------------------------------------------------------------------- /app/graphql/types/base_input_object.rb: -------------------------------------------------------------------------------- 1 | class Types::BaseInputObject < GraphQL::Schema::InputObject; end 2 | -------------------------------------------------------------------------------- /spec/factories/subscriptions.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :subscription do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/factories/task_messages.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :task_message do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/factories/users_companies.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :users_company do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/javascript/src/modules/layout/index.tsx: -------------------------------------------------------------------------------- 1 | import Layout from './Layout'; 2 | 3 | export default Layout; 4 | -------------------------------------------------------------------------------- /app/controllers/welcome_controller.rb: -------------------------------------------------------------------------------- 1 | class WelcomeController < ApplicationController 2 | def index; end 3 | end 4 | -------------------------------------------------------------------------------- /app/models/teams_user.rb: -------------------------------------------------------------------------------- 1 | class TeamsUser < ApplicationRecord 2 | belongs_to :team 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /spec/factories/task_message_alerts.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :task_message_alert do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: bundle exec rake db:migrate 2 | web: bundle exec rails server 3 | worker: bundle exec sidekiq -t 25 4 | -------------------------------------------------------------------------------- /app/javascript/src/__mocks__/emptyAsset.ts: -------------------------------------------------------------------------------- 1 | // File left blank 2 | 3 | export default 'assets-not-supported-in-jest'; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/ListPage/index.tsx: -------------------------------------------------------------------------------- 1 | import ListPage from './ListPage'; 2 | 3 | export default ListPage; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/TaskPage/index.tsx: -------------------------------------------------------------------------------- 1 | import TaskPage from './TaskPage'; 2 | 3 | export default TaskPage; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/user/index.tsx: -------------------------------------------------------------------------------- 1 | import UserProfile from './UserProfile'; 2 | 3 | export default UserProfile; 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include Pundit 3 | end 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/file/FilesPage/components/Body/index.tsx: -------------------------------------------------------------------------------- 1 | import Body from './Body'; 2 | 3 | export default Body; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/file/FilesPage/index.tsx: -------------------------------------------------------------------------------- 1 | import FilesPage from './FilesPage'; 2 | 3 | export default FilesPage; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/layout/components/TopBar/index.tsx: -------------------------------------------------------------------------------- 1 | import TopBar from './TopBar'; 2 | 3 | export default TopBar; 4 | -------------------------------------------------------------------------------- /app/models/file_task.rb: -------------------------------------------------------------------------------- 1 | class FileTask < ApplicationRecord 2 | belongs_to :task 3 | belongs_to :file_version 4 | end 5 | -------------------------------------------------------------------------------- /app/models/users_company.rb: -------------------------------------------------------------------------------- 1 | class UsersCompany < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :company 4 | end 5 | -------------------------------------------------------------------------------- /Procfile.dev: -------------------------------------------------------------------------------- 1 | redis: redis-server /usr/local/etc/redis.conf 2 | web: bundle exec rails server 3 | webpack: bin/webpack-dev-server 4 | -------------------------------------------------------------------------------- /app/assets/images/dummy/photos/Alana.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/PrepDD/HEAD/app/assets/images/dummy/photos/Alana.jpg -------------------------------------------------------------------------------- /app/assets/images/dummy/photos/Cassie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/PrepDD/HEAD/app/assets/images/dummy/photos/Cassie.jpg -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base; end 3 | end 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/layout/components/SideBar/index.tsx: -------------------------------------------------------------------------------- 1 | import SideBar from './SideBar'; 2 | 3 | export default SideBar; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/CreateTaskPage/components/Body/index.tsx: -------------------------------------------------------------------------------- 1 | import Body from './Body'; 2 | 3 | export default Body; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/team/index.tsx: -------------------------------------------------------------------------------- 1 | import TeamManagement from './TeamManagement'; 2 | 3 | export default TeamManagement; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/company/index.tsx: -------------------------------------------------------------------------------- 1 | import CompanySettings from './CompanySettings'; 2 | 3 | export default CompanySettings; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/file/FilesPage/components/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/team/components/Searchbar/index.tsx: -------------------------------------------------------------------------------- 1 | import Searchbar from './Searchbar'; 2 | 3 | export default Searchbar; 4 | -------------------------------------------------------------------------------- /spec/factories/user_notification_frequencies.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user_notification_frequency do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base; end 3 | end 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/company/components/FormPanel/index.tsx: -------------------------------------------------------------------------------- 1 | import FormPanel from './FormPanel'; 2 | 3 | export default FormPanel; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/components/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/CreateTaskPage/components/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/TaskPage/components/Message/index.tsx: -------------------------------------------------------------------------------- 1 | import Message from './Message'; 2 | 3 | export default Message; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/team/components/DetailPane/index.tsx: -------------------------------------------------------------------------------- 1 | import DetailPane from './DetailPane'; 2 | 3 | export default DetailPane; 4 | -------------------------------------------------------------------------------- /app/models/file_version_task.rb: -------------------------------------------------------------------------------- 1 | class FileVersionTask < ApplicationRecord 2 | belongs_to :task 3 | belongs_to :file_version 4 | end 5 | -------------------------------------------------------------------------------- /app/models/task_message_alert.rb: -------------------------------------------------------------------------------- 1 | class TaskMessageAlert < ApplicationRecord 2 | belongs_to :task_message 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "bracketSpacing": false 7 | } -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/index.tsx: -------------------------------------------------------------------------------- 1 | import CreateListPage from './CreateListPage'; 2 | 3 | export default CreateListPage; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/CreateTaskPage/index.tsx: -------------------------------------------------------------------------------- 1 | import CreateTaskPage from './CreateTaskPage'; 2 | 3 | export default CreateTaskPage; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/TaskPage/components/SidePanel/index.tsx: -------------------------------------------------------------------------------- 1 | import SidePanel from './SidePanel'; 2 | 3 | export default SidePanel; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/TaskPage/components/TaskTable/index.tsx: -------------------------------------------------------------------------------- 1 | import TaskTable from './TaskTable'; 2 | 3 | export default TaskTable; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/team/components/TableHeader/index.tsx: -------------------------------------------------------------------------------- 1 | import TableHeader from './TableHeader'; 2 | 3 | export default TableHeader; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/user/components/ProfilePane/index.tsx: -------------------------------------------------------------------------------- 1 | import ProfilePane from './ProfilePane'; 2 | 3 | export default ProfilePane; 4 | -------------------------------------------------------------------------------- /app/models/task_owner.rb: -------------------------------------------------------------------------------- 1 | class TaskOwner < ApplicationRecord 2 | belongs_to :task_ownerable, polymorphic: true 3 | belongs_to :task 4 | end 5 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /spec/factories/companies.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :company do 3 | sequence(:name) { Faker::Company.name } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/javascript/src/modules/company/components/UploadPanel/index.tsx: -------------------------------------------------------------------------------- 1 | import UploadPanel from './UploadPanel'; 2 | 3 | export default UploadPanel; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/team/components/TableToolbar/index.tsx: -------------------------------------------------------------------------------- 1 | import TableToolbar from './TableToolbar'; 2 | 3 | export default TableToolbar; 4 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/TaskPage/components/TaskToolbar/index.tsx: -------------------------------------------------------------------------------- 1 | import TaskToolbar from './TaskToolbar'; 2 | 3 | export default TaskToolbar; 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/javascript/src/modules/user/components/NotificationPane/index.tsx: -------------------------------------------------------------------------------- 1 | import NotificationPane from './NotificationPane'; 2 | 3 | export default NotificationPane; 4 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.session_store :active_record_store, 2 | key: 'prepdd_session' 3 | -------------------------------------------------------------------------------- /spec/factories/teams.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :team do 3 | sequence(:name) { Faker::Company.name } 4 | association :company 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/models/team_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Team, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /app/javascript/src/constants/keys.ts: -------------------------------------------------------------------------------- 1 | export const KEYS = { 2 | ARROW_LEFT: 37, 3 | ARROW_UP: 38, 4 | ARROW_RIGHT: 39, 5 | ARROW_DOWN: 40, 6 | ENTER: 13, 7 | }; 8 | -------------------------------------------------------------------------------- /app/javascript/src/modules/dashboard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Dashboard() { 4 | return
Dashboard Page
; 5 | } 6 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/components/CreateListStep/index.tsx: -------------------------------------------------------------------------------- 1 | import CreateListStep from './CreateListStep'; 2 | 3 | export default CreateListStep; 4 | -------------------------------------------------------------------------------- /app/models/lists_user.rb: -------------------------------------------------------------------------------- 1 | class ListsUser < ApplicationRecord 2 | belongs_to :list 3 | belongs_to :user, optional: true 4 | belongs_to :team, optional: true 5 | end 6 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/password_change.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

We're contacting you to notify you that your password has been changed.

4 | -------------------------------------------------------------------------------- /spec/factories/file_bases.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :file_basis, class: 'FileBase' do 3 | is_active { false } 4 | is_template { false } 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/models/file_base_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe FileBase, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/file_label_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe FileLabel, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/file_task_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe FileTask, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/teams_user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe TeamsUser, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /app/javascript/src/modules/common/NotAvailable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function NotAvailable() { 4 | return

Not Available Data

; 5 | } 6 | -------------------------------------------------------------------------------- /app/javascript/src/setupEnzyme.ts: -------------------------------------------------------------------------------- 1 | import {configure} from 'enzyme'; 2 | import EnzymeAdapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({adapter: new EnzymeAdapter()}); 5 | -------------------------------------------------------------------------------- /db/migrate/20190819034701_rename_role_title.rb: -------------------------------------------------------------------------------- 1 | class RenameRoleTitle < ActiveRecord::Migration[5.2] 2 | def change 3 | rename_column :roles, :title, :name 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/file_message_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe FileMessage, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/file_version_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe FileVersion, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/subscription_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Subscription, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/task_message_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe TaskMessage, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/users_company_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe UsersCompany, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/components/CreateTemplateStep/index.tsx: -------------------------------------------------------------------------------- 1 | import CreateTemplateStep from './CreateTemplateStep'; 2 | 3 | export default CreateTemplateStep; 4 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/components/SelectTemplateStep/index.tsx: -------------------------------------------------------------------------------- 1 | import SelectTemplateStep from './SelectTemplateStep'; 2 | 3 | export default SelectTemplateStep; 4 | -------------------------------------------------------------------------------- /app/models/broker_company.rb: -------------------------------------------------------------------------------- 1 | class BrokerCompany < ApplicationRecord 2 | belongs_to :child_broker, class_name: 'Company' 3 | belongs_to :parent_broker, class_name: 'Company' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/file_version.rb: -------------------------------------------------------------------------------- 1 | class FileVersion < ApplicationRecord 2 | has_many :file_version_tasks 3 | has_many :tasks, through: :file_version_tasks 4 | has_one_attached :file 5 | end 6 | -------------------------------------------------------------------------------- /app/models/parent_company.rb: -------------------------------------------------------------------------------- 1 | class ParentCompany < ApplicationRecord 2 | belongs_to :child_company, class_name: 'Company' 3 | belongs_to :parent_company, class_name: 'Company' 4 | end 5 | -------------------------------------------------------------------------------- /spec/models/file_message_alert_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe FileMessageAlert, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/task_message_alert_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe TaskMessageAlert, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /app/graphql/prepdd_schema.rb: -------------------------------------------------------------------------------- 1 | class PrepddSchema < GraphQL::Schema 2 | mutation(Types::MutationType) 3 | query(Types::QueryType) 4 | 5 | max_depth 6 6 | # max_complexity 1000 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20190812095935_add_company_id_to_teams.rb: -------------------------------------------------------------------------------- 1 | class AddCompanyIdToTeams < ActiveRecord::Migration[5.2] 2 | change_table :teams do |t| 3 | t.belongs_to :company 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/file_message_alerts.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :file_message_alert do 3 | file_message_id { 1 } 4 | user_id { 1 } 5 | is_read { false } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/javascript/src/constants/third_party_types/react-linkedin-login-oauth2.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-linkedin-login-oauth2' { 2 | export var LinkedIn: any; 3 | export var LinkedInPopUp: any; 4 | } 5 | -------------------------------------------------------------------------------- /config/initializers/sidekiq.rb: -------------------------------------------------------------------------------- 1 | Sidekiq.configure_server { |config| config.redis = { url: ENV['REDIS_URL'] } } 2 | 3 | Sidekiq.configure_client { |config| config.redis = { url: ENV['REDIS_URL'] } } 4 | -------------------------------------------------------------------------------- /config/webpack/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'; 2 | 3 | const environment = require('./environment'); 4 | 5 | module.exports = environment.toWebpackConfig(); 6 | -------------------------------------------------------------------------------- /db/migrate/20190903193053_remove_task_section_field.rb: -------------------------------------------------------------------------------- 1 | class RemoveTaskSectionField < ActiveRecord::Migration[5.2] 2 | def change 3 | remove_column :tasks, :section, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190913200833_add_list_number_to_tasks.rb: -------------------------------------------------------------------------------- 1 | class AddListNumberToTasks < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :tasks, :list_number, :bigint 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /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/webpack/development.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'; 2 | 3 | const environment = require('./environment'); 4 | 5 | module.exports = environment.toWebpackConfig(); 6 | -------------------------------------------------------------------------------- /config/webpack/production.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'; 2 | 3 | const environment = require('./environment'); 4 | 5 | module.exports = environment.toWebpackConfig(); 6 | -------------------------------------------------------------------------------- /spec/factories/roles.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :role do 3 | name { 'admin' } 4 | 5 | trait :super_admin do 6 | name { 'super_admin' } 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/javascript/src/modules/common/NotFoundPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function NotFound(props: {path?: string; default: boolean}) { 4 | return
Page Not Found
; 5 | } 6 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user do 3 | sequence(:email) { |n| Faker::Internet.email.gsub(/@/, "#{n}@") } 4 | sequence(:full_name) { Faker::Name.name } 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/models/user_notification_frequency_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe UserNotificationFrequency, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /lib/tasks/company.rake: -------------------------------------------------------------------------------- 1 | namespace :company do 2 | desc 'Remove Unused Companies' 3 | task remove_un_used_companies: :environment do 4 | Company::RemoveUnUsedCompaniesWorker.perform_async 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/file_messages.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :file_message do 3 | user_id { 1 } 4 | file_version_id { 1 } 5 | message { 'MyText' } 6 | is_public { false } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/javascript/src/modules/user/components/NotificationPane/components/Dropdown.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 4 | -------------------------------------------------------------------------------- /db/migrate/20190915022826_add_company_id_to_task_messages.rb: -------------------------------------------------------------------------------- 1 | class AddCompanyIdToTaskMessages < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :task_messages, :company_id, :bigint 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/workers/company/company_kms_creation_worker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | RSpec.describe Company::CompanyKmsCreationWorker, type: :worker do 3 | pending "add some examples to (or delete) #{__FILE__}" 4 | end 5 | -------------------------------------------------------------------------------- /db/migrate/20190708203246_create_roles.rb: -------------------------------------------------------------------------------- 1 | class CreateRoles < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :roles do |t| 4 | t.string :title 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20190903100322_add_section_field_in_task.rb: -------------------------------------------------------------------------------- 1 | class AddSectionFieldInTask < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :tasks do |t| 4 | t.string :section 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/task_message.rb: -------------------------------------------------------------------------------- 1 | class TaskMessage < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :task 4 | # a task message only belongs to a company if it's 'internal' 5 | belongs_to :company, optional: true 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190817122452_add_title_to_subcription.rb: -------------------------------------------------------------------------------- 1 | class AddTitleToSubcription < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :subscriptions do |t| 4 | t.string :name 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20190904160018_add_section_id_in_task.rb: -------------------------------------------------------------------------------- 1 | class AddSectionIdInTask < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :tasks do |t| 4 | t.belongs_to :task_section 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20190904191854_adteam_id_in_lists_user.rb: -------------------------------------------------------------------------------- 1 | class AdteamIdInListsUser < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :lists_users do |t| 4 | t.belongs_to :team 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/file_labels.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :file_label do 3 | description { 'MyText' } 4 | file_label_color { 'MyString' } 5 | is_active { false } 6 | is_public { false } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/workers/company/remove_un_used_companies_worker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | RSpec.describe Company::RemoveUnUsedCompaniesWorker, type: :worker do 3 | pending "add some examples to (or delete) #{__FILE__}" 4 | end 5 | -------------------------------------------------------------------------------- /app/graphql/types/role_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class RoleType < GraphQL::Schema::Object 3 | description 'All Available Roles' 4 | 5 | field :id, ID, null: false 6 | field :name, String, null: false 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /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 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/webpack/environment.js: -------------------------------------------------------------------------------- 1 | const {environment} = require('@rails/webpacker'); 2 | const typescript = require('./loaders/typescript'); 3 | 4 | environment.loaders.prepend('typescript', typescript); 5 | module.exports = environment; 6 | -------------------------------------------------------------------------------- /db/migrate/20190819141140_add_team_id_to_user_roles.rb: -------------------------------------------------------------------------------- 1 | class AddTeamIdToUserRoles < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :roles_users do |t| 4 | t.belongs_to :company 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/workers/company/company_s3_bucket_creation_worker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | RSpec.describe Company::CompanyS3BucketCreationWorker, type: :worker do 3 | pending "add some examples to (or delete) #{__FILE__}" 4 | end 5 | -------------------------------------------------------------------------------- /db/migrate/20190910220419_add_reviewer_type_task_owner.rb: -------------------------------------------------------------------------------- 1 | class AddReviewerTypeTaskOwner < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :task_owners do |t| 4 | t.string :owner_type 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/graphql/types/current_user_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class CurrentUserType < GraphQL::Schema::Object 3 | description 'CurrentUser' 4 | 5 | field :id, String, null: false 6 | field :user, UserType, null: true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/graphql/types/user_list_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class UserListType < GraphQL::Schema::Object 3 | description 'CurrentUser' 4 | 5 | field :id, String, null: false 6 | field :lists, [ListType], null: true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /config/initializers/aws.rb: -------------------------------------------------------------------------------- 1 | require 'aws-sdk' 2 | Aws.config.update( 3 | credentials: 4 | Aws::Credentials.new( 5 | ENV['AWS_ACCESS_KEY_ID'], 6 | ENV['AWS_SECRET_ACCESS_KEY'] 7 | ), 8 | region: 'us-east-1' 9 | ) 10 | -------------------------------------------------------------------------------- /db/migrate/20190702001204_add_fields_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddFieldsToUser < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :users do |t| 4 | t.string :first_name 5 | t.string :last_name 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20190821153008_add_last_company_id_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddLastCompanyIdToUser < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :users do |t| 4 | t.bigint :last_viewed_company_id 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20190903193648_create_task_sections.rb: -------------------------------------------------------------------------------- 1 | class CreateTaskSections < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :task_sections do |t| 4 | t.string :name 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20190913165628_add_is_public_to_task.rb: -------------------------------------------------------------------------------- 1 | class AddIsPublicToTask < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :task_messages do |t| 4 | t.boolean :is_public, default: false 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: prepdd_production 11 | -------------------------------------------------------------------------------- /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 += %i[password] 5 | -------------------------------------------------------------------------------- /db/migrate/20190911184105_add_rank_on_list.rb: -------------------------------------------------------------------------------- 1 | class AddRankOnList < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :lists do |t| 4 | t.integer :requester_rank 5 | t.integer :responder_rank 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

6 | -------------------------------------------------------------------------------- /db/migrate/20190916060852_create_file_version_tasks.rb: -------------------------------------------------------------------------------- 1 | class CreateFileVersionTasks < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :file_version_tasks do |t| 4 | t.bigint :file_version_id 5 | t.bigint :task_id 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20190717200222_create_teams.rb: -------------------------------------------------------------------------------- 1 | class CreateTeams < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :teams do |t| 4 | t.string :name 5 | t.boolean :is_active, default: true 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190708203644_create_roles_users.rb: -------------------------------------------------------------------------------- 1 | class CreateRolesUsers < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :roles_users do |t| 4 | t.belongs_to :user 5 | t.belongs_to :role 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190717200930_create_teams_users.rb: -------------------------------------------------------------------------------- 1 | class CreateTeamsUsers < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :teams_users do |t| 4 | t.belongs_to :user 5 | t.belongs_to :team 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190904104719_create_lists_users.rb: -------------------------------------------------------------------------------- 1 | class CreateListsUsers < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :lists_users do |t| 4 | t.belongs_to :user 5 | t.belongs_to :list 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190911145904_create_user_notification_scopes.rb: -------------------------------------------------------------------------------- 1 | class CreateUserNotificationScopes < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :user_notification_scopes do |t| 4 | t.string :description 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20190911051521_create_file_bases.rb: -------------------------------------------------------------------------------- 1 | class CreateFileBases < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :file_bases do |t| 4 | t.boolean :is_active 5 | t.boolean :is_template 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/graphql/types/task_section_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class TaskSectionType < GraphQL::Schema::Object 3 | description 'TaskSection' 4 | 5 | field :id, ID, null: false 6 | field :name, String, null: true 7 | field :tasks, [TaskType], null: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/factories/file_versions.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :file_version do 3 | file_id { 1 } 4 | file_owner_id { 1 } 5 | version { 1 } 6 | file_name { 'MyString' } 7 | file_extension { 'MyString' } 8 | file_location { 'MyString' } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/graphql/types/user_details_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class UserDetailsType < GraphQL::Schema::Object 3 | description 'UserDetails' 4 | 5 | field :id, String, null: false 6 | field :user, UserType, null: true 7 | field :role, RoleType, null: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20190814110824_create_users_companies.rb: -------------------------------------------------------------------------------- 1 | class CreateUsersCompanies < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :users_companies do |t| 4 | t.belongs_to :company 5 | t.belongs_to :user 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190911052855_create_file_tasks.rb: -------------------------------------------------------------------------------- 1 | class CreateFileTasks < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :file_tasks do |t| 4 | 5 | t.bigint :file_version_id 6 | t.bigint :task_id 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20190911153424_create_user_notification_frequencies.rb: -------------------------------------------------------------------------------- 1 | class CreateUserNotificationFrequencies < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :user_notification_frequencies do |t| 4 | t.string :description 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('postcss-flexbugs-fixes'), 5 | require('postcss-preset-env')({ 6 | autoprefixer: { 7 | flexbox: 'no-2009' 8 | }, 9 | stage: 3 10 | }) 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /spec/controllers/welcome_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe WelcomeController do 4 | describe 'Load React App' do 5 | it 'should load React app successfully' do 6 | get :index 7 | expect(response.status).to eq(200) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /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/webpack/loaders/typescript.js: -------------------------------------------------------------------------------- 1 | const PnpWebpackPlugin = require('pnp-webpack-plugin'); 2 | 3 | module.exports = { 4 | test: /\.(ts|tsx)?(\.erb)?$/, 5 | use: [ 6 | { 7 | loader: 'ts-loader', 8 | options: PnpWebpackPlugin.tsLoaderOptions(), 9 | }, 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /db/migrate/20190817111651_add_company_settings.rb: -------------------------------------------------------------------------------- 1 | class AddCompanySettings < ActiveRecord::Migration[5.2] 2 | change_table :companies do |t| 3 | t.boolean :auto_pdf, default: false 4 | t.boolean :auto_watermark, default: false 5 | t.boolean :preview_only, default: false 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/graphql/types/company_users_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class CompanyUsersType < GraphQL::Schema::Object 3 | description 'CompanyUsers' 4 | 5 | field :id, ID, null: false 6 | 7 | field :company, CompanyType, null: false 8 | field :users, [UserType], null: true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/roles_user.rb: -------------------------------------------------------------------------------- 1 | class RolesUser < ApplicationRecord 2 | belongs_to :role 3 | belongs_to :user 4 | belongs_to :company 5 | 6 | validates :user_id, 7 | uniqueness: { 8 | scope: :company_id, message: 'should have one role per company' 9 | } 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20190712213325_add_uuid_to_users_for_omani_auth.rb: -------------------------------------------------------------------------------- 1 | class AddUuidToUsersForOmaniAuth < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :users do |t| 4 | t.string :uuid 5 | t.string :token_id 6 | t.string :social_login_provider 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190824162739_create_parent_companies.rb: -------------------------------------------------------------------------------- 1 | class CreateParentCompanies < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :parent_companies do |t| 4 | t.belongs_to :child_company 5 | t.belongs_to :parent_company 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | local: 2 | service: Disk 3 | root: <%= Rails.root.join("storage") %> 4 | 5 | amazon: 6 | service: "S3" 7 | access_key_id: <%= ENV.fetch('AWS_ACCESS_KEY_ID') %> 8 | secret_access_key: <%= ENV.fetch('AWS_SECRET_ACCESS_KEY') %> 9 | bucket: "main-prepdd" 10 | region: "us-east-1" 11 | -------------------------------------------------------------------------------- /db/migrate/20190730211803_add_s3_bucket_location_to_company.rb: -------------------------------------------------------------------------------- 1 | class AddS3BucketLocationToCompany < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :companies do |t| 4 | t.string :s3_location 5 | t.string :kms_key_id 6 | t.string :kms_key 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190824161311_create_broker_companies.rb: -------------------------------------------------------------------------------- 1 | class CreateBrokerCompanies < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :broker_companies do |t| 4 | t.belongs_to :child_broker 5 | t.belongs_to :parent_broker 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20190909181608_create_task_owners.rb: -------------------------------------------------------------------------------- 1 | class CreateTaskOwners < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :task_owners do |t| 4 | t.belongs_to :task 5 | t.references :task_ownerable, polymorphic: true 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20190911154027_create_task_messages.rb: -------------------------------------------------------------------------------- 1 | class CreateTaskMessages < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :task_messages do |t| 4 | t.belongs_to :user 5 | t.belongs_to :task 6 | t.text :message 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /db/data/20190708204424_add_basic_roles.rb: -------------------------------------------------------------------------------- 1 | class AddBasicRoles < ActiveRecord::Migration[5.2] 2 | def up 3 | Role.add('SuperAdmin') 4 | Role.add('Admin') 5 | Role.add('Owner') 6 | Role.add('Manager') 7 | Role.add('User') 8 | end 9 | 10 | def down 11 | Role.delete_all 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/graphql/types/search_company_users_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class SearchCompanyUsersType < GraphQL::Schema::Object 3 | description 'Search users by company id' 4 | 5 | field :id, String, null: false 6 | field :users, [UserType], null: true 7 | field :teams, [TeamType], null: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/AllRoles.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import {AllRoles} from './__generated__/AllRoles'; 3 | 4 | export const useAllRoles = createQueryHook(gql` 5 | query AllRoles { 6 | roles { 7 | id 8 | name 9 | } 10 | } 11 | `); 12 | -------------------------------------------------------------------------------- /app/graphql/types/file_base_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class FileBaseType < GraphQL::Schema::Object 3 | description 'A logical container for many versions of a file.' 4 | 5 | field :id, String, null: false 6 | field :is_template, Boolean, null: true 7 | field :is_active, Boolean, null: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/javascript/src/modules/file/FilesPage/FilesPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Header from './components/Header'; 4 | import Body from './components/Body'; 5 | 6 | export default function FilesPage() { 7 | return ( 8 |
9 |
10 | 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /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 | require 'graphql/rake_task' 6 | 7 | Rails.application.load_tasks 8 | 9 | GraphQL::RakeTask.new(schema_name: 'PrepddSchema') -------------------------------------------------------------------------------- /db/migrate/20190710202528_add_user_association_with_company.rb: -------------------------------------------------------------------------------- 1 | class AddUserAssociationWithCompany < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :companies do |t| 4 | t.belongs_to :owner 5 | end 6 | 7 | change_table :users do |t| 8 | t.belongs_to :company 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/graphql/types/validation_error.rb: -------------------------------------------------------------------------------- 1 | class Types::UserError < GraphQL::Schema::Object 2 | description 'A user-readable error' 3 | 4 | field :message, String, null: false, description: 'A description of the error' 5 | field :path, 6 | [String], 7 | null: true, description: 'Which input value this error came from' 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20190911052002_create_file_message_alerts.rb: -------------------------------------------------------------------------------- 1 | class CreateFileMessageAlerts < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :file_message_alerts do |t| 4 | t.integer :file_message_id 5 | t.integer :user_id 6 | t.boolean :is_read 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/javascript/src/modules/route/team.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Switch, Route} from 'react-router-dom'; 3 | import ManagementPage from '../team'; 4 | 5 | export default function Router() { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /app/javascript/src/modules/route/user.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Switch, Route} from 'react-router-dom'; 3 | import ProfilePage from '../user'; 4 | 5 | export default function UserRoute() { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/CreateTaskPage/CreateTaskPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Header from './components/Header'; 4 | import Body from './components/Body'; 5 | 6 | export default function CreateTaskPage() { 7 | return ( 8 |
9 |
10 | 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

8 | -------------------------------------------------------------------------------- /app/graphql/types/form_error_type.rb: -------------------------------------------------------------------------------- 1 | class Types::FormErrorType < GraphQL::Schema::Object 2 | description 'A user-readable error from a form field' 3 | 4 | field :message, String, null: false, description: 'A description of the error' 5 | field :path, 6 | String, 7 | null: true, description: 'Which field this error came from' 8 | end 9 | -------------------------------------------------------------------------------- /app/graphql/types/search_companies_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class SearchCompaniesType < GraphQL::Schema::Object 3 | description 'Search companies by company name or user name & email' 4 | 5 | field :id, String, null: false 6 | field :users, [UserType], null: true 7 | field :companies, [CompanyType], null: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/javascript/src/modules/route/list.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Switch, Route} from 'react-router-dom'; 3 | import ListPage from '../list/ListPage'; 4 | 5 | export default function ListRoute() { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /app/javascript/src/modules/route/task.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Switch, Route} from 'react-router-dom'; 3 | import TaskPage from '../task/TaskPage'; 4 | 5 | export default function TaskRoute() { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /db/migrate/20190911051917_create_file_labels.rb: -------------------------------------------------------------------------------- 1 | class CreateFileLabels < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :file_labels do |t| 4 | t.text :description 5 | t.string :file_label_color 6 | t.boolean :is_active 7 | t.boolean :is_public 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20190911051944_create_file_messages.rb: -------------------------------------------------------------------------------- 1 | class CreateFileMessages < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :file_messages do |t| 4 | t.integer :user_id 5 | t.integer :file_version_id 6 | t.text :message 7 | t.boolean :is_public 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | begin 5 | exec "yarnpkg", *ARGV 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 | -------------------------------------------------------------------------------- /db/migrate/20190911155050_create_task_message_alerts.rb: -------------------------------------------------------------------------------- 1 | class CreateTaskMessageAlerts < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :task_message_alerts do |t| 4 | t.belongs_to :task_message 5 | t.belongs_to :user 6 | t.boolean :is_read, default: false 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/graphql/types/task_message_alert_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class TaskMessageAlertType < GraphQL::Schema::Object 3 | description 'TaskMessageAlert' 4 | 5 | field :id, ID, null: false 6 | field :user, UserType, null: false 7 | field :taskMessage_, TaskMessage, null: false 8 | field :isRead, Boolean, null: false 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/graphql/types/team_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class TeamType < GraphQL::Schema::Object 3 | description 'Team' 4 | 5 | field :id, ID, null: false 6 | field :companyId, ID, null: false 7 | field :name, String, null: false 8 | field :users, [UserType], null: false 9 | field :company, CompanyType, null: false 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20190706032837_add_full_name_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddFullNameToUser < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :users do |t| 4 | t.string :full_name, null: false, default: '' 5 | t.string :display_name, null: false, default: '' 6 | t.remove :first_name 7 | t.remove :last_name 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/views/devise/mailer/email_changed.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @email %>!

2 | 3 | <% if @resource.try(:unconfirmed_email?) %> 4 |

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

5 | <% else %> 6 |

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/ListPage/components/styled/index.tsx: -------------------------------------------------------------------------------- 1 | import StyledButton from './StyledButton'; 2 | import StyledTab from './StyledTab'; 3 | import StyledTableCell from './StyledTableCell'; 4 | import StyledTableRow from './StyledTableRow'; 5 | import StyledTabs from './StyledTabs'; 6 | 7 | export {StyledButton, StyledTab, StyledTableCell, StyledTableRow, StyledTabs}; 8 | -------------------------------------------------------------------------------- /db/migrate/20190731170005_add_sessions_table.rb: -------------------------------------------------------------------------------- 1 | class AddSessionsTable < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :sessions do |t| 4 | t.string :session_id, :null => false 5 | t.text :data 6 | t.timestamps 7 | end 8 | 9 | add_index :sessions, :session_id, :unique => true 10 | add_index :sessions, :updated_at 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/api_controller.rb: -------------------------------------------------------------------------------- 1 | class ApiController < ActionController::Base 2 | before_action :authenticate 3 | 4 | def authenticate 5 | api_key = request.headers['X-Api-Key'] 6 | if api_key.present? && api_key == 'jKXFpXpMXYeeI0aCPfh14w' 7 | true 8 | else 9 | render json: { error: 'Invalid Api-Key' }, status: :unauthorized 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20190708151108_create_companies.rb: -------------------------------------------------------------------------------- 1 | class CreateCompanies < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :companies do |t| 4 | t.string :name 5 | t.bigint :parent_id 6 | t.bigint :broker_co_id 7 | t.bigint :subscription_id 8 | t.boolean :is_active 9 | t.string :encryption_key 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/SignOutUser.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import {SignOutUser} from './__generated__/SignOutUser'; 3 | 4 | export const useSignOutUser = createMutationHook(gql` 5 | mutation SignOutUser { 6 | signOutUser { 7 | errors { 8 | path 9 | message 10 | } 11 | success 12 | } 13 | } 14 | `); 15 | -------------------------------------------------------------------------------- /app/javascript/src/helpers/queries.ts: -------------------------------------------------------------------------------- 1 | import {gql} from 'apollo-boost'; 2 | 3 | export const SEARCH_COMPANY_USERS = gql` 4 | query SearchCompanyUsers($text: String!, $companyId: ID!) { 5 | searchCompanyUsers(text: $text, companyId: $companyId) { 6 | users { 7 | id 8 | email 9 | fullName 10 | profileUrl 11 | } 12 | teams { 13 | id 14 | name 15 | } 16 | } 17 | } 18 | `; -------------------------------------------------------------------------------- /app/javascript/src/modules/route/company.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Switch, Route} from 'react-router-dom'; 3 | import SubscriptionPage from '../company/CompanySubscription'; 4 | import SettingsPage from '../company'; 5 | 6 | export default function CompanyRoute() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /db/migrate/20190911052305_create_file_versions.rb: -------------------------------------------------------------------------------- 1 | class CreateFileVersions < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :file_versions do |t| 4 | t.integer :file_id 5 | t.integer :file_owner_id 6 | t.integer :version 7 | t.string :file_name 8 | t.string :file_extension 9 | t.string :file_location 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/support/graphql_spec_helper.rb: -------------------------------------------------------------------------------- 1 | module GraphqlSpecHelper 2 | def graphql! 3 | PrepddSchema.execute(@query, context: @context, variables: @variables) 4 | end 5 | 6 | def prepare_query_variables(variables) 7 | @variables = variables 8 | end 9 | 10 | def prepare_context(context) 11 | @context = context 12 | end 13 | 14 | def prepare_query(query) 15 | @query = query 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Prepdd 5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | <%= javascript_pack_tag 'application_pack' %> 9 | 10 | 11 | 12 | <%= yield %> 13 | 14 | 15 | -------------------------------------------------------------------------------- /db/migrate/20190830185711_create_tasks.rb: -------------------------------------------------------------------------------- 1 | class CreateTasks < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :tasks do |t| 4 | t.string :name 5 | t.string :description 6 | t.integer :priority, default: 0 7 | t.integer :status, default: 0 8 | t.datetime :due_date 9 | t.boolean :is_active 10 | t.belongs_to :list 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/javascript/src/constants/theme.ts: -------------------------------------------------------------------------------- 1 | export const COLORS = { 2 | background: '#FFFFFF', 3 | primary: '#3A84FF', 4 | primaryHover: '#EBF2FF', 5 | }; 6 | 7 | export const FONT = { 8 | color: '#2C2C2C', 9 | family: 'Montserrat', 10 | weight: { 11 | bold: 800, 12 | regular: 600, 13 | }, 14 | size: { 15 | xs: '12px', 16 | sm: '15px', 17 | md: '18px', 18 | lg: '24px', 19 | xl: '30px', 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /app/graphql/mutations/delete_task_message.rb: -------------------------------------------------------------------------------- 1 | class Mutations::DeleteTaskMessage < GraphQL::Schema::Mutation 2 | argument :id, ID, required: true 3 | 4 | field :errors, [Types::FormErrorType], null: false 5 | field :success, Boolean, null: false 6 | 7 | def resolve(id: nil, message: nil) 8 | response = { errors: [] } 9 | 10 | TaskMessage.find(id).destroy! 11 | 12 | response[:success] = true 13 | response 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20190830182008_create_lists.rb: -------------------------------------------------------------------------------- 1 | class CreateLists < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :lists do |t| 4 | t.string :name 5 | t.string :description 6 | t.boolean :is_active, default: true 7 | t.boolean :is_template 8 | t.boolean :is_public_template 9 | t.belongs_to :requester 10 | t.belongs_to :responder 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/graphql/mutations/delete_lists.rb: -------------------------------------------------------------------------------- 1 | class Mutations::DeleteLists < GraphQL::Schema::Mutation 2 | argument :listIds, [ID], required: true 3 | 4 | field :errors, [Types::FormErrorType], null: false 5 | field :success, Boolean, null: false 6 | 7 | def resolve(list_ids: nil) 8 | response = { errors: [] } 9 | 10 | list_ids.each { |id| List.find(id).destroy! } 11 | 12 | response[:success] = true 13 | response 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/javascript/src/modules/company/components/FormPanel/components/StyledTableRow.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import TableRow from '@material-ui/core/TableRow'; 3 | 4 | const StyledTableRow = withStyles({ 5 | root: { 6 | height: '60px', 7 | fontFamily: 'Montserrat', 8 | fontWeight: 600, 9 | fontSize: '15px', 10 | color: '#2C2C2C', 11 | }, 12 | })(TableRow); 13 | 14 | export default StyledTableRow; 15 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe User, type: :model do 4 | describe '#associations' do 5 | it { should have_many :owned_companies } 6 | it { should have_many :roles } 7 | it { should have_many :roles_users } 8 | it { should have_many :teams } 9 | it { should have_many :teams_users } 10 | it { should have_many :users_companies } 11 | it { should have_many :companies } 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/UserLists.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import {UserLists} from './__generated__/UserLists'; 3 | 4 | export const useUserLists = createQueryHook(gql` 5 | query UserLists { 6 | userLists { 7 | id 8 | lists { 9 | id 10 | name 11 | sections { 12 | id 13 | name 14 | } 15 | } 16 | } 17 | } 18 | `); 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/ListPage/components/styled/StyledTabs.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {withStyles} from '@material-ui/core/styles'; 3 | import Tabs from '@material-ui/core/Tabs'; 4 | 5 | const StyledTabs = withStyles({ 6 | root: { 7 | borderBottom: '1px solid #D8D8D8', 8 | }, 9 | indicator: { 10 | backgroundColor: '#3A84FF', 11 | borderRadius: '3px 3px 0 0', 12 | }, 13 | })(Tabs); 14 | 15 | export default StyledTabs; 16 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/DeleteTasks.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import {deleteTasks, deleteTasksVariables} from './__generated__/deleteTasks'; 3 | 4 | const deleteTasks = createMutationHook(gql` 5 | mutation deleteTasks($taskIds: [ID!]!) { 6 | deleteTasks(taskIds: $taskIds) { 7 | success 8 | taskIds 9 | } 10 | } 11 | `); 12 | 13 | export default deleteTasks; 14 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/ListPage/components/styled/StyledTableCell.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import TableCell from '@material-ui/core/TableCell'; 3 | 4 | const StyledTableCell = withStyles({ 5 | root: { 6 | padding: '0px 24px 0px 24px', 7 | fontFamily: 'Montserrat', 8 | fontSize: '12px', 9 | fontWeight: 600, 10 | color: '#606060', 11 | }, 12 | })(TableCell); 13 | 14 | export default StyledTableCell; 15 | -------------------------------------------------------------------------------- /db/migrate/20190726063616_create_subscriptions.rb: -------------------------------------------------------------------------------- 1 | class CreateSubscriptions < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :subscriptions do |t| 4 | t.string :description 5 | t.integer :max_users 6 | t.integer :max_storage 7 | t.integer :additional_storage 8 | t.boolean :auto_pdf 9 | t.boolean :auto_watermark 10 | t.boolean :modify_subscription 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/models/team.rb: -------------------------------------------------------------------------------- 1 | class Team < ApplicationRecord 2 | has_many :teams_users 3 | has_many :users, through: :teams_users 4 | belongs_to :company 5 | 6 | has_many :lists_users 7 | has_many :lists, through: :lists_users 8 | 9 | has_many :task_owners, as: :task_ownerable 10 | has_many :owned_team_tasks, 11 | class_name: 'Task', 12 | through: :task_owners, 13 | source: :task_ownerable, 14 | source_type: 'Team' 15 | end 16 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | cIz/VrI4GAZa6tmUsqo/SWTpwexvyHuH1A/nSV1O3Afy/Ge47BQ2PA0hDlfYnm0bH5JlKvcWFeqyuQbbxLAI9j5zVvD6IvwFXLYIhESq2sfXbCbqdhSoHvviMGk2M/hH+qY2kWUNJotnk8bhaUXpGgHYV38G0m75MDDS0zNZDQlwNI07RwS2jTNxREgKuii2GvJQpTJpFh1w2bt93BfZC8adMrNFtfJKjRORBveZMpebtnzOdknCVpAFSau7fMFfaUv+daeFaEo+Al9c1B/vl0mJROZGzxfXac/lU04ZXcCYDrZCpmRcKR4oYIiCY8Woyemt89qVWnt45ygKteLcGOmFEiZPA7F8hkvms8S/cY4G0reOMlnOlwgGJA3v2VA6YNIONYax/zSHJFp8hOl4QQiBKya8h1P9NP8U--9vHccA8EdZrW9dvX--etz2jif3ZsuCWusIL5EbZg== -------------------------------------------------------------------------------- /app/graphql/types/subscription_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class SubscriptionType < GraphQL::Schema::Object 3 | field :id, ID, null: false 4 | field :name, String, null: false 5 | field :description, String, null: true 6 | field :max_users, String, null: true 7 | field :max_storage, String, null: true 8 | field :additional_storage, String, null: true 9 | field :auto_pdf, Boolean, null: true 10 | field :auto_watermark, Boolean, null: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/javascript/src/modules/company/components/FormPanel/components/StyledTableCell.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import TableCell from '@material-ui/core/TableCell'; 3 | 4 | const StyledTableCell = withStyles({ 5 | root: { 6 | height: '60px', 7 | padding: '0px', 8 | fontFamily: 'Montserrat', 9 | fontSize: '15px', 10 | fontWeight: 600, 11 | color: '#2C2C2C', 12 | }, 13 | })(TableCell); 14 | 15 | export default StyledTableCell; 16 | -------------------------------------------------------------------------------- /app/graphql/types/user_for_password_reset_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class UserForPasswordResetType < GraphQL::Schema::Object 3 | description 'User with limited fields for the password reset screen' 4 | 5 | field :email, String, null: false 6 | field :reset_password_period_valid, 7 | Boolean, 8 | null: true, method: :reset_password_period_valid? 9 | 10 | def email 11 | object.email if object.reset_password_period_valid? 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/javascript/src/helpers/roleHelpers.ts: -------------------------------------------------------------------------------- 1 | export const isSuperAdmin = (role: string) => role === 'SuperAdmin'; 2 | 3 | export const isAdmin = (role: string) => role === 'Admin'; 4 | 5 | export const isOwner = (role: string) => role === 'Owner'; 6 | 7 | export const isManager = (role: string) => role === 'Manager'; 8 | 9 | export const isUser = (role: string) => role === 'User'; 10 | 11 | export const canBeAdmin = (role: string) => 12 | isSuperAdmin(role) || isAdmin(role) || isOwner(role); 13 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password. You can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', URI::HTTP.build(host: URI.parse(root_url).host, path: "/reset_password/#{@token}").to_s %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /app/graphql/types/task_attributes.rb: -------------------------------------------------------------------------------- 1 | class Types::TaskAttributes < Types::BaseInputObject 2 | description 'Attributes for creating or updating a task' 3 | 4 | argument :name, String, required: true 5 | argument :description, String, required: true 6 | argument :priority, String, required: true 7 | argument :status, String, required: true 8 | argument :due_date, String, required: true 9 | argument :section, String, required: true 10 | argument :isActive, Boolean, required: true 11 | end 12 | -------------------------------------------------------------------------------- /app/models/role.rb: -------------------------------------------------------------------------------- 1 | class Role < ApplicationRecord 2 | has_many :roles_users 3 | has_many :users, through: :roles_users 4 | 5 | before_validation :customize_title 6 | validates :name, uniqueness: true 7 | 8 | def customize_title(role_name = name) 9 | self.name = role_name.camelize.titlecase.gsub(/\s+/, '') 10 | end 11 | 12 | def self.add(name) 13 | where(name: name).first_or_create 14 | rescue StandardError 15 | raise ArgumentError, 'Argument title not valid.' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20190715093652_add_bio_and_notification_fields_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddBioAndNotificationFieldsToUser < ActiveRecord::Migration[5.2] 2 | def change 3 | change_table :users do |t| 4 | t.belongs_to :notification 5 | t.integer :notification_scope 6 | t.integer :notification_frequency 7 | t.datetime :notification_time 8 | t.datetime :notification_day 9 | t.integer :active_state_id 10 | t.string :user_token 11 | t.string :bio 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/api/tasks_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::TasksController < ApplicationController 2 | skip_before_action :verify_authenticity_token 3 | 4 | def import_task 5 | status = true 6 | begin 7 | tasks = Task.import(task_params[:files]) 8 | rescue => error 9 | Rails.logger.info error 10 | status = false 11 | end 12 | render json: { status: status, tasks: tasks } 13 | end 14 | 15 | private 16 | 17 | def task_params 18 | params.permit(files: []) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /bin/webpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "rubygems" 11 | require "bundler/setup" 12 | 13 | require "webpacker" 14 | require "webpacker/webpack_runner" 15 | 16 | APP_ROOT = File.expand_path("..", __dir__) 17 | Dir.chdir(APP_ROOT) do 18 | Webpacker::WebpackRunner.run(ARGV) 19 | end 20 | -------------------------------------------------------------------------------- /app/javascript/src/modules/team/components/styled/StyledTableCell.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import TableCell from '@material-ui/core/TableCell'; 3 | 4 | const StyledTableCell = withStyles({ 5 | root: { 6 | padding: '6px 0px 6px 0px', 7 | fontFamily: 'Montserrat', 8 | fontSize: '12px', 9 | fontWeight: 600, 10 | color: '#606060', 11 | '&:last-child': { 12 | paddingRight: '0px', 13 | }, 14 | }, 15 | })(TableCell); 16 | 17 | export default StyledTableCell; 18 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | pool: 5 5 | 6 | development: 7 | <<: *default 8 | database: prepdd_development 9 | 10 | production: 11 | <<: *default 12 | url: <%= ENV['DATABASE_URL'] %> 13 | 14 | # Warning: The database defined as "test" will be erased and 15 | # re-generated from your development database when you run "rake". 16 | # Do not set this db to the same as development or production. 17 | test: 18 | <<: *default 19 | database: prepdd_test 20 | -------------------------------------------------------------------------------- /bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "rubygems" 11 | require "bundler/setup" 12 | 13 | require "webpacker" 14 | require "webpacker/dev_server_runner" 15 | 16 | APP_ROOT = File.expand_path("..", __dir__) 17 | Dir.chdir(APP_ROOT) do 18 | Webpacker::DevServerRunner.run(ARGV) 19 | end 20 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/CreateTask.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import {CreateTask, CreateTaskVariables} from './__generated__/CreateTask'; 3 | 4 | export const useCreateTask = createMutationHook< 5 | CreateTask, 6 | CreateTaskVariables 7 | >(gql` 8 | mutation CreateTask($listId: ID!, $tasks: [TaskAttributes!]!) { 9 | createTask(listId: $listId, tasks: $tasks) { 10 | errors { 11 | path 12 | message 13 | } 14 | success 15 | } 16 | } 17 | `); 18 | -------------------------------------------------------------------------------- /app/graphql/types/roles_user_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class RolesUserType < GraphQL::Schema::Object 3 | description 'All Available Roles of User' 4 | 5 | field :id, ID, null: false 6 | field :name, String, null: false 7 | field :companyId, ID, null: false 8 | 9 | def id 10 | object.role.id 11 | end 12 | 13 | def name 14 | object.role.name 15 | end 16 | 17 | def name 18 | object.role.name 19 | end 20 | 21 | def company_id 22 | object.company_id 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/AllTemplates.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import {AllTemplates} from './__generated__/AllTemplates'; 3 | 4 | export const useAllTemplates = createQueryHook(gql` 5 | query AllTemplates { 6 | templateLists { 7 | id 8 | name 9 | tasks { 10 | id 11 | name 12 | section { 13 | id 14 | name 15 | } 16 | description 17 | priority 18 | status 19 | } 20 | } 21 | } 22 | `); 23 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/UserForPasswordReset.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import { 3 | UserForPasswordReset, 4 | UserForPasswordResetVariables, 5 | } from './__generated__/UserForPasswordReset'; 6 | 7 | export const useUserForPasswordReset = createQueryHook< 8 | UserForPasswordReset, 9 | UserForPasswordResetVariables 10 | >(gql` 11 | query UserForPasswordReset($token: String!) { 12 | userForPasswordReset(token: $token) { 13 | email 14 | resetPasswordPeriodValid 15 | } 16 | } 17 | `); 18 | -------------------------------------------------------------------------------- /app/workers/company/company_s3_bucket_creation_worker.rb: -------------------------------------------------------------------------------- 1 | class Company::CompanyS3BucketCreationWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(company_id) 5 | require 'aws-sdk-s3' 6 | 7 | company = Company.find(company_id) 8 | if company && !company.s3_location 9 | s3 = Aws::S3::Client.new 10 | bucket = s3.create_bucket(bucket: company.name.downcase + "-prepdd-" + Rails.env) 11 | 12 | if bucket 13 | company.s3_location = bucket.location 14 | company.save! 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /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) { wrap_parameters format: %i[json] } 8 | 9 | # To enable root element in JSON for ActiveRecord objects. 10 | # ActiveSupport.on_load(:active_record) do 11 | # self.include_root_in_json = true 12 | # end 13 | -------------------------------------------------------------------------------- /app/graphql/mutations/delete_tasks.rb: -------------------------------------------------------------------------------- 1 | class Mutations::DeleteTasks < GraphQL::Schema::Mutation 2 | argument :taskIds, [ID], required: true 3 | 4 | field :errors, [Types::FormErrorType], null: false 5 | field :success, Boolean, null: false 6 | field :task_ids, [ID], null: false 7 | 8 | def resolve(task_ids: nil) 9 | response = { errors: [] } 10 | 11 | task_ids.each { |id| Task.find(id).destroy! } 12 | 13 | response[:success] = true 14 | # returning task ids to update the DOM 15 | response[:task_ids] = task_ids 16 | response 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/graphql/mutations/sign_out_user.rb: -------------------------------------------------------------------------------- 1 | class Mutations::SignOutUser < GraphQL::Schema::Mutation 2 | field :errors, [Types::FormErrorType], null: false 3 | field :success, Boolean, null: false 4 | 5 | def resolve 6 | response = { errors: [] } 7 | 8 | unless context[:controller].user_signed_in? 9 | response[:errors].push({ path: 'root', message: 'Already signed out.' }) 10 | response[:success] = false 11 | return response 12 | end 13 | 14 | context[:controller].sign_out 15 | response[:success] = true 16 | response 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/models/list.rb: -------------------------------------------------------------------------------- 1 | class List < ApplicationRecord 2 | belongs_to :requester, 3 | class_name: 'Company', foreign_key: 'requester_id', optional: true 4 | belongs_to :responder, 5 | class_name: 'Company', foreign_key: 'responder_id', optional: true 6 | 7 | has_many :tasks 8 | has_many :lists_users 9 | has_many :owners, class_name: 'User', through: :lists_users, source: :user 10 | 11 | after_create :add_rank 12 | 13 | def add_rank 14 | self.responder_rank = self.id 15 | self.requester_rank = self.id 16 | self.save! 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/__generated__/AllRoles.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: AllRoles 7 | // ==================================================== 8 | 9 | export interface AllRoles_roles { 10 | __typename: "Role"; 11 | id: string; 12 | name: string; 13 | } 14 | 15 | export interface AllRoles { 16 | /** 17 | * Return All available roles 18 | */ 19 | roles: AllRoles_roles[]; 20 | } 21 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/CreateCompany.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | CreateCompany, 4 | CreateCompanyVariables, 5 | } from './__generated__/CreateCompany'; 6 | 7 | export const useCompanyCreate = createMutationHook< 8 | CreateCompany, 9 | CreateCompanyVariables 10 | >(gql` 11 | mutation CreateCompany($name: String!) { 12 | createCompany(name: $name) { 13 | company { 14 | name 15 | } 16 | errors { 17 | path 18 | message 19 | } 20 | success 21 | } 22 | } 23 | `); 24 | -------------------------------------------------------------------------------- /app/javascript/src/modules/layout/components/TopBar/components/StyledBadge.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import Badge from '@material-ui/core/Badge'; 3 | 4 | const StyledBadge = withStyles({ 5 | badge: { 6 | top: '15%', 7 | right: '6px', 8 | minWidth: '15px', 9 | height: '15px', 10 | background: '#FF507C', 11 | fontFamily: 'Montserrat', 12 | fontWeight: 'bold', 13 | fontSize: '11px', 14 | color: '#FFFFFF', 15 | letterSpacing: '0', 16 | textAlign: 'center', 17 | }, 18 | })(Badge); 19 | 20 | export default StyledBadge; 21 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/app/javascript/src'], 3 | transform: { 4 | '^.+\\.jsx?$': 'babel-jest', 5 | '^.+\\.tsx?$': 'ts-jest', 6 | }, 7 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', 8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 9 | moduleNameMapper: { 10 | '\\.(svg|png|jpg)$': '/app/javascript/src/__mocks__/emptyAsset.ts', 11 | }, 12 | // Setup Enzyme 13 | snapshotSerializers: ['enzyme-to-json/serializer'], 14 | setupFilesAfterEnv: ['/app/javascript/src/setupEnzyme.ts'], 15 | }; 16 | -------------------------------------------------------------------------------- /app/javascript/src/constants/types.ts: -------------------------------------------------------------------------------- 1 | export interface NotificationType { 2 | variant: 'success' | 'warning' | 'error' | 'info'; 3 | message: string; 4 | } 5 | 6 | export interface ListType { 7 | name: string; 8 | description: string; 9 | requesterId: string; 10 | responderId: string; 11 | } 12 | 13 | export interface TaskType { 14 | name: string; 15 | description: string; 16 | priority: string; 17 | status: string; 18 | due_date: string; 19 | section: string; 20 | isActive: boolean; 21 | } 22 | 23 | export interface OptionType { 24 | value: string; 25 | label: string; 26 | } 27 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/PublicTaskMessages.ts: -------------------------------------------------------------------------------- 1 | /* same note as the others -- keeping public and private methods 2 | * segregated */ 3 | import {createQueryHook, gql} from '../graphqlHelpers'; 4 | import {publicTaskMessages} from './__generated__/publicTaskMessages'; 5 | 6 | const PublicTaskMessages = createQueryHook(gql` 7 | query publicTaskMessages($taskId: ID!) { 8 | publicTaskMessages(taskId: $taskId) { 9 | message 10 | user { 11 | fullName 12 | } 13 | createdAt 14 | } 15 | } 16 | `); 17 | 18 | export default PublicTaskMessages; 19 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/ListPage/components/styled/StyledTableRow.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import TableRow from '@material-ui/core/TableRow'; 3 | 4 | const StyledTableRow = withStyles({ 5 | root: { 6 | padding: '0px 24px 0px 24px', 7 | fontFamily: 'Montserrat', 8 | fontWeight: 600, 9 | fontSize: '12px', 10 | color: '#606060', 11 | '&$selected, &$hover:hover': { 12 | color: '#FFFFFF', 13 | background: '#EBF2FF', 14 | }, 15 | }, 16 | selected: {}, 17 | hover: {}, 18 | })(TableRow); 19 | 20 | export default StyledTableRow; 21 | -------------------------------------------------------------------------------- /app/javascript/src/modules/task/TaskPage/components/TaskTable/components/StyledBadge.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import Badge from '@material-ui/core/Badge'; 3 | 4 | const StyledBadge = withStyles({ 5 | badge: { 6 | top: '12%', 7 | right: '3px', 8 | minWidth: '10px', 9 | height: '10px', 10 | backgroundColor: '#6EB81D', 11 | fontFamily: 'Montserrat', 12 | fontWeight: 'bold', 13 | fontSize: '11px', 14 | color: '#FFFFFF', 15 | letterSpacing: '0', 16 | textAlign: 'center', 17 | }, 18 | })(Badge); 19 | 20 | export default StyledBadge; 21 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | 4 | def last_updated_at 5 | diff = Time.now - self.updated_at 6 | case diff 7 | when 0..60 8 | return 'just now' 9 | when 61..3_600 10 | diff = (diff / 60).to_i 11 | unit = 'minute'.pluralize diff 12 | when 3_601..(3_600 * 24) 13 | diff = (diff / 3_600).to_i 14 | unit = 'hour'.pluralize diff 15 | else 16 | diff = (diff / (3_600 * 24)).to_i 17 | unit = 'day'.pluralize diff 18 | end 19 | "#{diff} #{unit} ago" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/PrivateTaskMessages.ts: -------------------------------------------------------------------------------- 1 | /* Keeping Private and Public Message queries separate to 2 | * stave off mishaps */ 3 | import {createQueryHook, gql} from '../graphqlHelpers'; 4 | import {privateTaskMessages} from './__generated__/privateTaskMessages'; 5 | 6 | const PrivateTaskMessages = createQueryHook(gql` 7 | query privateTaskMessages($taskId: ID!) { 8 | privateTaskMessages(taskId: $taskId) { 9 | message 10 | user { 11 | fullName 12 | } 13 | createdAt 14 | } 15 | } 16 | `); 17 | 18 | export default PrivateTaskMessages; 19 | -------------------------------------------------------------------------------- /app/javascript/src/modules/team/components/styled/StyledTableRow.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import TableRow from '@material-ui/core/TableRow'; 3 | 4 | const StyledTableRow = withStyles({ 5 | root: { 6 | height: '48px', 7 | padding: '0px 31px 0px 31px', 8 | fontFamily: 'Montserrat', 9 | fontWeight: 600, 10 | fontSize: '12px', 11 | color: '#606060', 12 | '&$selected, &$hover:hover': { 13 | color: '#FFFFFF', 14 | background: '#EBF2FF', 15 | }, 16 | }, 17 | selected: {}, 18 | hover: {}, 19 | })(TableRow); 20 | 21 | export default StyledTableRow; 22 | -------------------------------------------------------------------------------- /spec/graphql/types/current_user_type_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | module Types 3 | RSpec.describe CurrentUserType do 4 | set_graphql_type 5 | # avail type definer in our tests 6 | types = GraphQL::Define::TypeDefiner.instance 7 | 8 | it 'has an :id field of ID type' do 9 | # Ensure that the field id is of type String 10 | expect(subject.fields['id'].type.to_type_signature).to eq('String!') 11 | end 12 | 13 | it 'has a :user field of User type' do 14 | # Ensure the field is of User type 15 | expect(subject.fields['user'].type.to_type_signature).to eq('User') 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/UpdateUserPassword.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | UpdateUserPassword, 4 | UpdateUserPasswordVariables, 5 | } from './__generated__/UpdateUserPassword'; 6 | 7 | export const useUpdateUserPassword = createMutationHook< 8 | UpdateUserPassword, 9 | UpdateUserPasswordVariables 10 | >(gql` 11 | mutation UpdateUserPassword($password: String!, $oldPassword: String!) { 12 | updateUserPassword(password: $password, oldPassword: $oldPassword) { 13 | errors { 14 | path 15 | message 16 | } 17 | success 18 | } 19 | } 20 | `); 21 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/UserDetails.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import {UserDetails} from './__generated__/UserDetails'; 3 | 4 | export const useUserDetails = createQueryHook(gql` 5 | query UserDetails($id: ID!) { 6 | user(id: $id) { 7 | id 8 | email 9 | fullName 10 | profileUrl 11 | roles { 12 | id 13 | name 14 | companyId 15 | } 16 | teams { 17 | id 18 | name 19 | companyId 20 | } 21 | companies { 22 | id 23 | name 24 | logoUrl 25 | } 26 | } 27 | } 28 | `); 29 | -------------------------------------------------------------------------------- /spec/graphql/types/form_error_type_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | module Types 3 | RSpec.describe FormErrorType do 4 | set_graphql_type 5 | # avail type definer in our tests 6 | types = GraphQL::Define::TypeDefiner.instance 7 | 8 | it 'has an :message field of String type' do 9 | # Ensure that the field id is of type String 10 | expect(subject.fields['message'].type.to_type_signature).to eq('String!') 11 | end 12 | 13 | it 'has a :path field of String type' do 14 | # Ensure the field is of String type 15 | expect(subject.fields['path'].type.to_type_signature).to eq('String') 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/api/file_versions_controller.rb: -------------------------------------------------------------------------------- 1 | ### WIP ##### 2 | module Api 3 | class FileVersionsController < ApiController 4 | skip_before_action :verify_authenticity_token 5 | 6 | def create 7 | ok = [] 8 | @task = Task.find(file_params[:task_id]) 9 | file_params[:files].each do |f| 10 | @file = FileVersion.create 11 | @file.file.attach(f) 12 | @task.file_versions << @file 13 | ok << url_for(@file.file) 14 | end 15 | 16 | render json: { success: ok } 17 | end 18 | 19 | private 20 | 21 | def file_params 22 | params.permit(:task_id, files: []) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/graphql/types/task_message_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class TaskMessageType < GraphQL::Schema::Object 3 | description 'TaskMessage' 4 | 5 | field :id, ID, null: false 6 | field :user, UserType, null: false 7 | field :task, TaskType, null: false 8 | field :message, String, null: false 9 | field :isPublic, Boolean, null: false 10 | # Right now, we're not implementing editing on messages -- 11 | # but we will, so this field name will change 12 | field :createdAt, String, null: false, method: :last_updated_at 13 | # For private messages, the message belongs to a company 14 | field :company, CompanyType, null: true 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/deleteTasks.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: deleteTasks 7 | // ==================================================== 8 | 9 | export interface deleteTasks_deleteTasks { 10 | __typename: "DeleteTasksPayload"; 11 | success: boolean; 12 | taskIds: string[]; 13 | } 14 | 15 | export interface deleteTasks { 16 | deleteTasks: deleteTasks_deleteTasks | null; 17 | } 18 | 19 | export interface deleteTasksVariables { 20 | taskIds: string[]; 21 | } 22 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/SendPasswordResetInstructions.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | SendPasswordResetInstructions, 4 | SendPasswordResetInstructionsVariables, 5 | } from './__generated__/SendPasswordResetInstructions'; 6 | 7 | export const useSendPasswordResetInstructions = createMutationHook< 8 | SendPasswordResetInstructions, 9 | SendPasswordResetInstructionsVariables 10 | >(gql` 11 | mutation SendPasswordResetInstructions($email: String!) { 12 | sendResetPasswordInstructions(email: $email) { 13 | errors { 14 | path 15 | message 16 | } 17 | success 18 | } 19 | } 20 | `); 21 | -------------------------------------------------------------------------------- /app/javascript/src/modules/user/__tests__/UserProfile.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {shallow} from 'enzyme'; 3 | import UserProfilePage from '../UserProfile'; 4 | import ProfilePane from '../components/ProfilePane'; 5 | import NotificationPane from '../components/NotificationPane'; 6 | 7 | describe('User Profile Page', () => { 8 | it('renders profile pane', () => { 9 | const wrapper = shallow(); 10 | expect(wrapper.find(ProfilePane).length).toBe(1); 11 | }); 12 | 13 | it('renders notification pane', () => { 14 | const wrapper = shallow(); 15 | expect(wrapper.find(NotificationPane).length).toBe(1); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/workers/company/company_kms_creation_worker.rb: -------------------------------------------------------------------------------- 1 | class Company::CompanyKmsCreationWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(company_id) 5 | require 'aws-sdk-kms' 6 | company = Company.find(company_id) 7 | 8 | if company && !company.kms_key_id 9 | client = Aws::KMS::Client.new 10 | kms = 11 | client.create_key( 12 | { 13 | tags: [{ tag_key: 'CompanyName', tag_value: company.name.downcase + "-prepdd-" + Rails.env }] 14 | } 15 | ) 16 | end 17 | 18 | if kms 19 | company.kms_key_id = kms.key_metadata.key_id 20 | company.kms_key = kms.key_metadata.arn 21 | company.save! 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/graphql/mutations/update_task_message.rb: -------------------------------------------------------------------------------- 1 | class Mutations::UpdateTaskMessage < GraphQL::Schema::Mutation 2 | argument :id, ID, required: true 3 | argument :message, String, required: true 4 | 5 | field :taskMessage, Types::TaskMessageType, null: false 6 | field :errors, [Types::FormErrorType], null: false 7 | field :success, Boolean, null: false 8 | 9 | def resolve(id: nil, message: nil) 10 | response = { errors: [] } 11 | 12 | task_message = TaskMessage.find(id) 13 | 14 | task_message.update(message: message) 15 | 16 | if task_message&.persisted? 17 | response[:success] = true 18 | response[:task_message] = task_message 19 | end 20 | response 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/javascript/src/modules/common/LoadingFallback.tsx: -------------------------------------------------------------------------------- 1 | import CircularProgress from '@material-ui/core/CircularProgress'; 2 | import Container from '@material-ui/core/Container'; 3 | import React from 'react'; 4 | import {makeStyles} from '@material-ui/core/styles'; 5 | 6 | const useStyles = makeStyles({ 7 | box: { 8 | display: 'flex', 9 | justifyContent: 'center', 10 | alignItems: 'center', 11 | height: '80vh', 12 | }, 13 | }); 14 | 15 | export default function LoadingFallback() { 16 | const classes = useStyles({}); 17 | return ( 18 | 19 |
20 | 21 |
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/models/company_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Company, type: :model do 4 | let!(:company) { create(:company) } 5 | 6 | describe '#validations' do 7 | it { should validate_uniqueness_of(:name) } 8 | it { should validate_presence_of(:name) } 9 | end 10 | 11 | describe '#associations' do 12 | it { should have_many :users } 13 | it { should belong_to :owner } 14 | it { should belong_to :subscription } 15 | end 16 | 17 | describe '#callback' do 18 | # Company callback 19 | describe '#method' do 20 | it { is_expected.to callback(:create_s3_kms).after(:create) } 21 | it { is_expected.to callback(:generate_encryption_key).before(:create) } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "declaration": false, 6 | "downlevelIteration": true, 7 | "esModuleInterop": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "importHelpers": true, 11 | "jsx": "react", 12 | "lib": ["es6", "dom"], 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "sourceMap": true, 16 | "strict": true, 17 | "target": "es5", 18 | "noImplicitAny": true, 19 | "typeRoots": ["node_modules/@types", "app/javascript/third_party_types"] 20 | }, 21 | "exclude": ["**/*.spec.ts", "node_modules", "vendor", "public"], 22 | "compileOnSave": false 23 | } 24 | -------------------------------------------------------------------------------- /app/controllers/api/companies_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::CompaniesController < ApiController 2 | skip_before_action :verify_authenticity_token 3 | before_action :set_company, only: %i[update_log] 4 | 5 | def update_log 6 | logo = params[:logo] 7 | if logo.content_type.in?(%['image/jpeg image/jpg image/png']) 8 | @company.logo.attach(params[:logo]) 9 | render json: { status: true, logo_url: url_for(@company.logo) } 10 | else 11 | render json: { status: false, message: 'Not a valid file type' } 12 | end 13 | end 14 | 15 | private 16 | 17 | def set_company 18 | @company = Company.find(company_params[:id]) 19 | end 20 | 21 | def company_params 22 | params.permit(:id, logo: []) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/AddListOwner.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | AddListOwner, 4 | AddListOwnerVariables, 5 | } from './__generated__/AddListOwner'; 6 | 7 | export const useAddListOwner = createMutationHook< 8 | AddListOwner, 9 | AddListOwnerVariables 10 | >(gql` 11 | mutation AddListOwner( 12 | $listId: ID! 13 | $companyId: ID! 14 | $userEmails: [String!] 15 | $teamIds: [ID!] 16 | ) { 17 | addListOwner( 18 | listId: $listId 19 | companyId: $companyId 20 | userEmails: $userEmails 21 | teamIds: $teamIds 22 | ) { 23 | errors { 24 | path 25 | message 26 | } 27 | success 28 | } 29 | } 30 | `); 31 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/ListPage/components/styled/StyledButton.tsx: -------------------------------------------------------------------------------- 1 | import {withStyles} from '@material-ui/core/styles'; 2 | import Button from '@material-ui/core/Button'; 3 | 4 | const StyledButton = withStyles({ 5 | root: { 6 | boxSizing: 'border-box', 7 | height: '36px', 8 | minWidth: '92px', 9 | marginLeft: 20, 10 | border: '2px solid #3A84FF', 11 | borderRadius: '3px', 12 | fontFamily: 'Montserrat', 13 | fontWeight: 'bold', 14 | fontSize: '12px', 15 | color: '#3A84FF', 16 | textAlign: 'center', 17 | '&:hover': { 18 | border: '2px solid #3A84FF', 19 | }, 20 | }, 21 | label: { 22 | textTransform: 'capitalize', 23 | }, 24 | })(Button); 25 | 26 | export default StyledButton; 27 | -------------------------------------------------------------------------------- /app/policies/application_policy.rb: -------------------------------------------------------------------------------- 1 | class ApplicationPolicy 2 | attr_reader :user, :record 3 | 4 | def initialize(user, record) 5 | @user = user 6 | @record = record 7 | end 8 | 9 | def index? 10 | false 11 | end 12 | 13 | def show? 14 | false 15 | end 16 | 17 | def create? 18 | false 19 | end 20 | 21 | def new? 22 | create? 23 | end 24 | 25 | def update? 26 | false 27 | end 28 | 29 | def edit? 30 | update? 31 | end 32 | 33 | def destroy? 34 | false 35 | end 36 | 37 | class Scope 38 | attr_reader :user, :scope 39 | 40 | def initialize(user, scope) 41 | @user = user 42 | @scope = scope 43 | end 44 | 45 | def resolve 46 | scope.all 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "addons": ["heroku-postgresql", "heroku-redis"], 3 | "buildpacks": [ 4 | { 5 | "url": "heroku/ruby" 6 | }, 7 | { 8 | "url": "heroku/nodejs" 9 | } 10 | ], 11 | "env": { 12 | "HEROKU_DEBUG_RAILS_RUNNER": "1", 13 | "AWS_ACCESS_KEY_ID": { 14 | "required": true 15 | }, 16 | "AWS_SECRET_ACCESS_KEY": { 17 | "required": true 18 | } 19 | }, 20 | "formation": { 21 | "web": { 22 | "quantity": 1 23 | } 24 | }, 25 | "name": "prepdd", 26 | "scripts": { 27 | "postdeploy": "bundle exec rake db:migrate data:migrate db:seed" 28 | }, 29 | "stack": "heroku-18", 30 | "environments": { 31 | "test": { 32 | "addons": ["heroku-postgresql:in-dyno"], 33 | "env": {} 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/CompanySettings.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import {CompanySettings} from './__generated__/CompanySettings'; 3 | 4 | export const useCompanySettings = createQueryHook(gql` 5 | query CompanySettings($id: ID!) { 6 | company(id: $id) { 7 | id 8 | name 9 | logoUrl 10 | parents { 11 | id 12 | name 13 | logoUrl 14 | } 15 | brokers { 16 | id 17 | name 18 | logoUrl 19 | } 20 | totalUsers 21 | totalStorage 22 | subscription { 23 | id 24 | maxUsers 25 | maxStorage 26 | } 27 | autoPdf 28 | autoWatermark 29 | previewOnly 30 | } 31 | } 32 | `); 33 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/UpdateUserData.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | UpdateUserData, 4 | UpdateUserDataVariables, 5 | } from './__generated__/UpdateUserData'; 6 | 7 | export const useUpdateUserData = createMutationHook< 8 | UpdateUserData, 9 | UpdateUserDataVariables 10 | >(gql` 11 | mutation UpdateUserData( 12 | $email: String! 13 | $fullName: String! 14 | $displayName: String! 15 | $lastViewedCompanyId: ID 16 | ) { 17 | updateUserData( 18 | email: $email 19 | fullName: $fullName 20 | displayName: $displayName 21 | lastViewedCompanyId: $lastViewedCompanyId 22 | ) { 23 | errors { 24 | path 25 | message 26 | } 27 | success 28 | } 29 | } 30 | `); 31 | -------------------------------------------------------------------------------- /lib/tasks/format.rake: -------------------------------------------------------------------------------- 1 | namespace :format do 2 | task :write do 3 | DIRECTORIES = %w[app bin config db lib spec storage test].join(',') 4 | EXTENSIONS = %w[js rake rb ts tsx].join(',') 5 | 6 | Signal.trap('INT') { exit } 7 | Signal.trap('TERM') { exit } 8 | 9 | files = 10 | Dir.glob("{#{DIRECTORIES}}/**/*.{#{EXTENSIONS}}").filter do |file| 11 | !file.include? '__generated__' 12 | end 13 | 14 | group_size = (files.size.to_f / Etc.nprocessors.to_f).ceil 15 | 16 | files.each_slice(group_size).map do |slice| 17 | spawn('./node_modules/.bin/prettier', '--write', *slice) 18 | end 19 | 20 | if Process.waitall.any? { |_pid, status| status.exitstatus != 0 } 21 | abort('Error: not all files could be formatted') 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/__generated__/globalTypes.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | //============================================================== 6 | // START Enums and Input Objects 7 | //============================================================== 8 | 9 | /** 10 | * Attributes for creating or updating a task 11 | */ 12 | export interface TaskAttributes { 13 | description: string; 14 | dueDate: string; 15 | isActive: boolean; 16 | name: string; 17 | priority: string; 18 | section: string; 19 | status: string; 20 | } 21 | 22 | //============================================================== 23 | // END Enums and Input Objects 24 | //============================================================== 25 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/CreatePrivateTaskMessage.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | CreatePrivateTaskMessage, 4 | CreatePrivateTaskMessageVariables, 5 | } from './__generated__/CreatePrivateTaskMessage'; 6 | 7 | const createPrivateTaskMessage = createMutationHook< 8 | CreatePrivateTaskMessage, 9 | CreatePrivateTaskMessageVariables 10 | >(gql` 11 | mutation CreatePrivateTaskMessage($taskId: ID!, $message: String!) { 12 | createTaskMessages(taskId: $taskId, message: $message, isPublic: false) { 13 | success 14 | message { 15 | id 16 | message 17 | createdAt 18 | user { 19 | fullName 20 | } 21 | } 22 | } 23 | } 24 | `); 25 | 26 | export default createPrivateTaskMessage; 27 | -------------------------------------------------------------------------------- /spec/graphql/mutations/create_company_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | module Mutations 3 | RSpec.describe CreateCompany, type: :request do 4 | describe 'resolve' do 5 | it 'Create new company' do 6 | company = create(:company) 7 | expect do 8 | res = post '/graphql', params: { query: query(name: company.name) } 9 | to change { Company.count }.by(1) 10 | it { expect Company.first.name.eql? company.name } 11 | end 12 | end 13 | end 14 | 15 | def query(name:) 16 | <<~GQL 17 | mutation{ 18 | CreateCompany( 19 | name: #{ 20 | name 21 | }, 22 | ) { 23 | errors { 24 | path 25 | message 26 | } 27 | success 28 | } 29 | } 30 | GQL 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/__generated__/UserForPasswordReset.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: UserForPasswordReset 7 | // ==================================================== 8 | 9 | export interface UserForPasswordReset_userForPasswordReset { 10 | __typename: "UserForPasswordReset"; 11 | email: string; 12 | resetPasswordPeriodValid: boolean | null; 13 | } 14 | 15 | export interface UserForPasswordReset { 16 | /** 17 | * Information for resetting a users password 18 | */ 19 | userForPasswordReset: UserForPasswordReset_userForPasswordReset | null; 20 | } 21 | 22 | export interface UserForPasswordResetVariables { 23 | token: string; 24 | } 25 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/CreateList.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import {CreateList, CreateListVariables} from './__generated__/CreateList'; 3 | 4 | export const useCreateList = createMutationHook< 5 | CreateList, 6 | CreateListVariables 7 | >(gql` 8 | mutation CreateList( 9 | $name: String! 10 | $description: String 11 | $requesterId: ID! 12 | $responderId: ID! 13 | $tasks: [TaskAttributes!] 14 | ) { 15 | createList( 16 | name: $name 17 | description: $description 18 | requesterId: $requesterId 19 | responderId: $responderId 20 | tasks: $tasks 21 | ) { 22 | list { 23 | id 24 | name 25 | } 26 | errors { 27 | path 28 | message 29 | } 30 | success 31 | } 32 | } 33 | `); 34 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/ListPage/components/styled/StyledTab.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {withStyles} from '@material-ui/core/styles'; 3 | import Tab from '@material-ui/core/Tab'; 4 | 5 | const StyledTab = withStyles(theme => ({ 6 | root: { 7 | minWidth: 36, 8 | padding: 0, 9 | fontSize: '12px', 10 | fontWeight: 'bold', 11 | color: '#606060', 12 | fontFamily: 'Montserrat', 13 | lineHeight: '20px', 14 | textTransform: 'capitalize', 15 | marginLeft: '24px', 16 | '&:hover': { 17 | color: '#40a9ff', 18 | opacity: 1, 19 | }, 20 | '&$selected': { 21 | color: '#3A84FF', 22 | }, 23 | '&:focus': { 24 | color: '#40a9ff', 25 | }, 26 | }, 27 | selected: {}, 28 | }))((props: any) => ); 29 | 30 | export default StyledTab; 31 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | devise_for :users, skip: :all 3 | 4 | namespace :api do 5 | post '/update_user_profile', to: 'users#update' 6 | post '/update_company_logo', to: 'companies#update_log' 7 | post '/import_task', to: 'tasks#import_task' 8 | post '/upload', to: 'file_versions#create' 9 | end 10 | 11 | require 'sidekiq/web' 12 | mount Sidekiq::Web => '/sidekiq' 13 | 14 | post '/graphql', to: 'graphql#execute' 15 | if Rails.env.development? 16 | mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql' 17 | end 18 | 19 | root to: 'welcome#index' 20 | 21 | # Redirect all html requests to the root so they can be handled by javascript 22 | get '*path', 23 | to: 'welcome#index', 24 | constraints: ->(request) { !request.xhr? && request.format.html? } 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/api/users_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::UsersController < ApiController 2 | skip_before_action :verify_authenticity_token 3 | before_action :set_user, only: %i[update] 4 | 5 | def update 6 | picture = params[:profile_picture] 7 | if picture.content_type.in?(%['image/jpeg image/jpg image/png']) 8 | @user.profile_picture.attach(params[:profile_picture]) 9 | render json: { 10 | status: true, 11 | user: @user, 12 | profile_url: url_for(@user.profile_picture) 13 | } 14 | else 15 | render json: { status: false, message: 'Not a valid file type' } 16 | end 17 | end 18 | 19 | private 20 | 21 | def set_user 22 | @user = User.find(user_params[:id]) 23 | end 24 | 25 | def user_params 26 | params.permit(:id, profile_picture: []) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/tasks/graphql.rake: -------------------------------------------------------------------------------- 1 | # This file contains our custom graphql tasks. The rest of the graphql tasks 2 | # are provided by the graphql gem 3 | 4 | namespace :graphql do 5 | desc 'Remove generated files' 6 | task :clean do 7 | rm_rf ['./__generated__', *Dir.glob('app/**/__generated__')] 8 | end 9 | 10 | desc 'Generate typescript definitions' 11 | task :types do 12 | exec( 13 | 'yarn', 14 | 'run', 15 | 'apollo', 16 | 'codegen:generate', 17 | '--localSchemaFile=schema.graphql', 18 | '--includes=app/javascript/src/**/*.{ts,tsx}', 19 | '--target=typescript', 20 | '--tagName=gql', 21 | '--globalTypesFile=app/javascript/src/graphql/__generated__/globalTypes.ts' 22 | ) 23 | end 24 | 25 | desc 'Run all steps to generate typescript definitions' 26 | task gen: %w[clean schema:dump types] 27 | end 28 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/SignOutUser.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: SignOutUser 7 | // ==================================================== 8 | 9 | export interface SignOutUser_signOutUser_errors { 10 | __typename: "FormError"; 11 | /** 12 | * Which field this error came from 13 | */ 14 | path: string | null; 15 | /** 16 | * A description of the error 17 | */ 18 | message: string; 19 | } 20 | 21 | export interface SignOutUser_signOutUser { 22 | __typename: "SignOutUserPayload"; 23 | errors: SignOutUser_signOutUser_errors[]; 24 | success: boolean; 25 | } 26 | 27 | export interface SignOutUser { 28 | signOutUser: SignOutUser_signOutUser | null; 29 | } 30 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/CreatePublicTaskMessage.ts: -------------------------------------------------------------------------------- 1 | /* Again, using separate mutations to be safe and make it 2 | * hard to mixup private and public */ 3 | import {createMutationHook, gql} from '../graphqlHelpers'; 4 | import { 5 | CreatePublicTaskMessage, 6 | CreatePublicTaskMessageVariables, 7 | } from './__generated__/CreatePublicTaskMessage'; 8 | 9 | const createPublicTaskMessage = createMutationHook< 10 | CreatePublicTaskMessage, 11 | CreatePublicTaskMessageVariables 12 | >(gql` 13 | mutation CreatePublicTaskMessage($taskId: ID!, $message: String!) { 14 | createTaskMessages(taskId: $taskId, message: $message, isPublic: true) { 15 | success 16 | message { 17 | id 18 | message 19 | createdAt 20 | user { 21 | fullName 22 | } 23 | } 24 | } 25 | } 26 | `); 27 | 28 | export default createPublicTaskMessage; 29 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/components/SelectTemplateStep/components/MAPane.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 4 | import {Paper} from '@material-ui/core'; 5 | 6 | const useStyles = makeStyles((theme: Theme) => 7 | createStyles({ 8 | root: {}, 9 | invisible: { 10 | display: 'none', 11 | }, 12 | }) 13 | ); 14 | 15 | interface MAPaneProps { 16 | value?: number; 17 | index?: number; 18 | } 19 | 20 | export default function MAPane(props: MAPaneProps) { 21 | const {value, index} = props; 22 | const classes = useStyles(); 23 | 24 | return ( 25 | 30 |

M&A

31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/graphqlHelpers.ts: -------------------------------------------------------------------------------- 1 | import {gql, ApolloError} from 'apollo-boost'; 2 | import {useQuery, useMutation} from 'react-apollo'; 3 | import {DocumentNode} from 'graphql'; 4 | import {QueryResult, MutationTuple} from 'react-apollo'; 5 | 6 | export {gql, useQuery}; 7 | 8 | export function createQueryHook( 9 | query: DocumentNode 10 | ): (variables: TVariables) => QueryResult { 11 | return function(variables) { 12 | return useQuery(query, {variables}); 13 | }; 14 | } 15 | 16 | export function createMutationHook( 17 | mutation: DocumentNode 18 | ): ( 19 | variables: TVariables, 20 | onCompleted?: (data: TData) => void, 21 | onError?: (error: ApolloError) => void 22 | ) => MutationTuple { 23 | return function(variables) { 24 | return useMutation(mutation, { 25 | variables, 26 | }); 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/components/SelectTemplateStep/components/LegalPane.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 4 | import {Paper} from '@material-ui/core'; 5 | 6 | const useStyles = makeStyles((theme: Theme) => 7 | createStyles({ 8 | root: {}, 9 | invisible: { 10 | display: 'none', 11 | }, 12 | }) 13 | ); 14 | 15 | interface LegalPaneProps { 16 | value?: number; 17 | index?: number; 18 | } 19 | 20 | export default function LegalPane(props: LegalPaneProps) { 21 | const {value, index} = props; 22 | const classes = useStyles(); 23 | 24 | return ( 25 | 30 |

Legal

31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /app/graphql/types/user_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class UserType < GraphQL::Schema::Object 3 | description 'User' 4 | 5 | field :id, ID, null: false 6 | field :fullName, String, null: false 7 | field :email, String, null: false 8 | field :displayName, String, null: true 9 | field :ownedCompanies, [CompanyType], null: true 10 | field :companies, [CompanyType], null: true 11 | field :teams, [TeamType], null: true 12 | field :roles, [RolesUserType], null: true 13 | field :lastViewedCompanyId, ID, null: true 14 | field :profile_url, String, null: true 15 | 16 | def profile_url 17 | if object.profile_picture.attached? 18 | Rails.application.routes.url_helpers.rails_blob_url( 19 | object.profile_picture, 20 | only_path: true 21 | ) 22 | end 23 | end 24 | 25 | def roles 26 | object.roles_users 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/javascript/packs/application_pack.tsx: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | // This file is automatically compiled by Webpack, along with any other files 3 | // present in this directory. You're encouraged to place your actual application logic in 4 | // a relevant structure within app/javascript and only use these pack files to reference 5 | // that code so it'll be compiled. 6 | // 7 | // To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate 8 | // layout file, like app/views/layouts/application.html.erb 9 | 10 | // Uncomment to copy all static images under ../images to the output folder and reference 11 | // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>) 12 | // or the `imagePath` JavaScript helper below. 13 | // 14 | // const images = require.context('../images', true) 15 | // const imagePath = (name) => images(name, true) 16 | 17 | import '../src/application'; 18 | -------------------------------------------------------------------------------- /spec/graphql/mutations/sign_up_user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | module Mutations 4 | RSpec.describe SignUpUser, type: :request do 5 | describe 'resolve' do 6 | it 'Sign up user' do 7 | user = create(:user) 8 | expect do 9 | res = 10 | post '/graphql', 11 | params: { 12 | query: query(email: user.email, name: user.full_name) 13 | } 14 | to change { User.count }.by(1) 15 | end 16 | end 17 | end 18 | 19 | def query(email:, name:) 20 | <<~GQL 21 | mutation{ 22 | signUpUser( 23 | email: #{email}, 24 | fullName: #{ 25 | name 26 | } 27 | ) { 28 | user { 29 | email 30 | } 31 | errors { 32 | path 33 | message 34 | } 35 | success 36 | } 37 | } 38 | GQL 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/components/SelectTemplateStep/components/FinancePane.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 4 | import {Paper} from '@material-ui/core'; 5 | 6 | const useStyles = makeStyles((theme: Theme) => 7 | createStyles({ 8 | root: {}, 9 | invisible: { 10 | display: 'none', 11 | }, 12 | }) 13 | ); 14 | 15 | interface FinancePaneProps { 16 | value?: number; 17 | index?: number; 18 | } 19 | 20 | export default function FinancePane(props: FinancePaneProps) { 21 | const {value, index} = props; 22 | const classes = useStyles(); 23 | 24 | return ( 25 | 30 |

Finance

31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /app/graphql/mutations/send_reset_password_instructions.rb: -------------------------------------------------------------------------------- 1 | class Mutations::SendResetPasswordInstructions < GraphQL::Schema::Mutation 2 | argument :email, String, required: true 3 | 4 | field :errors, [Types::FormErrorType], null: false 5 | field :success, Boolean, null: false 6 | 7 | def resolve(email: nil) 8 | response = { errors: [] } 9 | 10 | if context[:controller].user_signed_in? 11 | response[:errors].push({ path: 'root', message: 'Already signed in.' }) 12 | response[:success] = false 13 | return response 14 | end 15 | 16 | user = User.find_for_database_authentication(email: email) 17 | 18 | unless user 19 | response[:errors].push({ path: 'email', message: 'Unrecognized email.' }) 20 | response[:success] = false 21 | return response 22 | end 23 | 24 | user.send_reset_password_instructions 25 | 26 | response[:success] = true 27 | response 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a way to update your development environment automatically. 14 | # Add necessary update steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | puts "\n== Updating database ==" 24 | system! 'bin/rails db:migrate' 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! 'bin/rails log:clear tmp:clear' 28 | 29 | puts "\n== Restarting application server ==" 30 | system! 'bin/rails restart' 31 | end 32 | -------------------------------------------------------------------------------- /app/assets/images/dummy/logos/drag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/assets/images/dummy/logos/drag-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/SignInUser.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import {SignInUser, SignInUserVariables} from './__generated__/SignInUser'; 3 | 4 | export const useSignInUser = createMutationHook< 5 | SignInUser, 6 | SignInUserVariables 7 | >(gql` 8 | mutation SignInUser( 9 | $email: String! 10 | $password: String! 11 | $socialLogin: Boolean! 12 | $provider: String! 13 | $tokenID: String! 14 | $uuID: String! 15 | ) { 16 | signInUser( 17 | email: $email 18 | password: $password 19 | socialLogin: $socialLogin 20 | provider: $provider 21 | tokenID: $tokenID 22 | uuID: $uuID 23 | ) { 24 | currentUser { 25 | id 26 | user { 27 | id 28 | fullName 29 | email 30 | } 31 | } 32 | errors { 33 | path 34 | message 35 | } 36 | success 37 | } 38 | } 39 | `); 40 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/InviteNewCompanyToList.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | InviteNewCompanyToList, 4 | InviteNewCompanyToListVariables, 5 | } from './__generated__/InviteNewCompanyToList'; 6 | 7 | export const useInviteNewCompanyToList = createMutationHook< 8 | InviteNewCompanyToList, 9 | InviteNewCompanyToListVariables 10 | >(gql` 11 | mutation InviteNewCompanyToList( 12 | $listId: ID! 13 | $companyId: ID! 14 | $ownerEmail: String! 15 | $newCompanyName: String! 16 | $isRequest: Boolean 17 | $isShare: Boolean 18 | ) { 19 | inviteNewCompanyToList( 20 | listId: $listId 21 | companyId: $companyId 22 | ownerEmail: $ownerEmail 23 | newCompanyName: $newCompanyName 24 | isRequest: $isRequest 25 | isShare: $isShare 26 | ) { 27 | errors { 28 | path 29 | message 30 | } 31 | success 32 | } 33 | } 34 | `); 35 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/CurrentUser.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import { 3 | CurrentUser, 4 | CurrentUser_currentUser_user, 5 | } from './__generated__/CurrentUser'; 6 | 7 | export type User = CurrentUser_currentUser_user; 8 | 9 | export const useCurrentUser = createQueryHook(gql` 10 | query CurrentUser { 11 | currentUser { 12 | id 13 | user { 14 | id 15 | email 16 | fullName 17 | displayName 18 | profileUrl 19 | lastViewedCompanyId 20 | ownedCompanies { 21 | id 22 | name 23 | } 24 | companies { 25 | id 26 | name 27 | logoUrl 28 | } 29 | teams { 30 | id 31 | companyId 32 | name 33 | } 34 | roles { 35 | id 36 | name 37 | companyId 38 | } 39 | } 40 | } 41 | } 42 | `); 43 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/__generated__/publicTaskMessages.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: publicTaskMessages 7 | // ==================================================== 8 | 9 | export interface publicTaskMessages_publicTaskMessages_user { 10 | __typename: "User"; 11 | fullName: string; 12 | } 13 | 14 | export interface publicTaskMessages_publicTaskMessages { 15 | __typename: "TaskMessage"; 16 | message: string; 17 | user: publicTaskMessages_publicTaskMessages_user; 18 | createdAt: string; 19 | } 20 | 21 | export interface publicTaskMessages { 22 | /** 23 | * A separate query to retrieve public messages 24 | */ 25 | publicTaskMessages: publicTaskMessages_publicTaskMessages[]; 26 | } 27 | 28 | export interface publicTaskMessagesVariables { 29 | taskId: string; 30 | } 31 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/RemoveTeamMember.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | RemoveTeamMember, 4 | RemoveTeamMemberVariables, 5 | } from './__generated__/RemoveTeamMember'; 6 | 7 | export const useRemoveTeamMember = createMutationHook< 8 | RemoveTeamMember, 9 | RemoveTeamMemberVariables 10 | >(gql` 11 | mutation RemoveTeamMember($teamId: ID!, $userId: ID!, $userIds: [ID!]) { 12 | removeTeamMember(teamId: $teamId, userId: $userId, userIds: $userIds) { 13 | user { 14 | id 15 | fullName 16 | profileUrl 17 | roles { 18 | id 19 | name 20 | companyId 21 | } 22 | teams { 23 | id 24 | name 25 | companyId 26 | } 27 | companies { 28 | id 29 | name 30 | } 31 | } 32 | errors { 33 | path 34 | message 35 | } 36 | success 37 | } 38 | } 39 | `); 40 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/__generated__/UserLists.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: UserLists 7 | // ==================================================== 8 | 9 | export interface UserLists_userLists_lists_sections { 10 | __typename: "TaskSection"; 11 | id: string; 12 | name: string | null; 13 | } 14 | 15 | export interface UserLists_userLists_lists { 16 | __typename: "List"; 17 | id: string; 18 | name: string | null; 19 | sections: UserLists_userLists_lists_sections[] | null; 20 | } 21 | 22 | export interface UserLists_userLists { 23 | __typename: "UserList"; 24 | id: string; 25 | lists: UserLists_userLists_lists[] | null; 26 | } 27 | 28 | export interface UserLists { 29 | /** 30 | * All users lists & tasks in current company 31 | */ 32 | userLists: UserLists_userLists; 33 | } 34 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/__generated__/privateTaskMessages.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: privateTaskMessages 7 | // ==================================================== 8 | 9 | export interface privateTaskMessages_privateTaskMessages_user { 10 | __typename: "User"; 11 | fullName: string; 12 | } 13 | 14 | export interface privateTaskMessages_privateTaskMessages { 15 | __typename: "TaskMessage"; 16 | message: string; 17 | user: privateTaskMessages_privateTaskMessages_user; 18 | createdAt: string; 19 | } 20 | 21 | export interface privateTaskMessages { 22 | /** 23 | * A separate query to retrieve private messages 24 | */ 25 | privateTaskMessages: privateTaskMessages_privateTaskMessages[]; 26 | } 27 | 28 | export interface privateTaskMessagesVariables { 29 | taskId: string; 30 | } 31 | -------------------------------------------------------------------------------- /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/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 Prepdd 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.2 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration can go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded after loading 17 | # the framework and any gems in your application. 18 | # config.generators.javascript_engine = :js 19 | config.generators.stylesheets = false 20 | config.generators.javascripts = false 21 | config.generators.helper = false 22 | 23 | config.autoload_paths << Rails.root.join('graphql', 'types') 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/CreateAssociatedCompany.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | CreateAssociatedCompany, 4 | CreateAssociatedCompanyVariables, 5 | } from './__generated__/CreateAssociatedCompany'; 6 | 7 | export const useCreateAssociatedCompany = createMutationHook< 8 | CreateAssociatedCompany, 9 | CreateAssociatedCompanyVariables 10 | >(gql` 11 | mutation CreateAssociatedCompany( 12 | $companyId: ID! 13 | $ownerEmail: String! 14 | $newCompanyName: String! 15 | $isParent: Boolean 16 | $isBroker: Boolean 17 | ) { 18 | createAssociatedCompany( 19 | companyId: $companyId 20 | ownerEmail: $ownerEmail 21 | newCompanyName: $newCompanyName 22 | isParent: $isParent 23 | isBroker: $isBroker 24 | ) { 25 | company { 26 | id 27 | name 28 | logoUrl 29 | } 30 | errors { 31 | path 32 | message 33 | } 34 | success 35 | } 36 | } 37 | `); 38 | -------------------------------------------------------------------------------- /.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 the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | .idea/ 15 | 16 | # Ignore all logfiles and tempfiles. 17 | /log/* 18 | /tmp/* 19 | !/log/.keep 20 | !/tmp/.keep 21 | 22 | # Ignore uploaded files in development 23 | /storage/* 24 | !/storage/.keep 25 | 26 | /node_modules 27 | /yarn-error.log 28 | 29 | /public/assets 30 | .byebug_history 31 | 32 | # Ignore master key for decrypting credentials and more. 33 | /config/master.key 34 | 35 | /public/packs 36 | /public/packs-test 37 | /node_modules 38 | /yarn-error.log 39 | yarn-debug.log* 40 | .yarn-integrity 41 | 42 | .DS_Store 43 | coverage 44 | 45 | /.env -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/CompanyUsers.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import {CompanyUsers} from './__generated__/CompanyUsers'; 3 | 4 | export const useCompanyUsers = createQueryHook(gql` 5 | query CompanyUsers($companyId: ID!, $teamId: ID, $limit: Int, $offset: Int) { 6 | companyUsers( 7 | CompanyId: $companyId 8 | TeamId: $teamId 9 | limit: $limit 10 | offset: $offset 11 | ) { 12 | company { 13 | id 14 | name 15 | teams { 16 | id 17 | name 18 | } 19 | } 20 | users { 21 | id 22 | fullName 23 | profileUrl 24 | roles { 25 | id 26 | name 27 | companyId 28 | } 29 | teams { 30 | id 31 | name 32 | companyId 33 | } 34 | companies { 35 | id 36 | name 37 | logoUrl 38 | } 39 | } 40 | } 41 | } 42 | `); 43 | -------------------------------------------------------------------------------- /app/workers/company/remove_un_used_companies_worker.rb: -------------------------------------------------------------------------------- 1 | class Company::RemoveUnUsedCompaniesWorker 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | Company.where(owner_id: nil).each do |company| 6 | if company.created_at < 1.day.ago 7 | begin 8 | # Remove s3 bucket of company 9 | if company.s3_location? 10 | s3_client = Aws::S3::Client.new 11 | bucket_name = company.s3_location.gsub('/', '') 12 | s3_client.delete_bucket(bucket: bucket_name) 13 | end 14 | 15 | # Remove KMS key of company 16 | if company.kms_key_id? 17 | client = Aws::KMS::Client.new 18 | key_id = company.kms_key_id 19 | resp = 20 | client.schedule_key_deletion( 21 | { key_id: key_id, pending_window_in_days: 7 } 22 | ) 23 | end 24 | rescue StandardError 25 | 26 | end 27 | 28 | company.destroy 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/CreateTask.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | import { TaskAttributes } from "./../../__generated__/globalTypes"; 6 | 7 | // ==================================================== 8 | // GraphQL mutation operation: CreateTask 9 | // ==================================================== 10 | 11 | export interface CreateTask_createTask_errors { 12 | __typename: "FormError"; 13 | /** 14 | * Which field this error came from 15 | */ 16 | path: string | null; 17 | /** 18 | * A description of the error 19 | */ 20 | message: string; 21 | } 22 | 23 | export interface CreateTask_createTask { 24 | __typename: "CreateTaskPayload"; 25 | errors: CreateTask_createTask_errors[]; 26 | success: boolean; 27 | } 28 | 29 | export interface CreateTask { 30 | createTask: CreateTask_createTask | null; 31 | } 32 | 33 | export interface CreateTaskVariables { 34 | listId: string; 35 | tasks: TaskAttributes[]; 36 | } 37 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/RemoveCompanyMember.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | RemoveCompanyMember, 4 | RemoveCompanyMemberVariables, 5 | } from './__generated__/RemoveCompanyMember'; 6 | 7 | export const useRemoveCompanyMember = createMutationHook< 8 | RemoveCompanyMember, 9 | RemoveCompanyMemberVariables 10 | >(gql` 11 | mutation RemoveCompanyMember($companyId: ID!, $userId: ID, $userIds: [ID!]) { 12 | removeCompanyMember( 13 | companyId: $companyId 14 | userId: $userId 15 | userIds: $userIds 16 | ) { 17 | errors { 18 | path 19 | message 20 | } 21 | success 22 | user { 23 | id 24 | fullName 25 | profileUrl 26 | roles { 27 | id 28 | name 29 | companyId 30 | } 31 | teams { 32 | id 33 | name 34 | companyId 35 | } 36 | companies { 37 | id 38 | name 39 | } 40 | } 41 | } 42 | } 43 | `); 44 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/AddListOwner.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: AddListOwner 7 | // ==================================================== 8 | 9 | export interface AddListOwner_addListOwner_errors { 10 | __typename: "FormError"; 11 | /** 12 | * Which field this error came from 13 | */ 14 | path: string | null; 15 | /** 16 | * A description of the error 17 | */ 18 | message: string; 19 | } 20 | 21 | export interface AddListOwner_addListOwner { 22 | __typename: "AddListOwnerPayload"; 23 | errors: AddListOwner_addListOwner_errors[]; 24 | success: boolean; 25 | } 26 | 27 | export interface AddListOwner { 28 | addListOwner: AddListOwner_addListOwner | null; 29 | } 30 | 31 | export interface AddListOwnerVariables { 32 | listId: string; 33 | companyId: string; 34 | userEmails?: string[] | null; 35 | teamIds?: string[] | null; 36 | } 37 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/UserTasks.ts: -------------------------------------------------------------------------------- 1 | import {createQueryHook, gql} from '../graphqlHelpers'; 2 | import {UserTasks} from './__generated__/UserTasks'; 3 | 4 | export const useUserTasks = createQueryHook(gql` 5 | query UserTasks( 6 | $listIds: [ID!]! 7 | $sectionIds: [ID!]! 8 | $offset: Int 9 | $limit: Int 10 | ) { 11 | userTasks( 12 | listIds: $listIds 13 | sectionIds: $sectionIds 14 | offset: $offset 15 | limit: $limit 16 | ) { 17 | id 18 | name 19 | priority 20 | status 21 | dueDate 22 | updatedAt 23 | listNumber 24 | list { 25 | id 26 | } 27 | userOwners { 28 | id 29 | email 30 | fullName 31 | profileUrl 32 | } 33 | teamOwners { 34 | id 35 | name 36 | } 37 | userReviewers { 38 | id 39 | email 40 | fullName 41 | profileUrl 42 | } 43 | teamReviewers { 44 | id 45 | name 46 | } 47 | } 48 | } 49 | `); 50 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/UpdateUserPassword.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: UpdateUserPassword 7 | // ==================================================== 8 | 9 | export interface UpdateUserPassword_updateUserPassword_errors { 10 | __typename: "FormError"; 11 | /** 12 | * Which field this error came from 13 | */ 14 | path: string | null; 15 | /** 16 | * A description of the error 17 | */ 18 | message: string; 19 | } 20 | 21 | export interface UpdateUserPassword_updateUserPassword { 22 | __typename: "UpdateUserPasswordPayload"; 23 | errors: UpdateUserPassword_updateUserPassword_errors[]; 24 | success: boolean; 25 | } 26 | 27 | export interface UpdateUserPassword { 28 | updateUserPassword: UpdateUserPassword_updateUserPassword | null; 29 | } 30 | 31 | export interface UpdateUserPasswordVariables { 32 | password: string; 33 | oldPassword: string; 34 | } 35 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/UpdateUserData.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: UpdateUserData 7 | // ==================================================== 8 | 9 | export interface UpdateUserData_updateUserData_errors { 10 | __typename: "FormError"; 11 | /** 12 | * Which field this error came from 13 | */ 14 | path: string | null; 15 | /** 16 | * A description of the error 17 | */ 18 | message: string; 19 | } 20 | 21 | export interface UpdateUserData_updateUserData { 22 | __typename: "UpdateUserDataPayload"; 23 | errors: UpdateUserData_updateUserData_errors[]; 24 | success: boolean; 25 | } 26 | 27 | export interface UpdateUserData { 28 | updateUserData: UpdateUserData_updateUserData | null; 29 | } 30 | 31 | export interface UpdateUserDataVariables { 32 | email: string; 33 | fullName: string; 34 | displayName: string; 35 | lastViewedCompanyId?: string | null; 36 | } 37 | -------------------------------------------------------------------------------- /app/javascript/src/modules/list/CreateListPage/components/CreateListStep/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 3 | import Typography from '@material-ui/core/Typography'; 4 | import WarningIcon from '@material-ui/icons/WarningRounded'; 5 | 6 | const useStyles = makeStyles((theme: Theme) => 7 | createStyles({ 8 | root: { 9 | display: 'flex', 10 | width: '100%', 11 | alignItems: 'center', 12 | backgroundColor: 'rgba(248,231,28,0.20)', 13 | border: '1px solid #D8D8D8', 14 | borderRadius: '3px', 15 | marginTop: '12px', 16 | padding: '12px 20px', 17 | }, 18 | }) 19 | ); 20 | 21 | export default function Alert() { 22 | const classes = useStyles(); 23 | 24 | return ( 25 |
26 | 27 | 28 | This list and all of it’s contents will be public to the selected 29 | company upon creation. 30 | 31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /app/javascript/src/modules/route/app.tsx: -------------------------------------------------------------------------------- 1 | import LoadingFallback from '../common/LoadingFallback'; 2 | import React, {lazy, Suspense} from 'react'; 3 | import {Switch, Route} from 'react-router-dom'; 4 | 5 | const Layout = lazy(() => import('../layout')); 6 | const CompanyRoutes = lazy(() => import('./company')); 7 | const TeamRoutes = lazy(() => import('./team')); 8 | const UserRoutes = lazy(() => import('./user')); 9 | const ListRoutes = lazy(() => import('./list')); 10 | const TaskRoutes = lazy(() => import('./task')); 11 | 12 | export default function AppRoute() { 13 | return ( 14 | }> 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a starting point to setup your application. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! 'bin/rails db:setup' 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! 'bin/rails log:clear tmp:clear' 33 | 34 | puts "\n== Restarting application server ==" 35 | system! 'bin/rails restart' 36 | end 37 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/UpdateTeamMember.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | UpdateTeamMember, 4 | UpdateTeamMemberVariables, 5 | } from './__generated__/UpdateTeamMember'; 6 | 7 | export const useUpdateTeamMember = createMutationHook< 8 | UpdateTeamMember, 9 | UpdateTeamMemberVariables 10 | >(gql` 11 | mutation UpdateTeamMember( 12 | $id: ID! 13 | $fullName: String! 14 | $companyId: ID! 15 | $role: ID! 16 | ) { 17 | updateTeamMember( 18 | id: $id 19 | fullName: $fullName 20 | companyId: $companyId 21 | role: $role 22 | ) { 23 | success 24 | errors { 25 | path 26 | message 27 | } 28 | user { 29 | id 30 | fullName 31 | profileUrl 32 | roles { 33 | id 34 | name 35 | companyId 36 | } 37 | teams { 38 | id 39 | name 40 | companyId 41 | } 42 | companies { 43 | id 44 | name 45 | logoUrl 46 | } 47 | } 48 | } 49 | } 50 | `); 51 | -------------------------------------------------------------------------------- /app/graphql/types/list_type.rb: -------------------------------------------------------------------------------- 1 | module Types 2 | class ListType < GraphQL::Schema::Object 3 | description 'Lists' 4 | 5 | field :id, String, null: false 6 | field :name, String, null: true 7 | field :description, String, null: true 8 | field :isActive, Boolean, null: true 9 | field :isTemplate, Boolean, null: true 10 | field :isPublicTemplate, Boolean, null: true 11 | field :requesterCompany, CompanyType, null: true 12 | field :responderCompany, CompanyType, null: true 13 | field :tasks, [TaskType], null: true 14 | field :owners, [UserType], null: true 15 | field :sections, [TaskSectionType], null: true 16 | field :requester_rank, Integer, null: true 17 | field :responder_rank, Integer, null: true 18 | 19 | def requester_company 20 | object.requester 21 | end 22 | 23 | def responder_company 24 | object.responder 25 | end 26 | 27 | def sections 28 | sections = 29 | object.tasks.includes(:task_section).map do |task| 30 | task.task_section&.id 31 | end 32 | TaskSection.where(id: sections) 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /db/migrate/20190822034212_create_active_storage_tables.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20170806125915) 2 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2] 3 | def change 4 | create_table :active_storage_blobs do |t| 5 | t.string :key, null: false 6 | t.string :filename, null: false 7 | t.string :content_type 8 | t.text :metadata 9 | t.bigint :byte_size, null: false 10 | t.string :checksum, null: false 11 | t.datetime :created_at, null: false 12 | 13 | t.index [ :key ], unique: true 14 | end 15 | 16 | create_table :active_storage_attachments do |t| 17 | t.string :name, null: false 18 | t.references :record, null: false, polymorphic: true, index: false 19 | t.references :blob, null: false 20 | 21 | t.datetime :created_at, null: false 22 | 23 | t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true 24 | t.foreign_key :active_storage_blobs, column: :blob_id 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/__generated__/AllTemplates.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: AllTemplates 7 | // ==================================================== 8 | 9 | export interface AllTemplates_templateLists_tasks_section { 10 | __typename: "TaskSection"; 11 | id: string; 12 | name: string | null; 13 | } 14 | 15 | export interface AllTemplates_templateLists_tasks { 16 | __typename: "Task"; 17 | id: string; 18 | name: string | null; 19 | section: AllTemplates_templateLists_tasks_section | null; 20 | description: string | null; 21 | priority: string | null; 22 | status: string | null; 23 | } 24 | 25 | export interface AllTemplates_templateLists { 26 | __typename: "List"; 27 | id: string; 28 | name: string | null; 29 | tasks: AllTemplates_templateLists_tasks[] | null; 30 | } 31 | 32 | export interface AllTemplates { 33 | /** 34 | * All Available lists templates 35 | */ 36 | templateLists: AllTemplates_templateLists[]; 37 | } 38 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/AddTeamMember.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | AddTeamMember, 4 | AddTeamMemberVariables, 5 | } from './__generated__/AddTeamMember'; 6 | 7 | export const useAddTeamMember = createMutationHook< 8 | AddTeamMember, 9 | AddTeamMemberVariables 10 | >(gql` 11 | mutation AddTeamMember( 12 | $email: String! 13 | $fullName: String! 14 | $role: ID! 15 | $team: String! 16 | $companyId: ID! 17 | ) { 18 | addTeamMember( 19 | email: $email 20 | fullName: $fullName 21 | role: $role 22 | team: $team 23 | companyId: $companyId 24 | ) { 25 | errors { 26 | path 27 | message 28 | } 29 | success 30 | user { 31 | id 32 | fullName 33 | profileUrl 34 | roles { 35 | id 36 | name 37 | companyId 38 | } 39 | teams { 40 | id 41 | name 42 | companyId 43 | } 44 | companies { 45 | id 46 | name 47 | logoUrl 48 | } 49 | } 50 | } 51 | } 52 | `); 53 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/CreateCompany.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: CreateCompany 7 | // ==================================================== 8 | 9 | export interface CreateCompany_createCompany_company { 10 | __typename: "Company"; 11 | name: string; 12 | } 13 | 14 | export interface CreateCompany_createCompany_errors { 15 | __typename: "FormError"; 16 | /** 17 | * Which field this error came from 18 | */ 19 | path: string | null; 20 | /** 21 | * A description of the error 22 | */ 23 | message: string; 24 | } 25 | 26 | export interface CreateCompany_createCompany { 27 | __typename: "CreateCompanyPayload"; 28 | company: CreateCompany_createCompany_company | null; 29 | errors: CreateCompany_createCompany_errors[]; 30 | success: boolean; 31 | } 32 | 33 | export interface CreateCompany { 34 | createCompany: CreateCompany_createCompany | null; 35 | } 36 | 37 | export interface CreateCompanyVariables { 38 | name: string; 39 | } 40 | -------------------------------------------------------------------------------- /app/graphql/mutations/create_team.rb: -------------------------------------------------------------------------------- 1 | class Mutations::CreateTeam < GraphQL::Schema::Mutation 2 | argument :name, String, required: true 3 | argument :companyID, String, required: true 4 | 5 | field :team, Types::TeamType, null: true 6 | field :errors, [Types::FormErrorType], null: false 7 | field :success, Boolean, null: false 8 | 9 | def resolve(name: nil, company_id: nil) 10 | response = { errors: [] } 11 | 12 | if !context[:controller].user_signed_in? 13 | response[:errors].push( 14 | { path: 'root', message: 'Not authorized to do it.' } 15 | ) 16 | response[:success] = false 17 | return response 18 | end 19 | 20 | team = Team.create(name: name, company_id: company_id, is_active: true) 21 | 22 | team.errors.messages.each do |path, messages| 23 | messages.each do |message| 24 | response[:errors].push( 25 | { path: path.to_s.camelcase(:lower), message: message } 26 | ) 27 | response[:success] = false 28 | end 29 | end 30 | 31 | if team&.persisted? 32 | response[:success] = true 33 | response 34 | end 35 | 36 | response 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/javascript/src/modules/file/FilesPage/components/Body/Body.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import idx from 'idx'; 4 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 5 | import {Typography} from '@material-ui/core'; 6 | 7 | import Panel from '../../../../common/Panel'; 8 | import SingleTask from './components/SingleTask'; 9 | import MultipleTasks from './components/MultipleTasks'; 10 | 11 | const useStyles = makeStyles((theme: Theme) => 12 | createStyles({ 13 | root: { 14 | padding: '0px calc((100% - 1080px) / 2) 0px calc((100% - 1080px) / 2)', 15 | }, 16 | flex: { 17 | display: 'flex', 18 | alignItems: 'center', 19 | }, 20 | }) 21 | ); 22 | 23 | const labels = [ 24 | { label: 'Single Task' }, 25 | { label: 'Multiple Tasks' }, 26 | ]; 27 | 28 | export default function Body() { 29 | const classes = useStyles(); 30 | 31 | return ( 32 |
33 | Add Files 34 | 35 | 36 | 37 | 38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/SendPasswordResetInstructions.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: SendPasswordResetInstructions 7 | // ==================================================== 8 | 9 | export interface SendPasswordResetInstructions_sendResetPasswordInstructions_errors { 10 | __typename: "FormError"; 11 | /** 12 | * Which field this error came from 13 | */ 14 | path: string | null; 15 | /** 16 | * A description of the error 17 | */ 18 | message: string; 19 | } 20 | 21 | export interface SendPasswordResetInstructions_sendResetPasswordInstructions { 22 | __typename: "SendResetPasswordInstructionsPayload"; 23 | errors: SendPasswordResetInstructions_sendResetPasswordInstructions_errors[]; 24 | success: boolean; 25 | } 26 | 27 | export interface SendPasswordResetInstructions { 28 | sendResetPasswordInstructions: SendPasswordResetInstructions_sendResetPasswordInstructions | null; 29 | } 30 | 31 | export interface SendPasswordResetInstructionsVariables { 32 | email: string; 33 | } 34 | -------------------------------------------------------------------------------- /spec/graphql/types/team_type_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | module Types 3 | RSpec.describe TeamType do 4 | set_graphql_type 5 | # avail type definer in our tests 6 | types = GraphQL::Define::TypeDefiner.instance 7 | 8 | it 'has an :id field of ID type' do 9 | # Ensure that the field id is of type ID 10 | expect(subject.fields['id'].type.to_type_signature).to eq('ID!') 11 | end 12 | 13 | it 'has an :companyId field of ID type' do 14 | # Ensure that the field id is of type ID 15 | expect(subject.fields['companyId'].type.to_type_signature).to eq('ID!') 16 | end 17 | 18 | it 'has a :name field of String type' do 19 | # Ensure the field is of String type 20 | expect(subject.fields['name'].type.to_type_signature).to eq('String!') 21 | end 22 | 23 | it 'has :users field of User type' do 24 | # Ensure the field is of User type 25 | expect(subject.fields['users'].type.to_type_signature).to eq('[User!]!') 26 | end 27 | 28 | it 'has :company field of Company type' do 29 | # Ensure the field is of Company type 30 | expect(subject.fields['company'].type.to_type_signature).to eq('Company!') 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/javascript/src/helpers/__generated__/SearchCompanyUsers.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: SearchCompanyUsers 7 | // ==================================================== 8 | 9 | export interface SearchCompanyUsers_searchCompanyUsers_users { 10 | __typename: "User"; 11 | id: string; 12 | email: string; 13 | fullName: string; 14 | profileUrl: string | null; 15 | } 16 | 17 | export interface SearchCompanyUsers_searchCompanyUsers_teams { 18 | __typename: "Team"; 19 | id: string; 20 | name: string; 21 | } 22 | 23 | export interface SearchCompanyUsers_searchCompanyUsers { 24 | __typename: "SearchCompanyUsers"; 25 | users: SearchCompanyUsers_searchCompanyUsers_users[] | null; 26 | teams: SearchCompanyUsers_searchCompanyUsers_teams[] | null; 27 | } 28 | 29 | export interface SearchCompanyUsers { 30 | /** 31 | * Search users by company id 32 | */ 33 | searchCompanyUsers: SearchCompanyUsers_searchCompanyUsers; 34 | } 35 | 36 | export interface SearchCompanyUsersVariables { 37 | text: string; 38 | companyId: string; 39 | } 40 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | 19 | # If you are using UJS then enable automatic nonce generation 20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 21 | 22 | # Report CSP violations to a specified URI 23 | # For further information see the following documentation: 24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 25 | # Rails.application.config.content_security_policy_report_only = true 26 | -------------------------------------------------------------------------------- /app/graphql/mutations/update_task.rb: -------------------------------------------------------------------------------- 1 | class Mutations::UpdateTask < GraphQL::Schema::Mutation 2 | argument :id, ID, required: true 3 | argument :name, String, required: false 4 | argument :description, String, required: false 5 | argument :priority, String, required: false 6 | argument :status, String, required: false 7 | argument :dueDate, String, required: false 8 | 9 | field :task, Types::TaskType, null: false 10 | field :errors, [Types::FormErrorType], null: false 11 | field :success, Boolean, null: false 12 | 13 | def resolve( 14 | id: nil, 15 | name: nil, 16 | description: nil, 17 | priority: nil, 18 | status: nil, 19 | due_date: nil 20 | ) 21 | response = { errors: [] } 22 | 23 | task = Task.find(id) 24 | 25 | task.update(name: name) if name.present? 26 | 27 | task.update(description: description) if description.present? 28 | 29 | task.update(priority: priority) if priority.present? 30 | 31 | task.update(status: status) if status.present? 32 | 33 | task.update(due_date: due_date) if due_date.present? 34 | 35 | if task&.persisted? 36 | response[:success] = true 37 | response[:task] = task 38 | end 39 | 40 | response 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/CreatePublicTaskMessage.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: CreatePublicTaskMessage 7 | // ==================================================== 8 | 9 | export interface CreatePublicTaskMessage_createTaskMessages_message_user { 10 | __typename: "User"; 11 | fullName: string; 12 | } 13 | 14 | export interface CreatePublicTaskMessage_createTaskMessages_message { 15 | __typename: "TaskMessage"; 16 | id: string; 17 | message: string; 18 | createdAt: string; 19 | user: CreatePublicTaskMessage_createTaskMessages_message_user; 20 | } 21 | 22 | export interface CreatePublicTaskMessage_createTaskMessages { 23 | __typename: "CreateTaskMessagesPayload"; 24 | success: boolean; 25 | message: CreatePublicTaskMessage_createTaskMessages_message | null; 26 | } 27 | 28 | export interface CreatePublicTaskMessage { 29 | createTaskMessages: CreatePublicTaskMessage_createTaskMessages | null; 30 | } 31 | 32 | export interface CreatePublicTaskMessageVariables { 33 | taskId: string; 34 | message: string; 35 | } 36 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/CreatePrivateTaskMessage.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: CreatePrivateTaskMessage 7 | // ==================================================== 8 | 9 | export interface CreatePrivateTaskMessage_createTaskMessages_message_user { 10 | __typename: "User"; 11 | fullName: string; 12 | } 13 | 14 | export interface CreatePrivateTaskMessage_createTaskMessages_message { 15 | __typename: "TaskMessage"; 16 | id: string; 17 | message: string; 18 | createdAt: string; 19 | user: CreatePrivateTaskMessage_createTaskMessages_message_user; 20 | } 21 | 22 | export interface CreatePrivateTaskMessage_createTaskMessages { 23 | __typename: "CreateTaskMessagesPayload"; 24 | success: boolean; 25 | message: CreatePrivateTaskMessage_createTaskMessages_message | null; 26 | } 27 | 28 | export interface CreatePrivateTaskMessage { 29 | createTaskMessages: CreatePrivateTaskMessage_createTaskMessages | null; 30 | } 31 | 32 | export interface CreatePrivateTaskMessageVariables { 33 | taskId: string; 34 | message: string; 35 | } 36 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/InviteNewCompanyToList.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: InviteNewCompanyToList 7 | // ==================================================== 8 | 9 | export interface InviteNewCompanyToList_inviteNewCompanyToList_errors { 10 | __typename: "FormError"; 11 | /** 12 | * Which field this error came from 13 | */ 14 | path: string | null; 15 | /** 16 | * A description of the error 17 | */ 18 | message: string; 19 | } 20 | 21 | export interface InviteNewCompanyToList_inviteNewCompanyToList { 22 | __typename: "InviteNewCompanyToListPayload"; 23 | errors: InviteNewCompanyToList_inviteNewCompanyToList_errors[]; 24 | success: boolean; 25 | } 26 | 27 | export interface InviteNewCompanyToList { 28 | inviteNewCompanyToList: InviteNewCompanyToList_inviteNewCompanyToList | null; 29 | } 30 | 31 | export interface InviteNewCompanyToListVariables { 32 | listId: string; 33 | companyId: string; 34 | ownerEmail: string; 35 | newCompanyName: string; 36 | isRequest?: boolean | null; 37 | isShare?: boolean | null; 38 | } 39 | -------------------------------------------------------------------------------- /app/javascript/src/modules/user/UserProfile.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import clsx from 'clsx'; 3 | import {makeStyles} from '@material-ui/core/styles'; 4 | import Typography from '@material-ui/core/Typography'; 5 | 6 | import Panel from '../common/Panel'; 7 | import ProfilePane from './components/ProfilePane'; 8 | import NotificationPane from './components/NotificationPane'; 9 | 10 | const useStyle = makeStyles({ 11 | root: { 12 | display: 'block', 13 | padding: `84px calc((100% - 900px) / 2) 0px calc((100% - 900px) / 2)`, 14 | height: 'calc(100vh - 64px)', 15 | }, 16 | title: { 17 | color: '#2C2C2C', 18 | fontFamily: 'Montserrat', 19 | fontSize: '24px', 20 | fontWeight: 'bold', 21 | }, 22 | }); 23 | 24 | const labels = [ 25 | {label: 'Personal Information'}, 26 | {label: 'Notification Settings'}, 27 | ]; 28 | 29 | export default function Profile(props: {path?: string}) { 30 | const classes = useStyle(); 31 | 32 | return ( 33 |
34 | 35 | Profile 36 | 37 | 38 | 39 | 40 | 41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /app/graphql/mutations/update_user_data.rb: -------------------------------------------------------------------------------- 1 | class Mutations::UpdateUserData < GraphQL::Schema::Mutation 2 | argument :fullName, String, required: true 3 | argument :email, String, required: true 4 | argument :displayName, String, required: true 5 | argument :lastViewedCompanyId, ID, required: false 6 | 7 | field :user, Types::UserType, null: true 8 | field :errors, [Types::FormErrorType], null: false 9 | field :success, Boolean, null: false 10 | 11 | def resolve( 12 | full_name: nil, email: nil, display_name: nil, last_viewed_company_id: nil 13 | ) 14 | response = { errors: [] } 15 | 16 | user = context[:controller].current_user 17 | 18 | user.update( 19 | { 20 | full_name: full_name, 21 | email: email, 22 | display_name: display_name, 23 | last_viewed_company_id: last_viewed_company_id 24 | } 25 | ) 26 | 27 | user.errors.messages.each do |path, messages| 28 | messages.each do |message| 29 | response[:errors].push( 30 | { path: path.to_s.camelcase(:lower), message: message } 31 | ) 32 | response[:success] = false 33 | end 34 | end 35 | 36 | if user.persisted? 37 | response[:success] = true 38 | response 39 | end 40 | 41 | response 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/javascript/src/modules/user/components/NotificationPane/NotificationPane.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 4 | import {Paper, Typography, Select, MenuItem} from '@material-ui/core'; 5 | 6 | const useStyles = makeStyles((theme: Theme) => 7 | createStyles({ 8 | root: { 9 | paddingTop: '36px', 10 | }, 11 | invisible: { 12 | display: 'none', 13 | }, 14 | title: { 15 | fontFamily: 'Montserrat', 16 | fontSize: '24px', 17 | fontWeight: 'bold', 18 | color: '#2C2C2C', 19 | }, 20 | settings: { 21 | display: 'flex', 22 | }, 23 | }) 24 | ); 25 | 26 | interface NotificationPaneProps { 27 | value?: number; 28 | index?: number; 29 | } 30 | 31 | export default function NotificationPane(props: NotificationPaneProps) { 32 | const {value, index} = props; 33 | const classes = useStyles(); 34 | 35 | return ( 36 | 41 | 42 | Alert Notifications 43 | 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /spec/graphql/mutations/update_user_data.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | module Mutations 4 | RSpec.describe UpdateUserData, type: :request do 5 | describe '.resolve' do 6 | it 'updates a user details' do 7 | user = create(:user) 8 | 9 | expect do 10 | post '/graphql', 11 | params: { query: query(email: user.email, name: user.full_name) } 12 | post '/graphql', 13 | params: { query: query_update_user(name: 'Aijaz Khan') } 14 | it { expect(User.first.full_name).to eq 'Aijaz Khan' } 15 | end 16 | end 17 | end 18 | 19 | def query(email:, name:) 20 | <<~GQL 21 | mutation{ 22 | signUpUser( 23 | email: #{email}, 24 | fullName: #{ 25 | name 26 | } 27 | ) { 28 | user { 29 | email 30 | } 31 | errors { 32 | path 33 | message 34 | } 35 | success 36 | } 37 | } 38 | GQL 39 | end 40 | 41 | def query_update_user(name:) 42 | <<~GQL 43 | mutation{ 44 | updateUserData( 45 | fullName: #{ 46 | name 47 | } 48 | ) { 49 | errors { 50 | path 51 | message 52 | } 53 | success 54 | } 55 | } 56 | GQL 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/graphql/mutations/update_company_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | module Mutations 4 | RSpec.describe UpdateCompanySettings, type: :request do 5 | describe '.resolve' do 6 | it 'update a company details' do 7 | company = create(:company) 8 | expect do 9 | post '/graphql', params: { query: query(name: company.name) } 10 | post '/graphql', 11 | params: { 12 | query: 13 | query_update_company(id: company.id, name: 'PrepDD-Test') 14 | } 15 | it { expect(Company.first.full_name).to eq 'PrepDD-Test' } 16 | end 17 | end 18 | end 19 | 20 | def query(name:) 21 | <<~GQL 22 | mutation{ 23 | CreateCompany( 24 | name: #{ 25 | name 26 | }, 27 | ) { 28 | errors { 29 | path 30 | message 31 | } 32 | success 33 | } 34 | } 35 | GQL 36 | end 37 | 38 | def query_update_company(id:, name:) 39 | <<~GQL 40 | mutation{ 41 | updateCompany( 42 | id: #{id}, 43 | name: #{ 44 | name 45 | } 46 | ) { 47 | errors { 48 | path 49 | message 50 | } 51 | success 52 | } 53 | } 54 | GQL 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /app/javascript/src/modules/common/DefaultUserImage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 4 | 5 | const useStyles = makeStyles((theme: Theme) => 6 | createStyles({ 7 | root: { 8 | display: 'flex', 9 | width: '30px', 10 | height: '30px', 11 | position: 'relative', 12 | borderRadius: '50%', 13 | background: '#AFAFAF', 14 | alignItems: 'center', 15 | justifyContent: 'center', 16 | fontFamily: 'Montserrat', 17 | fontSize: '12px', 18 | fontWeight: 'bold', 19 | }, 20 | label: { 21 | color: '#FFFFFF', 22 | }, 23 | }) 24 | ); 25 | 26 | interface DefaultUserImageProps { 27 | className?: string; 28 | style?: React.CSSProperties; 29 | userName: string; 30 | } 31 | 32 | export default function DefaultUserImage(props: DefaultUserImageProps) { 33 | const {className, style, userName} = props; 34 | const classes = useStyles(); 35 | 36 | return ( 37 |
38 |

39 | {userName 40 | .split(' ') 41 | .slice(0, 2) 42 | .map(name => name[0]) 43 | .join('')} 44 |

45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/CreateList.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | import { TaskAttributes } from "./../../__generated__/globalTypes"; 6 | 7 | // ==================================================== 8 | // GraphQL mutation operation: CreateList 9 | // ==================================================== 10 | 11 | export interface CreateList_createList_list { 12 | __typename: "List"; 13 | id: string; 14 | name: string | null; 15 | } 16 | 17 | export interface CreateList_createList_errors { 18 | __typename: "FormError"; 19 | /** 20 | * Which field this error came from 21 | */ 22 | path: string | null; 23 | /** 24 | * A description of the error 25 | */ 26 | message: string; 27 | } 28 | 29 | export interface CreateList_createList { 30 | __typename: "CreateListPayload"; 31 | list: CreateList_createList_list | null; 32 | errors: CreateList_createList_errors[]; 33 | success: boolean; 34 | } 35 | 36 | export interface CreateList { 37 | createList: CreateList_createList | null; 38 | } 39 | 40 | export interface CreateListVariables { 41 | name: string; 42 | description?: string | null; 43 | requesterId: string; 44 | responderId: string; 45 | tasks?: TaskAttributes[] | null; 46 | } 47 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/queries/__generated__/UserDetails.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: UserDetails 7 | // ==================================================== 8 | 9 | export interface UserDetails_user_roles { 10 | __typename: "RolesUser"; 11 | id: string; 12 | name: string; 13 | companyId: string; 14 | } 15 | 16 | export interface UserDetails_user_teams { 17 | __typename: "Team"; 18 | id: string; 19 | name: string; 20 | companyId: string; 21 | } 22 | 23 | export interface UserDetails_user_companies { 24 | __typename: "Company"; 25 | id: string; 26 | name: string; 27 | logoUrl: string | null; 28 | } 29 | 30 | export interface UserDetails_user { 31 | __typename: "User"; 32 | id: string; 33 | email: string; 34 | fullName: string; 35 | profileUrl: string | null; 36 | roles: UserDetails_user_roles[] | null; 37 | teams: UserDetails_user_teams[] | null; 38 | companies: UserDetails_user_companies[] | null; 39 | } 40 | 41 | export interface UserDetails { 42 | /** 43 | * Return details of a user 44 | */ 45 | user: UserDetails_user; 46 | } 47 | 48 | export interface UserDetailsVariables { 49 | id: string; 50 | } 51 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/UpdateTask.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import {UpdateTask, UpdateTaskVariables} from './__generated__/UpdateTask'; 3 | 4 | export const useUpdateTask = createMutationHook< 5 | UpdateTask, 6 | UpdateTaskVariables 7 | >(gql` 8 | mutation UpdateTask( 9 | $id: ID! 10 | $name: String 11 | $description: String 12 | $priority: String 13 | $status: String 14 | $dueDate: String 15 | ) { 16 | updateTask( 17 | id: $id 18 | name: $name 19 | description: $description 20 | priority: $priority 21 | status: $status 22 | dueDate: $dueDate 23 | ) { 24 | success 25 | errors { 26 | path 27 | message 28 | } 29 | task { 30 | id 31 | name 32 | priority 33 | status 34 | dueDate 35 | updatedAt 36 | userOwners { 37 | id 38 | email 39 | fullName 40 | profileUrl 41 | } 42 | teamOwners { 43 | id 44 | name 45 | } 46 | userReviewers { 47 | id 48 | email 49 | fullName 50 | profileUrl 51 | } 52 | teamReviewers { 53 | id 54 | name 55 | } 56 | } 57 | } 58 | } 59 | `); 60 | -------------------------------------------------------------------------------- /app/graphql/mutations/create_task.rb: -------------------------------------------------------------------------------- 1 | class Mutations::CreateTask < GraphQL::Schema::Mutation 2 | argument :listId, ID, required: true 3 | argument :tasks, [Types::TaskAttributes], required: true 4 | 5 | field :task, Types::TaskType, null: true 6 | field :errors, [Types::FormErrorType], null: false 7 | field :success, Boolean, null: false 8 | 9 | def resolve(list_id: nil, tasks: nil) 10 | response = { errors: [] } 11 | 12 | list = List.find(list_id) 13 | 14 | tasks.each do |task| 15 | # We want to set the list_number as the id of the last task in the list 16 | # plus one 17 | list_number = 18 | list.tasks.any? ? list.tasks.last.list_number + 1 : 1 19 | if task.section.present? 20 | task_section = TaskSection.where(name: task.section).first_or_create 21 | list.tasks.create( 22 | name: task.name, 23 | description: task.description, 24 | priority: task.priority, 25 | task_section_id: task_section&.id, 26 | list_number: list_number 27 | ) 28 | else 29 | list.tasks.create( 30 | name: task.name, 31 | description: task.description, 32 | priority: task.priority, 33 | list_number: list_number 34 | ) 35 | end 36 | end 37 | 38 | response[:success] = true 39 | response 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/javascript/src/application.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {render} from 'react-dom'; 3 | import ApolloClient from 'apollo-client'; 4 | import {ApolloProvider} from 'react-apollo'; 5 | import {createHttpLink} from 'apollo-link-http'; 6 | import {InMemoryCache} from 'apollo-cache-inmemory'; 7 | import {ThemeProvider} from '@material-ui/styles'; 8 | import CssBaseline from '@material-ui/core/CssBaseline'; 9 | import App from './containers/app'; 10 | import theme from './config/theme'; 11 | import {Provider} from './store'; 12 | 13 | function getCSRFToken(): string { 14 | const el = document.querySelector('meta[name="csrf-token"]'); 15 | return (el && el.getAttribute('content')) || ''; 16 | } 17 | 18 | document.addEventListener('DOMContentLoaded', () => { 19 | const link = createHttpLink({ 20 | credentials: 'same-origin', 21 | headers: { 22 | 'X-CSRF-Token': getCSRFToken(), 23 | }, 24 | }); 25 | 26 | const client = new ApolloClient({ 27 | link, 28 | cache: new InMemoryCache(), 29 | connectToDevTools: true, 30 | }); 31 | 32 | render( 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | , 41 | document.body.appendChild(document.createElement('div')) 42 | ); 43 | }); 44 | -------------------------------------------------------------------------------- /app/graphql/mutations/remove_team_member.rb: -------------------------------------------------------------------------------- 1 | class Mutations::RemoveTeamMember < GraphQL::Schema::Mutation 2 | argument :teamId, ID, required: true 3 | argument :userId, ID, required: false 4 | argument :userIds, [ID], required: false 5 | 6 | field :user, Types::UserType, null: true 7 | field :teams, [Types::TeamType], null: true 8 | field :team, Types::TeamType, null: true 9 | field :companies, [Types::CompanyType], null: true 10 | field :role, Types::RoleType, null: true 11 | field :errors, [Types::FormErrorType], null: false 12 | field :success, Boolean, null: false 13 | 14 | def resolve(team_id: nil, user_id: nil, user_ids: nil) 15 | response = { errors: [] } 16 | 17 | if user_id 18 | TeamsUser.where(user_id: user_id, team_id: team_id).first&.destroy! 19 | elsif user_ids.present? 20 | user_ids.each do |id| 21 | TeamsUser.where(user_id: id, team_id: team_id).first&.destroy! 22 | end 23 | end 24 | 25 | company = Team.find(team_id)&.company 26 | 27 | user = User.find(user_id) 28 | teams = user.teams 29 | role = RolesUser.where(user_id: user.id, company_id: company.id).first.role 30 | companies = user.companies 31 | 32 | response[:success] = true 33 | response[:user] = user 34 | response[:companies] = companies 35 | response[:teams] = teams 36 | response[:role] = role 37 | 38 | response 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/AddTaskOwners.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import { 3 | AddTaskOwners, 4 | AddTaskOwnersVariables, 5 | } from './__generated__/AddTaskOwners'; 6 | 7 | export const useAddTaskOwners = createMutationHook< 8 | AddTaskOwners, 9 | AddTaskOwnersVariables 10 | >(gql` 11 | mutation AddTaskOwners( 12 | $taskID: ID! 13 | $userOwners: [String!] 14 | $userReviewers: [String!] 15 | $teamOwners: [ID!] 16 | $teamReviewers: [ID!] 17 | ) { 18 | addTaskOwners( 19 | taskID: $taskID 20 | userOwners: $userOwners 21 | userReviewers: $userReviewers 22 | teamOwners: $teamOwners 23 | teamReviewers: $teamReviewers 24 | ) { 25 | errors { 26 | path 27 | message 28 | } 29 | success 30 | task { 31 | id 32 | name 33 | priority 34 | status 35 | dueDate 36 | updatedAt 37 | userOwners { 38 | id 39 | email 40 | fullName 41 | profileUrl 42 | } 43 | teamOwners { 44 | id 45 | name 46 | } 47 | userReviewers { 48 | id 49 | email 50 | fullName 51 | profileUrl 52 | } 53 | teamReviewers { 54 | id 55 | name 56 | } 57 | } 58 | } 59 | } 60 | `); 61 | -------------------------------------------------------------------------------- /app/controllers/graphql_controller.rb: -------------------------------------------------------------------------------- 1 | class GraphqlController < ApplicationController 2 | def execute 3 | variables = ensure_hash(params[:variables]) 4 | query = params[:query] 5 | operation_name = params[:operationName] 6 | context = { 7 | # sign_out: sign_out 8 | session: session, 9 | # current_user: current_user, 10 | controller: self 11 | } 12 | 13 | result = 14 | PrepddSchema.execute( 15 | query, 16 | variables: variables, context: context, operation_name: operation_name 17 | ) 18 | render json: result 19 | rescue => e 20 | raise e unless Rails.env.development? 21 | handle_error_in_development e 22 | end 23 | 24 | private 25 | 26 | # Handle form data, JSON body, or a blank value 27 | def ensure_hash(ambiguous_param) 28 | case ambiguous_param 29 | when String 30 | ambiguous_param.present? ? ensure_hash(JSON.parse(ambiguous_param)) : {} 31 | when Hash, ActionController::Parameters 32 | ambiguous_param 33 | when nil 34 | {} 35 | else 36 | raise ArgumentError, "Unexpected parameter: #{ambiguous_param}" 37 | end 38 | end 39 | 40 | def handle_error_in_development(e) 41 | logger.error e.message 42 | logger.error e.backtrace.join("\n") 43 | 44 | render json: { 45 | error: { message: e.message, backtrace: e.backtrace }, data: {} 46 | }, 47 | status: 500 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/javascript/src/modules/common/ConfirmModal.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 3 | import {Button} from '@material-ui/core'; 4 | import Typography from '@material-ui/core/Typography'; 5 | import WarningIcon from '@material-ui/icons/WarningRounded'; 6 | 7 | const useStyles = makeStyles((theme: Theme) => 8 | createStyles({ 9 | root: { 10 | display: 'flex', 11 | width: '100%', 12 | alignItems: 'center', 13 | backgroundColor: 'rgba(248,231,28,0.20)', 14 | border: '1px solid #D8D8D8', 15 | borderRadius: '3px', 16 | marginTop: '12px', 17 | padding: '12px 20px', 18 | }, 19 | }) 20 | ); 21 | 22 | interface ConfirmModalProps { 23 | confirmMessage: string; 24 | confirmAction: any; 25 | denyAction: any; 26 | } 27 | 28 | export default function ConfirmModal(props: ConfirmModalProps) { 29 | const classes = useStyles(); 30 | const {confirmMessage, confirmAction, denyAction} = props; 31 | 32 | return ( 33 |
34 | 35 | 36 | {confirmMessage} 37 | 38 | 41 | 44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/SignUpUser.ts: -------------------------------------------------------------------------------- 1 | import {createMutationHook, gql} from '../graphqlHelpers'; 2 | import {SignUpUser, SignUpUserVariables} from './__generated__/SignUpUser'; 3 | 4 | export const useSignUpUser = createMutationHook< 5 | SignUpUser, 6 | SignUpUserVariables 7 | >(gql` 8 | mutation SignUpUser( 9 | $fullName: String! 10 | $email: String! 11 | $password: String! 12 | $companyName: String! 13 | $socialLogin: Boolean! 14 | $provider: String! 15 | $tokenID: String! 16 | $uuID: String! 17 | ) { 18 | signUpUser( 19 | fullName: $fullName 20 | email: $email 21 | password: $password 22 | companyName: $companyName 23 | socialLogin: $socialLogin 24 | provider: $provider 25 | tokenID: $tokenID 26 | uuID: $uuID 27 | ) { 28 | user { 29 | id 30 | email 31 | fullName 32 | displayName 33 | profileUrl 34 | lastViewedCompanyId 35 | ownedCompanies { 36 | id 37 | name 38 | } 39 | companies { 40 | id 41 | name 42 | logoUrl 43 | } 44 | teams { 45 | id 46 | companyId 47 | name 48 | } 49 | roles { 50 | id 51 | name 52 | companyId 53 | } 54 | } 55 | errors { 56 | path 57 | message 58 | } 59 | success 60 | } 61 | } 62 | `); 63 | -------------------------------------------------------------------------------- /app/javascript/src/graphql/mutations/__generated__/SignInUser.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: SignInUser 7 | // ==================================================== 8 | 9 | export interface SignInUser_signInUser_currentUser_user { 10 | __typename: "User"; 11 | id: string; 12 | fullName: string; 13 | email: string; 14 | } 15 | 16 | export interface SignInUser_signInUser_currentUser { 17 | __typename: "CurrentUser"; 18 | id: string; 19 | user: SignInUser_signInUser_currentUser_user | null; 20 | } 21 | 22 | export interface SignInUser_signInUser_errors { 23 | __typename: "FormError"; 24 | /** 25 | * Which field this error came from 26 | */ 27 | path: string | null; 28 | /** 29 | * A description of the error 30 | */ 31 | message: string; 32 | } 33 | 34 | export interface SignInUser_signInUser { 35 | __typename: "SignInUserPayload"; 36 | currentUser: SignInUser_signInUser_currentUser | null; 37 | errors: SignInUser_signInUser_errors[]; 38 | success: boolean; 39 | } 40 | 41 | export interface SignInUser { 42 | signInUser: SignInUser_signInUser | null; 43 | } 44 | 45 | export interface SignInUserVariables { 46 | email: string; 47 | password: string; 48 | socialLogin: boolean; 49 | provider: string; 50 | tokenID: string; 51 | uuID: string; 52 | } 53 | -------------------------------------------------------------------------------- /spec/graphql/mutations/create_team_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | module Mutations 3 | RSpec.describe CreateTeam, type: :request do 4 | describe 'resolve' do 5 | it 'Create new Team' do 6 | company = create(:company) 7 | team = create(:team) 8 | expect do 9 | res = 10 | post '/graphql', 11 | params: { query: query_create_company(name: company.name) } 12 | res = 13 | post '/graphql', 14 | params: { 15 | query: query(company_id: company.id, name: team.name) 16 | } 17 | to change { Team.count }.by(1) 18 | it { expect Team.first.name.eql? team.name } 19 | it { expect Team.first.company_id.eql? company.id } 20 | end 21 | end 22 | end 23 | 24 | def query(company_id:, name:) 25 | <<~GQL 26 | mutation{ 27 | CreateTeam( 28 | name: #{name}, 29 | company_id: #{ 30 | company_id 31 | } 32 | ) { 33 | errors { 34 | path 35 | message 36 | } 37 | success 38 | } 39 | } 40 | GQL 41 | end 42 | 43 | def query_create_company(name:) 44 | <<~GQL 45 | mutation{ 46 | CreateCompany( 47 | name: #{ 48 | name 49 | }, 50 | ) { 51 | errors { 52 | path 53 | message 54 | } 55 | success 56 | } 57 | } 58 | GQL 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /app/javascript/src/modules/team/components/TableHeader/TableHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Theme, makeStyles, createStyles} from '@material-ui/core/styles'; 3 | import TableHead from '@material-ui/core/TableHead'; 4 | 5 | import StyledTableRow from '../styled/StyledTableRow'; 6 | import StyledTableCell from '../styled/StyledTableCell'; 7 | 8 | type roleType = 'Member' | 'Admin'; 9 | 10 | interface Company { 11 | url: string; 12 | label: string; 13 | } 14 | 15 | interface Data { 16 | name: string; 17 | companies: Company[]; 18 | teams: string[]; 19 | role: roleType; 20 | } 21 | 22 | interface HeadRows { 23 | id: keyof Data; 24 | label: string; 25 | } 26 | 27 | const headRows: HeadRows[] = [ 28 | {id: 'name', label: 'Name'}, 29 | {id: 'companies', label: 'Company(s)'}, 30 | {id: 'teams', label: 'Team(s)'}, 31 | {id: 'role', label: 'Role'}, 32 | ]; 33 | 34 | const useStyles = makeStyles((theme: Theme) => 35 | createStyles({ 36 | root: { 37 | '& th:first-child': { 38 | paddingLeft: '31px', 39 | }, 40 | }, 41 | }) 42 | ); 43 | 44 | export default function TableHeader() { 45 | const classes = useStyles(); 46 | return ( 47 | 48 | 49 | {headRows.map(row => ( 50 | 51 | {row.label} 52 | 53 | ))} 54 | 55 | 56 | ); 57 | } 58 | --------------------------------------------------------------------------------