├── log └── .keep ├── tmp └── .keep ├── vendor └── .keep ├── .nvmrc ├── lib ├── assets │ └── .keep ├── tasks │ ├── .keep │ ├── cached_templates.rake │ ├── challenge.rake │ └── firestore_sync.rake └── mailer_previews │ ├── charges_limit_mailer_preview.rb │ ├── trial_expired_mailer_preview.rb │ ├── billing_overdue_mailer_preview.rb │ ├── trial_users_count_exceeded_mailer_preview.rb │ ├── billing_changes_mailer_preview.rb │ ├── test_collection_mailer_preview.rb │ ├── invoice_payment_failed_mailer_preview.rb │ ├── trial_ending_soon_mailer_preview.rb │ └── payment_method_expiring_mailer_preview.rb ├── test ├── helpers │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ └── user_test.rb ├── system │ └── .keep ├── controllers │ └── .keep ├── fixtures │ ├── .keep │ ├── files │ │ └── .keep │ └── users.yml ├── integration │ └── .keep └── test_helper.rb ├── .ruby-gemset ├── .ruby-version ├── app ├── assets │ ├── images │ │ ├── .keep │ │ ├── logo_2x.png │ │ ├── logo-no-text_1x.png │ │ ├── logo-no-text_2x.png │ │ ├── move_helper_diagram.png │ │ └── favicons │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ └── apple-touch-icon.png │ ├── stylesheets │ │ ├── application.scss │ │ └── global.scss │ ├── fonts │ │ ├── Gotham-Bold.otf │ │ ├── Gotham-Book.otf │ │ ├── Gotham-Light.otf │ │ ├── Gotham-Medium.otf │ │ ├── Sentinel-Book.otf │ │ ├── Sentinel-Light.otf │ │ ├── Sentinel-Medium.otf │ │ ├── Sentinel-BoldItalic.otf │ │ ├── Sentinel-BookItalic.otf │ │ ├── Sentinel-MediumItal.otf │ │ └── Sentinel-MediumItalic.otf │ └── config │ │ └── manifest.js ├── models │ ├── concerns │ │ ├── .keep │ │ └── has_activities.rb │ ├── global_translation.rb │ ├── app_metric.rb │ ├── groups_thread.rb │ └── application_record.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── api │ │ └── v1 │ │ │ ├── admin │ │ │ └── base_controller.rb │ │ │ ├── sessions_controller.rb │ │ │ └── filestack_controller.rb │ ├── admin │ │ └── dashboard_controller.rb │ ├── callbacks │ │ └── slack_controller.rb │ ├── users │ │ └── login_redirect_controller.rb │ ├── urls_controller.rb │ └── invitations_controller.rb ├── javascript │ ├── ui │ │ ├── reporting │ │ │ └── ButtonContainer.js │ │ ├── icons │ │ │ ├── ResizeIcon.js │ │ │ ├── DropdownIcon.js │ │ │ ├── TestGraphIcon.js │ │ │ ├── AddTextIcon.js │ │ │ ├── ChevronLeftIcon.js │ │ │ ├── ChevronRightIcon.js │ │ │ ├── GoIcon.js │ │ │ ├── MoreIcon.js │ │ │ ├── CheckIcon.js │ │ │ ├── SingleCrossIcon.js │ │ │ ├── htc │ │ │ │ └── TextIcon.js │ │ │ └── OkIcon.js │ │ ├── groups │ │ │ └── styles.js │ │ ├── pages │ │ │ └── AdminPage.js │ │ ├── global │ │ │ ├── Link.js │ │ │ ├── TextButton.md │ │ │ ├── Avatar.md │ │ │ ├── modals │ │ │ │ └── AddCardModal.js │ │ │ ├── PlainLink.js │ │ │ ├── Moment.js │ │ │ ├── ColorSquare.js │ │ │ └── HoverableDescriptionIcon.md │ │ ├── grid │ │ │ └── covers │ │ │ │ └── PaddedCardCover.js │ │ ├── billing │ │ │ └── EmptyList.js │ │ └── test_collections │ │ │ ├── GreetingMessage.js │ │ │ └── PaperAirplane.js │ ├── utils │ │ ├── __mocks__ │ │ │ ├── sleep.js │ │ │ ├── IdeoSSO.js │ │ │ └── ChannelManager.js │ │ ├── sleep.js │ │ ├── objectUtils.js │ │ ├── googleAnalytics │ │ │ └── __mocks__ │ │ │ │ └── utmUtils.js │ │ ├── isEmail.js │ │ ├── ActionCableConsumer.js │ │ ├── IdeoSSO.js │ │ ├── scrolling.js │ │ ├── paramUtils.js │ │ └── time.js │ ├── assets │ │ ├── bct_icons.png │ │ ├── emoji │ │ │ ├── error.png │ │ │ ├── eyeglasses.png │ │ │ ├── 129398-56694.png │ │ │ ├── dark-sunglasses.png │ │ │ ├── woman-shrugging.png │ │ │ ├── exploding-head_1f92f.png │ │ │ └── slightly-smiling-face.png │ │ ├── Powered-by-IDEO.png │ │ ├── create_new_org.png │ │ ├── paper-airplane.png │ │ ├── ask_for_invitation.png │ │ ├── organic_grid_black.png │ │ ├── organic_grid_pill_gray.png │ │ └── Powered-by-IDEO-Inverted.png │ ├── stores │ │ ├── jsonApi │ │ │ ├── __mocks__ │ │ │ │ └── Collection.js │ │ │ ├── FilestackFile.js │ │ │ ├── QuestionChoice.js │ │ │ ├── SharedRecordMixin.js │ │ │ ├── DataItemsDataset.js │ │ │ ├── Tag.js │ │ │ └── UsersThread.js │ │ └── __mocks__ │ │ │ └── index.js │ ├── packs │ │ └── babel-polyfill.js │ ├── enums │ │ └── actionEnums.js │ └── vendor │ │ └── cypress.js ├── jobs │ └── application_job.rb ├── lib │ ├── mashie.rb │ └── url_exists.rb ├── deserializers │ ├── deserializable_tag.rb │ ├── deserializable_user.rb │ ├── deserializable_group.rb │ ├── deserializable_item.rb │ ├── deserializable_role.rb │ ├── deserializable_activity.rb │ ├── deserializable_audience.rb │ ├── deserializable_comment.rb │ ├── deserializable_dataset.rb │ ├── deserializable_notification.rb │ ├── deserializable_organization.rb │ ├── deserializable_test_audience.rb │ ├── deserializable_comment_thread.rb │ ├── deserializable_filestack_file.rb │ ├── deserializable_question_answer.rb │ ├── deserializable_question_choice.rb │ ├── deserializable_survey_response.rb │ ├── deserializable_collection_filter.rb │ ├── deserializable_data_items_dataset.rb │ ├── deserializable_collection.rb │ ├── deserializable_collection_card.rb │ └── base_json_deserializer.rb ├── views │ ├── home │ │ ├── index.html.erb │ │ ├── marketing.html.erb │ │ ├── login.html.erb │ │ └── sign_up.html.erb │ ├── admin │ │ └── dashboard │ │ │ └── index.html.erb │ ├── deprovision_user_mailer │ │ ├── missing_org_admin.text.erb │ │ ├── missing_group_admin.text.erb │ │ └── missing_collection_editor.text.erb │ ├── test_collection_mailer │ │ └── notify_closed.text.erb │ ├── invitation_mailer │ │ └── invite.text.erb │ ├── charges_limit_mailer │ │ ├── notify.text.erb │ │ └── notify.html.erb │ ├── trial_ending_soon_mailer │ │ └── notify.text.erb │ └── layouts │ │ ├── _trackers_and_widgets_body.html.erb │ │ ├── mailer.text.erb │ │ └── _metatags_and_favicons.html.erb ├── services │ ├── simple_service.rb │ ├── slack │ │ └── common.rb │ ├── search │ │ └── filters │ │ │ ├── type_item.rb │ │ │ ├── type_collection.rb │ │ │ ├── record_id.rb │ │ │ └── base.rb │ ├── mailer_helper │ │ └── router.rb │ ├── card_identifier.rb │ ├── quill_schmoozer.rb │ ├── text_item_highlighter.rb │ └── firestore_client.rb ├── serializers │ ├── serializable_tag.rb │ ├── serializable_submissions_collection.rb │ ├── serializable_data_items_datasets.rb │ ├── serializable_filestack_file.rb │ ├── serializable_data_item.rb │ ├── serializable_admin_test_collection.rb │ ├── serializable_comment_thread.rb │ ├── serializable_question_choice.rb │ ├── serializable_survey_response.rb │ ├── concerns │ │ └── serialized_external_id.rb │ ├── serializable_legend_item.rb │ ├── serializable_collection_filter.rb │ ├── serializable_question_answer.rb │ ├── serializable_simple_user.rb │ ├── serializable_activity.rb │ ├── serializable_simple_group.rb │ └── serializable_users_thread.rb ├── workers │ ├── organization_shell_worker.rb │ ├── record_paid_survey_response_worker.rb │ ├── network_user_update_worker.rb │ ├── notification_mention_mailer_worker.rb │ ├── daily_organization_usage_worker.rb │ ├── survey_response_completion_worker.rb │ ├── collection_grid │ │ └── board_migrator_worker.rb │ ├── organization_shell_refresh_worker.rb │ ├── overdue_notification_worker.rb │ ├── organization_membership_worker.rb │ ├── trial_expired_worker.rb │ ├── notification_mailer_worker.rb │ ├── mark_as_overdue_worker.rb │ ├── create_activity_notifications_worker.rb │ └── mailing_list_subscription_worker.rb ├── interactors │ └── record_organization_usage.rb ├── mailers │ └── charges_limit_mailer.rb └── channels │ └── application_cable │ └── channel.rb ├── .node-version ├── script ├── pre-commit ├── review-app-teardown └── dev-setup ├── .postcssrc.yml ├── config ├── initializers │ ├── time_formats.rb │ ├── searchkick.rb │ ├── money.rb │ ├── slack.rb │ ├── active_record_query_trace.rb │ ├── webmock.rb │ ├── twilio.rb │ ├── mime_types.rb │ ├── locale.rb │ ├── filter_parameter_logging.rb │ ├── network_api.rb │ ├── application_controller_renderer.rb │ ├── cookies_serializer.rb │ ├── sidekiq.rb │ ├── rolify.rb │ ├── backtrace_silencers.rb │ └── rack_cors.rb ├── crystalball.yml ├── webpack │ ├── test.js │ ├── production.js │ └── development.js ├── environment.rb ├── boot.rb ├── appsignal.yml ├── cable.yml └── spring.rb ├── public ├── favicon.ico └── robots.txt ├── .rspec ├── spec ├── factories │ ├── app_metrics.rb │ ├── roles.rb │ ├── question_answer_choices.rb │ ├── user_collection_filters.rb │ ├── users_role.rb │ ├── users_threads.rb │ ├── groups_threads.rb │ ├── tags.rb │ ├── user_tags.rb │ ├── notifications.rb │ ├── audience_organizations.rb │ ├── question_choices.rb │ ├── application_organizations.rb │ ├── global_translations.rb │ ├── test_audience_invitations.rb │ ├── group_hierarchies.rb │ ├── external_records.rb │ ├── collection_filters.rb │ ├── feedback_incentive_records.rb │ ├── activities.rb │ ├── api_tokens.rb │ ├── network_invitations.rb │ ├── applications.rb │ └── payments.rb ├── models │ ├── app_metric_spec.rb │ ├── data_items_datasets_spec.rb │ ├── global_translation_spec.rb │ ├── network_invitation_spec.rb │ ├── item │ │ ├── link_item_spec.rb │ │ └── video_item_spec.rb │ ├── groups_thread_spec.rb │ ├── external_record_spec.rb │ └── api_token_spec.rb ├── support │ ├── api │ │ └── schemas │ │ │ ├── comment_thread.json │ │ │ ├── role.json │ │ │ ├── tag.json │ │ │ ├── survey_response.json │ │ │ ├── simple_user.json │ │ │ ├── question_answer.json │ │ │ ├── test_audience.json │ │ │ └── notification.json │ └── api_schema_matcher.rb ├── workers │ ├── daily_organization_usage_worker_spec.rb │ ├── survey_response_completion_worker_spec.rb │ ├── searchkick_reindex_worker_spec.rb │ └── update_template_instances_worker_spec.rb └── services │ └── text_messenger_spec.rb ├── __mocks__ ├── mobx-localstorage.js └── ideo-sso.js ├── prettier.config.js ├── bin ├── cypress ├── heroku-web ├── rspec ├── rake ├── rails ├── yarn ├── webpack └── webpack-dev-server ├── config.ru ├── .gitmodules ├── db └── migrate │ ├── 20180222194905_add_url_to_items.rb │ ├── 20190917002145_add_parent_to_comment.rb │ ├── 20191001195956_add_name_to_dataset.rb │ ├── 20180223170513_add_text_data_to_item.rb │ ├── 20191014230812_add_style_to_legend_item.rb │ ├── 20180309222116_add_status_to_users.rb │ ├── 20180531180724_add_content_to_activities.rb │ ├── 20190213230015_add_report_type_to_items.rb │ ├── 20190801001606_add_tiers_to_dataset.rb │ ├── 20190814210208_add_created_by_to_group.rb │ ├── 20180228212023_add_thumbnail_url_to_items.rb │ ├── 20180905223105_add_question_type_to_items.rb │ ├── 20181114015125_add_data_settings_to_items.rb │ ├── 20190522234643_add_description_to_datasets.rb │ ├── 20191007233341_add_status_to_comment.rb │ ├── 20191031165716_add_question_type_index_to_items.rb │ ├── 20180604200903_add_draftjs_data_to_comments.rb │ ├── 20180809214501_add_show_move_helper.rb │ ├── 20181204090132_add_created_at_index_to_activities.rb │ ├── 20190523213147_add_groupings_to_dataset.rb │ ├── 20190814000051_add_application_to_dataset.rb │ ├── 20191018203725_add_edited_to_comment.rb │ ├── 20180404234941_add_created_by_to_collection.rb │ ├── 20180511004129_add_show_helper_to_users.rb │ ├── 20180518214410_add_cached_attributes_to_items.rb │ ├── 20180626000452_add_admins_to_organization.rb │ ├── 20180709231601_add_template_id_to_collections.rb │ ├── 20180711224359_add_cached_attributes_to_users.rb │ ├── 20180717214927_add_docinfo_to_filestack_files.rb │ ├── 20190312204433_add_legend_item_id_to_data_item.rb │ ├── 20190524214536_add_type_to_groups.rb │ ├── 20191206221958_add_search_term_to_collection.rb │ ├── 20200206224344_add_num_columns_to_collections.rb │ ├── 20200417163307_add_last_broadcast_at_to_items.rb │ ├── 20180921212427_add_processing_to_collections.rb │ ├── 20181002172736_add_mailing_list_to_users.rb │ ├── 20181005173335_add_data_source_to_chart_item.rb │ ├── 20181009204421_add_last_active_at_column_to_user.rb │ ├── 20190402225253_add_index_to_coll_card_rows_cols.rb │ ├── 20190418233316_add_launched_at_to_collection.rb │ ├── 20191024211521_add_organization_id_to_tags.rb │ ├── 20200125010741_add_blank_to_organization.rb │ ├── 20180209192525_add_current_organization_to_user.rb │ ├── 20180416195913_add_terms_accepted_to_users.rb │ ├── 20181009212428_add_trial_ends_at_to_organization.rb │ ├── 20190319185820_add_cover_type_to_collections.rb │ ├── 20190514224531_add_test_closed_at_to_collection.rb │ ├── 20190523220518_remove_organization_id_from_audiences.rb │ ├── 20190528210255_rename_name_to_identifier_for_dataset.rb │ ├── 20190604040111_add_shape_circle_to_users.rb │ ├── 20190718204608_add_default_group_to_collection.rb │ ├── 20200731212721_add_billable_to_organzation.rb │ ├── 20180308233902_add_pic_url_square_to_organization.rb │ ├── 20180413042009_add_shared_collection_to_group.rb │ ├── 20180604225632_add_handle_to_users.rb │ ├── 20180817154020_add_template_helper_to_users.rb │ ├── 20180927190104_add_status_to_survey_responses.rb │ ├── 20181212191943_add_more_data_indexing.rb │ ├── 20190729171359_add_terms_version_to_organizations.rb │ ├── 20191028205029_add_section_type_to_collection_card.rb │ ├── 20180406184838_add_current_user_collection_to_user.rb │ ├── 20180422165218_add_cached_attributes_to_collections.rb │ ├── 20180618224154_add_email_notifications_to_user.rb │ ├── 20180727160754_add_last_notification_sent_to_users.rb │ ├── 20180810231839_add_icon_url_to_items.rb │ ├── 20180822181719_add_submissions_collection_to_collections.rb │ ├── 20181015214646_add_is_cover_to_collection_card.rb │ ├── 20190107220606_add_hidden_to_collection_cards.rb │ ├── 20190110222910_add_custom_terms_id_to_organization.rb │ ├── 20190122183523_add_subscribed_to_users_thread.rb │ ├── 20190220003415_rename_text_data_to_data_content_on_items.rb │ ├── 20190424210415_add_should_recontact_to_users.rb │ ├── 20190430234650_add_joinable_group_id_to_collections.rb │ ├── 20190514000935_add_test_audience_id_to_survey_responses.rb │ ├── 20190703185758_add_network_id_to_groups.rb │ ├── 20201110210451_add_section_name_to_collection_cards.rb │ ├── 20180627183820_add_profile_template_to_organization.rb │ ├── 20190924174615_add_children_counter_cache_to_comment.rb │ ├── 20191106185833_add_idea_to_collections.rb │ ├── 20191217233558_add_collection_type_to_collections.rb │ ├── 20200226214443_add_user_settings_data_to_user.rb │ ├── 20200626174631_add_parent_snapshot_to_collection_cards.rb │ ├── 20180702204405_add_profile_template_id_to_organization.rb │ ├── 20180815173801_add_master_template_to_collections.rb │ ├── 20190108234025_add_hide_submissions_to_collection.rb │ ├── 20190425182045_add_anyone_can_view_to_collections.rb │ ├── 20190430210655_add_anyone_can_join_to_collections.rb │ ├── 20191101212918_add_test_show_media_to_collections.rb │ ├── 20191106190741_add_respondent_alias_to_survey_response.rb │ ├── 20180703192353_add_profile_collection_id_to_organizations.rb │ ├── 20180924221647_add_network_subscription_to_organization.rb │ ├── 20180928225725_add_test_collection_id_to_collections.rb │ ├── 20181015181308_add_getting_started_id_to_collection.rb │ ├── 20181106065121_add_type_indices.rb │ ├── 20190215192116_add_replaceable_to_collection_cards.rb │ ├── 20190513210422_add_feedback_terms_accepted_to_users.rb │ ├── 20201002180033_add_cached_attributes_to_organizations.rb │ ├── 20201016211257_add_cover_card_id_to_collection_cards.rb │ ├── 20180329211218_add_invitation_token_to_users.rb │ ├── 20180828211535_add_image_contain_to_collection_cards.rb │ ├── 20190220192800_add_shared_with_org_to_collections.rb │ ├── 20190422230949_add_submissions_enabled_to_collection.rb │ ├── 20190529222849_add_respondent_terms_accepted_to_user.rb │ ├── 20191119202037_change_price_per_response_to_min_price.rb │ ├── 20200728220748_add_is_background_to_collection_cards.rb │ ├── 20180131212625_create_organizations.rb │ ├── 20180917173119_add_test_status_to_collections.rb │ ├── 20181009212705_add_trial_user_count_to_organization.rb │ ├── 20181106211944_add_deactivated_flag_to_organization.rb │ ├── 20190726063501_add_activities_indexes.rb │ ├── 20191112190014_add_survey_responses_id_to_test_results_collection.rb │ ├── 20200624153202_add_icon_to_collections.rb │ ├── 20201016210636_add_cached_attributes_to_collection_cards.rb │ ├── 20181009172408_add_active_users_count_to_organization.rb │ ├── 20181016214104_add_in_app_billing_flag_to_organization.rb │ ├── 20190103230243_add_filter_to_collection_card.rb │ ├── 20190513232903_add_price_per_response_to_test_audiences.rb │ ├── 20191015192616_add_application_to_groups.rb │ ├── 20200324231908_add_subgroups_to_group.rb │ ├── 20180320003745_add_resource_identifier_to_roles.rb │ ├── 20180410000605_add_archive_field_to_groups.rb │ ├── 20180524202508_add_organization_id_to_activity.rb │ ├── 20190327205722_add_row_col_to_collection_cards.rb │ ├── 20191120180804_add_incentive_paid_to_survey_response.rb │ ├── 20180529175444_add_combined_activities_to_notifications.rb │ ├── 20200514232956_add_start_end_date_to_collection.rb │ ├── 20180830052131_add_source_to_activities.rb │ ├── 20190110221600_add_autojoin_emails_to_groups.rb │ ├── 20190528222323_add_status_to_test_audience.rb │ ├── 20191014212029_add_anyone_can_view_to_datasets.rb │ ├── 20191031202704_add_selected_choice_ids_to_question_answers.rb │ ├── 20181025181109_add_trail_expired_email_sent_to_organization.rb │ ├── 20201021200558_add_background_color_to_items.rb │ ├── 20180710204923_change_templated_from_to_id.rb │ ├── 20180912174042_add_session_uid_to_survey_responses.rb │ ├── 20190522210918_add_payment_id_to_test_audiences.rb │ ├── 20190904204944_add_locale_to_user_and_organization.rb │ ├── 20191107222348_add_identifier_to_collection_cards.rb │ ├── 20190201202722_create_applications.rb │ ├── 20190805230103_create_global_translations.rb │ ├── 20171104063543_add_omniauth_to_users.rb │ ├── 20180831213535_create_survey_responses.rb │ ├── 20181101191730_add_cached_test_scores_to_collections.rb │ ├── 20190607215955_add_incentive_status_to_survey_response.rb │ ├── 20180928000522_add_open_response_item_and_question_item.rb │ ├── 20180703175717_add_pinned_templated_to_collection_cards.rb │ ├── 20181018175707_add_collection_to_test_to_test_collection.rb │ ├── 20190104004056_add_autojoin_whitelist_to_organizations.rb │ ├── 20191009212511_add_subjects_to_comments.rb │ ├── 20200124010234_add_font_properties_to_collection_cards.rb │ ├── 20181023184040_add_trial_users_count_exceeded_email_sent_to_organization.rb │ ├── 20181031164042_add_unarchived_at_fields.rb │ ├── 20190517171959_legend_search_source_data_items_dataset_index.rb │ ├── 20190523194711_create_audience_organizations.rb │ ├── 20190613230119_add_incentive_owed_paid_date_to_survey_response.rb │ ├── 20191030230157_add_archive_batch_index.rb │ ├── 20180522184536_create_activity_subjects.rb │ ├── 20180321233803_create_groups_role.rb │ ├── 20180522003321_create_activities.rb │ ├── 20191015211748_create_collection_filters.rb │ ├── 20180208012030_create_groups.rb │ ├── 20180522023119_create_notifications.rb │ ├── 20191120192557_drop_feedback_incentive_record.rb │ ├── 20181029205523_add_overdue_at_and_has_payment_method_to_organization.rb │ ├── 20190426224043_create_audiences.rb │ ├── 20200310183359_create_app_metrics.rb │ ├── 20180316164813_add_handle_to_group_and_organization.rb │ ├── 20191015212000_user_collection_filter.rb │ ├── 20180214211104_add_breadcrumb_to_objects.rb │ ├── 20190530193416_add_unique_index_to_question_answer.rb │ ├── 20190822221612_add_addtl_info_to_application.rb │ ├── 20180514204738_create_comment_threads.rb │ ├── 20180514204917_create_comments.rb │ ├── 20180221220511_create_filestack_files.rb │ ├── 20190426225125_create_test_audiences.rb │ ├── 20200123191847_add_index_to_items_cached_attributes.rb │ ├── 20180131216045_create_collections.rb │ ├── 20200520210612_add_challenge_group_ids_to_collections.rb │ ├── 20180202214858_create_items.rb │ ├── 20200729224906_add_collection_style_settings.rb │ ├── 20191030212834_create_question_choices.rb │ ├── 20190227194651_add_phone_number_and_null_email_to_users.rb │ ├── 20180302185830_add_archive_fields_to_cards_and_collections.rb │ ├── 20180831213853_create_question_answers.rb │ ├── 20190702002222_update_comment_thread_index.rb │ ├── 20190815222240_change_last_active_at_to_jsonb.rb │ ├── 20200616222835_create_user_tags.rb │ ├── 20180202214843_create_collection_cards.rb │ ├── 20180329014550_users_roles_unique_index.rb │ ├── 20190520233054_add_default_value_to_audiences_price_per_response.rb │ ├── 20190109231008_add_roles_anchor_collection_id.rb │ ├── 20180821185430_add_submission_box_template_id_to_collections.rb │ ├── 20180712013914_add_archived_info_columns.rb │ ├── 20190925184346_create_group_hierarchies.rb │ ├── 20200401042139_remove_uniqueness_from_handles.rb │ ├── 20190513232516_change_price_per_response_for_audiences.rb │ ├── 20200302212854_create_network_invitations.rb │ ├── 20180523225540_create_groups_threads.rb │ ├── 20190520215105_create_test_audience_invitations.rb │ ├── 20190117195055_add_defaults_and_indexes_to_cached_attributes.rb │ ├── 20190510215957_create_datasets.rb │ ├── 20180426163602_add_guest_group_id_and_domains_to_organizations.rb │ ├── 20181105224054_add_charges_limit_flags_to_organization.rb │ ├── 20190606222127_create_payments.rb │ ├── 20190508180048_create_feedback_incentive_records.rb │ ├── 20200608220051_add_type_to_audiences.rb │ ├── 20190201191608_create_api_tokens.rb │ ├── 20190729171554_update_user_terms_accepted.rb │ ├── 20180329055510_add_type_and_remove_reference_from_collection_cards.rb │ ├── 20180207202630_rolify_create_roles.rb │ ├── 20190207083622_create_external_records.rb │ ├── 20180319231721_add_filestack_file_to_groups_and_orgs.rb │ ├── 20190513203005_create_data_items_datasets.rb │ └── 20190201202932_create_application_organizations.rb ├── docker-compose.yml ├── Procfile ├── .eslintignore ├── __js_test_config ├── helpers │ ├── expectTreeToMatchSnapshot.js │ └── expectToMatchSnapshot.js └── mocks │ ├── fakeUndoStore.js │ └── fakeActionCableConsumer.js ├── cypress └── integration │ ├── MarketingPage.feature │ ├── MyCollection.feature │ ├── items │ ├── LinkItem.feature │ └── VideoItem.feature │ ├── collections │ └── CreateTemplate.feature │ └── admin │ └── NewAudienceQuery.feature ├── Rakefile ├── Procfile.development ├── cypress.json └── __tests__ └── ui └── grid ├── FoamcoreZoomControls.unit.test.js └── covers └── CollectionCoverTitle.unit.test.js /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 10.15 2 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | shape 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.5.5 2 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 10.15.3 2 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/javascript/ui/reporting/ButtonContainer.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /script/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | script/format-staged 4 | -------------------------------------------------------------------------------- /.postcssrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | postcss-import: {} 3 | postcss-cssnext: {} 4 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /config/initializers/time_formats.rb: -------------------------------------------------------------------------------- 1 | Time::DATE_FORMATS[:mdy] = '%m/%d/%y' 2 | -------------------------------------------------------------------------------- /app/lib/mashie.rb: -------------------------------------------------------------------------------- 1 | class Mashie < Hashie::Mash 2 | disable_warnings 3 | end 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --order rand 3 | --require spec_helper 4 | --format documentation 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | /* 2 | *= require global 3 | *= require_self 4 | */ 5 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_tag.rb: -------------------------------------------------------------------------------- 1 | class DeserializableTag < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_user.rb: -------------------------------------------------------------------------------- 1 | class DeserializableUser < BaseJsonSerializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_group.rb: -------------------------------------------------------------------------------- 1 | class DeserializableGroup < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_item.rb: -------------------------------------------------------------------------------- 1 | class DeserializableItem < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_role.rb: -------------------------------------------------------------------------------- 1 | class DeserializableRole < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/javascript/utils/__mocks__/sleep.js: -------------------------------------------------------------------------------- 1 | export default jest.fn().mockReturnValue(Promise.resolve()) 2 | -------------------------------------------------------------------------------- /config/crystalball.yml: -------------------------------------------------------------------------------- 1 | --- 2 | map_expiration_period: 604800 # 1 week 3 | diff_from: origin/master 4 | -------------------------------------------------------------------------------- /app/assets/images/logo_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/images/logo_2x.png -------------------------------------------------------------------------------- /app/deserializers/deserializable_activity.rb: -------------------------------------------------------------------------------- 1 | class DeserializableActivity < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_audience.rb: -------------------------------------------------------------------------------- 1 | class DeserializableAudience < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_comment.rb: -------------------------------------------------------------------------------- 1 | class DeserializableComment < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_dataset.rb: -------------------------------------------------------------------------------- 1 | class DeserializableDataset < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/assets/fonts/Gotham-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Gotham-Bold.otf -------------------------------------------------------------------------------- /app/assets/fonts/Gotham-Book.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Gotham-Book.otf -------------------------------------------------------------------------------- /app/assets/fonts/Gotham-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Gotham-Light.otf -------------------------------------------------------------------------------- /config/initializers/searchkick.rb: -------------------------------------------------------------------------------- 1 | Searchkick.redis = ConnectionPool.new { Redis.new(url: ENV['REDIS_URL']) } 2 | -------------------------------------------------------------------------------- /spec/factories/app_metrics.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :app_metric do 3 | 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/assets/fonts/Gotham-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Gotham-Medium.otf -------------------------------------------------------------------------------- /app/assets/fonts/Sentinel-Book.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Sentinel-Book.otf -------------------------------------------------------------------------------- /app/assets/fonts/Sentinel-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Sentinel-Light.otf -------------------------------------------------------------------------------- /app/assets/fonts/Sentinel-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Sentinel-Medium.otf -------------------------------------------------------------------------------- /app/deserializers/deserializable_notification.rb: -------------------------------------------------------------------------------- 1 | class DeserializableNotification < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_organization.rb: -------------------------------------------------------------------------------- 1 | class DeserializableOrganization < BaseJsonSerializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_test_audience.rb: -------------------------------------------------------------------------------- 1 | class DeserializableTestAudience < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/javascript/assets/bct_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/bct_icons.png -------------------------------------------------------------------------------- /app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | <%= javascript_packs_with_polyfill 'app' %> 4 | -------------------------------------------------------------------------------- /config/initializers/money.rb: -------------------------------------------------------------------------------- 1 | # see https://github.com/RubyMoney/money#deprecation 2 | Money.locale_backend = :i18n 3 | -------------------------------------------------------------------------------- /config/initializers/slack.rb: -------------------------------------------------------------------------------- 1 | Slack.configure do |config| 2 | config.token = ENV['SLACK_BOT_API_TOKEN'] 3 | end 4 | -------------------------------------------------------------------------------- /app/assets/images/logo-no-text_1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/images/logo-no-text_1x.png -------------------------------------------------------------------------------- /app/assets/images/logo-no-text_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/images/logo-no-text_2x.png -------------------------------------------------------------------------------- /app/deserializers/deserializable_comment_thread.rb: -------------------------------------------------------------------------------- 1 | class DeserializableCommentThread < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_filestack_file.rb: -------------------------------------------------------------------------------- 1 | class DeserializableFilestackFile < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_question_answer.rb: -------------------------------------------------------------------------------- 1 | class DeserializableQuestionAnswer < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_question_choice.rb: -------------------------------------------------------------------------------- 1 | class DeserializableQuestionChoice < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_survey_response.rb: -------------------------------------------------------------------------------- 1 | class DeserializableSurveyResponse < BaseJsonSerializer 2 | end 3 | -------------------------------------------------------------------------------- /app/javascript/assets/emoji/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/emoji/error.png -------------------------------------------------------------------------------- /app/services/simple_service.rb: -------------------------------------------------------------------------------- 1 | class SimpleService 2 | def self.call(*args) 3 | new(*args).call 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /spec/factories/roles.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :role do 3 | name { Faker::App.name } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/assets/fonts/Sentinel-BoldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Sentinel-BoldItalic.otf -------------------------------------------------------------------------------- /app/assets/fonts/Sentinel-BookItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Sentinel-BookItalic.otf -------------------------------------------------------------------------------- /app/assets/fonts/Sentinel-MediumItal.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Sentinel-MediumItal.otf -------------------------------------------------------------------------------- /app/assets/images/move_helper_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/images/move_helper_diagram.png -------------------------------------------------------------------------------- /app/deserializers/deserializable_collection_filter.rb: -------------------------------------------------------------------------------- 1 | class DeserializableCollectionFilter < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_data_items_dataset.rb: -------------------------------------------------------------------------------- 1 | class DeserializableDataItemsDataset < BaseJsonDeserializer 2 | end 3 | -------------------------------------------------------------------------------- /app/javascript/assets/Powered-by-IDEO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/Powered-by-IDEO.png -------------------------------------------------------------------------------- /app/javascript/assets/create_new_org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/create_new_org.png -------------------------------------------------------------------------------- /app/javascript/assets/paper-airplane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/paper-airplane.png -------------------------------------------------------------------------------- /app/views/admin/dashboard/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | <%= javascript_packs_with_polyfill 'admin' %> 4 | -------------------------------------------------------------------------------- /app/views/home/marketing.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | <%= javascript_packs_with_polyfill 'marketing' %> 4 | -------------------------------------------------------------------------------- /config/webpack/test.js: -------------------------------------------------------------------------------- 1 | const environment = require('./environment') 2 | 3 | module.exports = environment.toWebpackConfig() 4 | -------------------------------------------------------------------------------- /spec/factories/question_answer_choices.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :question_answer_choice do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/factories/user_collection_filters.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user_collection_filter do 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/factories/users_role.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :users_role do 3 | role 4 | user 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/assets/fonts/Sentinel-MediumItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/fonts/Sentinel-MediumItalic.otf -------------------------------------------------------------------------------- /app/javascript/assets/emoji/eyeglasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/emoji/eyeglasses.png -------------------------------------------------------------------------------- /app/javascript/utils/sleep.js: -------------------------------------------------------------------------------- 1 | const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) 2 | 3 | export default sleep 4 | -------------------------------------------------------------------------------- /config/webpack/production.js: -------------------------------------------------------------------------------- 1 | const environment = require('./environment') 2 | 3 | module.exports = environment.toWebpackConfig() 4 | -------------------------------------------------------------------------------- /__mocks__/mobx-localstorage.js: -------------------------------------------------------------------------------- 1 | const localStorage = jest.genMockFromModule('mobx-localstorage') 2 | 3 | export default localStorage 4 | -------------------------------------------------------------------------------- /app/assets/images/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/images/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /app/assets/images/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/images/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /app/javascript/assets/ask_for_invitation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/ask_for_invitation.png -------------------------------------------------------------------------------- /app/javascript/assets/emoji/129398-56694.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/emoji/129398-56694.png -------------------------------------------------------------------------------- /app/javascript/assets/organic_grid_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/organic_grid_black.png -------------------------------------------------------------------------------- /config/webpack/development.js: -------------------------------------------------------------------------------- 1 | const environment = require('./environment') 2 | 3 | module.exports = environment.toWebpackConfig() 4 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/assets/images/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /app/javascript/assets/emoji/dark-sunglasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/emoji/dark-sunglasses.png -------------------------------------------------------------------------------- /app/javascript/assets/emoji/woman-shrugging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/emoji/woman-shrugging.png -------------------------------------------------------------------------------- /app/javascript/assets/organic_grid_pill_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/organic_grid_pill_gray.png -------------------------------------------------------------------------------- /app/javascript/stores/jsonApi/__mocks__/Collection.js: -------------------------------------------------------------------------------- 1 | import { fakeCollection } from '#/mocks/data' 2 | 3 | export default fakeCollection 4 | -------------------------------------------------------------------------------- /config/initializers/active_record_query_trace.rb: -------------------------------------------------------------------------------- 1 | ActiveRecordQueryTrace.enabled = true if Rails.env.development? && ENV['AR_QUERY_TRACE'] 2 | -------------------------------------------------------------------------------- /config/initializers/webmock.rb: -------------------------------------------------------------------------------- 1 | if Rails.env.test? 2 | require 'webmock' 3 | WebMock.disable_net_connect!(allow_localhost: true) 4 | end 5 | -------------------------------------------------------------------------------- /spec/factories/users_threads.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :users_thread do 3 | user 4 | comment_thread 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /__mocks__/ideo-sso.js: -------------------------------------------------------------------------------- 1 | export default { 2 | baseApiUrl: '', 3 | profileUrl: '', 4 | init: jest.fn(), 5 | initFromEnv: jest.fn(), 6 | } 7 | -------------------------------------------------------------------------------- /app/javascript/assets/Powered-by-IDEO-Inverted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/Powered-by-IDEO-Inverted.png -------------------------------------------------------------------------------- /app/serializers/serializable_tag.rb: -------------------------------------------------------------------------------- 1 | class SerializableTag < BaseJsonSerializer 2 | type 'tags' 3 | attributes :name, :taggings_count 4 | end 5 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | singleQuote: true, 4 | trailingComma: 'es5', 5 | bracketSpacing: true, 6 | } 7 | -------------------------------------------------------------------------------- /app/javascript/assets/emoji/exploding-head_1f92f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/emoji/exploding-head_1f92f.png -------------------------------------------------------------------------------- /app/javascript/assets/emoji/slightly-smiling-face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ideo/shape/HEAD/app/javascript/assets/emoji/slightly-smiling-face.png -------------------------------------------------------------------------------- /spec/factories/groups_threads.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :groups_thread do 3 | group nil 4 | comment_thread nil 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/tags.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :tag, class: ActsAsTaggableOn::Tag do 3 | name { Faker::Company.bs } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/user_tags.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user_tag do 3 | user 4 | record { create(:collection) } 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /bin/cypress: -------------------------------------------------------------------------------- 1 | pkill -F tmp/pids/test.pid 2 | rake db:test:prepare 3 | CYPRESS=true bin/rails server -d -e test -p 3001 -P tmp/pids/test.pid 4 | yarn cypress 5 | -------------------------------------------------------------------------------- /spec/factories/notifications.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :notification do 3 | activity 4 | read false 5 | user 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/serializers/serializable_submissions_collection.rb: -------------------------------------------------------------------------------- 1 | class SerializableSubmissionsCollection < SerializableCollection 2 | attribute :sort_options 3 | end 4 | -------------------------------------------------------------------------------- /app/services/slack/common.rb: -------------------------------------------------------------------------------- 1 | module Slack 2 | module Common 3 | def client 4 | @client ||= Slack::Web::Client.new 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/factories/audience_organizations.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :audience_organization do 3 | audience 4 | organization 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/api/v1/admin/base_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Admin::BaseController < Api::V1::BaseController 2 | before_action :authorize_shape_admin! 3 | end 4 | -------------------------------------------------------------------------------- /spec/factories/question_choices.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :question_choice do 3 | text { Faker::TvShows::BreakingBad.character } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_collection.rb: -------------------------------------------------------------------------------- 1 | class DeserializableCollection < BaseJsonDeserializer 2 | has_one :organization 3 | has_many :collection_cards 4 | end 5 | -------------------------------------------------------------------------------- /app/javascript/packs/babel-polyfill.js: -------------------------------------------------------------------------------- 1 | import 'core-js/stable' 2 | import 'regenerator-runtime/runtime' 3 | import 'whatwg-fetch' 4 | import '~/utils/otherPolyfills' 5 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /spec/factories/application_organizations.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :application_organization do 3 | application 4 | organization 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/global_translations.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :global_translation do 3 | value { { 'CD-QUALITIES-PURPOSE': 'Purpose' } } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/test_audience_invitations.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :test_audience_invitation do 3 | test_audience nil 4 | user nil 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "app/javascript/ui/shared"] 2 | path = app/javascript/ui/shared 3 | url = git@github.com:ideo/network-react-components.git 4 | branch = shape 5 | -------------------------------------------------------------------------------- /config/initializers/twilio.rb: -------------------------------------------------------------------------------- 1 | Twilio.configure do |config| 2 | config.account_sid = ENV['TWILIO_ACCOUNT_SID'] 3 | config.auth_token = ENV['TWILIO_AUTH_TOKEN'] 4 | end 5 | -------------------------------------------------------------------------------- /db/migrate/20180222194905_add_url_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddUrlToItems < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :url, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.3' 2 | services: 3 | elasticsearch: 4 | image: docker.elastic.co/elasticsearch/elasticsearch:6.4.0 5 | ports: 6 | - "9200:9200" 7 | -------------------------------------------------------------------------------- /spec/factories/group_hierarchies.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :group_hierarchy do 3 | association :subgroup 4 | association :parent_group 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/models/app_metric_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe AppMetric, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/external_records.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :external_record do 3 | external_id nil 4 | externalizable nil 5 | application 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/workers/organization_shell_worker.rb: -------------------------------------------------------------------------------- 1 | class OrganizationShellWorker 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | OrganizationShellBuilder.new.save 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20190917002145_add_parent_to_comment.rb: -------------------------------------------------------------------------------- 1 | class AddParentToComment < ActiveRecord::Migration[5.2] 2 | def change 3 | add_reference :comments, :parent 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191001195956_add_name_to_dataset.rb: -------------------------------------------------------------------------------- 1 | class AddNameToDataset < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :datasets, :name, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/api/schemas/comment_thread.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "updated_at": { "type": "date" } 5 | }, 6 | "required": ["updated_at"] 7 | } 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/global.scss: -------------------------------------------------------------------------------- 1 | @import 'quill/quill'; 2 | @import 'react-viewer/dist/index'; 3 | 4 | @import '_core/reset'; 5 | @import '_core/base'; 6 | @import '_core/typography'; 7 | -------------------------------------------------------------------------------- /app/deserializers/deserializable_collection_card.rb: -------------------------------------------------------------------------------- 1 | class DeserializableCollectionCard < BaseJsonDeserializer 2 | has_one :parent 3 | has_one :item 4 | has_one :collection 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180223170513_add_text_data_to_item.rb: -------------------------------------------------------------------------------- 1 | class AddTextDataToItem < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :text_data, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191014230812_add_style_to_legend_item.rb: -------------------------------------------------------------------------------- 1 | class AddStyleToLegendItem < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :items, :style, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/data_items_datasets_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe DataItemsDataset, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/global_translation_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe GlobalTranslation, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/network_invitation_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe NetworkInvitation, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180309222116_add_status_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :status, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: ANYCABLE_REDIS_URL=$REDIS_URL REDIS=$REDIS_URL bin/heroku-web 2 | release: bundle exec rake db:migrate 3 | worker: bundle exec sidekiq -e ${RACK_ENV:-development} -C config/sidekiq.yml 4 | -------------------------------------------------------------------------------- /bin/heroku-web: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$ANYCABLE_DEPLOYMENT" == "true" ]; then 4 | bundle exec anycable --server-command="anycable-go" 5 | else 6 | bundle exec puma -C config/puma.rb 7 | fi 8 | -------------------------------------------------------------------------------- /db/migrate/20180531180724_add_content_to_activities.rb: -------------------------------------------------------------------------------- 1 | class AddContentToActivities < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :activities, :content, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190213230015_add_report_type_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddReportTypeToItems < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :report_type, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190801001606_add_tiers_to_dataset.rb: -------------------------------------------------------------------------------- 1 | class AddTiersToDataset < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :datasets, :tiers, :jsonb, default: [] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190814210208_add_created_by_to_group.rb: -------------------------------------------------------------------------------- 1 | class AddCreatedByToGroup < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :groups, :created_by_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/mailer_previews/charges_limit_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class ChargesLimitMailerPreview < ActionMailer::Preview 2 | def notify 3 | ChargesLimitMailer.notify(Organization.last) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/mailer_previews/trial_expired_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class TrialExpiredMailerPreview < ActionMailer::Preview 2 | def notify 3 | TrialExpiredMailer.notify(Organization.last) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/collection_filters.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :collection_filter do 3 | collection 4 | text { Faker::Color.color_name } 5 | filter_type :search 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20180228212023_add_thumbnail_url_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddThumbnailUrlToItems < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :thumbnail_url, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180905223105_add_question_type_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddQuestionTypeToItems < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :question_type, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181114015125_add_data_settings_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddDataSettingsToItems < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :data_settings, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190522234643_add_description_to_datasets.rb: -------------------------------------------------------------------------------- 1 | class AddDescriptionToDatasets < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :datasets, :description, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191007233341_add_status_to_comment.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToComment < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :comments, :status, :integer, default: nil 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191031165716_add_question_type_index_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddQuestionTypeIndexToItems < ActiveRecord::Migration[5.2] 2 | def change 3 | add_index :items, :question_type 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/mailer_previews/billing_overdue_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class BillingOverdueMailerPreview < ActionMailer::Preview 2 | def notify 3 | BillingOverdueMailer.notify(Organization.last) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/api/schemas/role.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "name": { "type": "string" }, 5 | "label": { "type": "string" } 6 | }, 7 | "required": ["name"] 8 | } 9 | -------------------------------------------------------------------------------- /app/serializers/serializable_data_items_datasets.rb: -------------------------------------------------------------------------------- 1 | class SerializableDataItemsDataset < BaseJsonSerializer 2 | type 'data_items_datasets' 3 | 4 | belongs_to :dataset 5 | belongs_to :data_item 6 | end 7 | -------------------------------------------------------------------------------- /app/serializers/serializable_filestack_file.rb: -------------------------------------------------------------------------------- 1 | class SerializableFilestackFile < BaseJsonSerializer 2 | type 'filestack_files' 3 | attributes :filename, :url, :handle, :size, :mimetype, :docinfo 4 | end 5 | -------------------------------------------------------------------------------- /config/initializers/locale.rb: -------------------------------------------------------------------------------- 1 | # Whitelist locales available for the application 2 | I18n.available_locales = [ 3 | :en, 4 | :es, 5 | :ja, 6 | :zh_cn, 7 | ] 8 | 9 | I18n.default_locale = :en 10 | -------------------------------------------------------------------------------- /db/migrate/20180604200903_add_draftjs_data_to_comments.rb: -------------------------------------------------------------------------------- 1 | class AddDraftjsDataToComments < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :comments, :draftjs_data, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180809214501_add_show_move_helper.rb: -------------------------------------------------------------------------------- 1 | class AddShowMoveHelper < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :show_move_helper, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181204090132_add_created_at_index_to_activities.rb: -------------------------------------------------------------------------------- 1 | class AddCreatedAtIndexToActivities < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :activities, :created_at 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190523213147_add_groupings_to_dataset.rb: -------------------------------------------------------------------------------- 1 | class AddGroupingsToDataset < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :datasets, :groupings, :jsonb, default: [] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190814000051_add_application_to_dataset.rb: -------------------------------------------------------------------------------- 1 | class AddApplicationToDataset < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :datasets, :application_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191018203725_add_edited_to_comment.rb: -------------------------------------------------------------------------------- 1 | class AddEditedToComment < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :comments, :edited, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/api/schemas/tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "name": { "type": "string" }, 5 | "taggings_count": { "type": "number" } 6 | }, 7 | "required": ["name"] 8 | } 9 | -------------------------------------------------------------------------------- /db/migrate/20180404234941_add_created_by_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddCreatedByToCollection < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :created_by_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180511004129_add_show_helper_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddShowHelperToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :show_helper, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180518214410_add_cached_attributes_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddCachedAttributesToItems < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :cached_attributes, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180626000452_add_admins_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddAdminsToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :admin_group_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180709231601_add_template_id_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddTemplateIdToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :template_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180711224359_add_cached_attributes_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddCachedAttributesToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :cached_attributes, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180717214927_add_docinfo_to_filestack_files.rb: -------------------------------------------------------------------------------- 1 | class AddDocinfoToFilestackFiles < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :filestack_files, :docinfo, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190312204433_add_legend_item_id_to_data_item.rb: -------------------------------------------------------------------------------- 1 | class AddLegendItemIdToDataItem < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :legend_item_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190524214536_add_type_to_groups.rb: -------------------------------------------------------------------------------- 1 | class AddTypeToGroups < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :groups, :type, :string 4 | add_index :groups, :type 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191206221958_add_search_term_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddSearchTermToCollection < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :search_term, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200206224344_add_num_columns_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddNumColumnsToCollections < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :num_columns, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200417163307_add_last_broadcast_at_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddLastBroadcastAtToItems < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :items, :last_broadcast_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/feedback_incentive_records.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :feedback_incentive_record do 3 | user nil 4 | survey_response 5 | amount 2.00 6 | current_balance 0 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/models/item/link_item_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Item::LinkItem, type: :model do 4 | context 'validations' do 5 | it { should validate_presence_of(:url) } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20180921212427_add_processing_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddProcessingToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :processing_status, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181002172736_add_mailing_list_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddMailingListToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :mailing_list, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181005173335_add_data_source_to_chart_item.rb: -------------------------------------------------------------------------------- 1 | class AddDataSourceToChartItem < ActiveRecord::Migration[5.1] 2 | def change 3 | add_reference :items, :data_source, polymorphic: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181009204421_add_last_active_at_column_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddLastActiveAtColumnToUser < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :last_active_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190402225253_add_index_to_coll_card_rows_cols.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToCollCardRowsCols < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :collection_cards, [:order, :row, :col] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190418233316_add_launched_at_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddLaunchedAtToCollection < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :test_launched_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191024211521_add_organization_id_to_tags.rb: -------------------------------------------------------------------------------- 1 | class AddOrganizationIdToTags < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :tags, :organization_ids, :jsonb, default: [] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200125010741_add_blank_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddBlankToOrganization < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :organizations, :shell, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/activities.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :activity do 3 | organization factory: :organization_without_groups 4 | actor factory: :user 5 | target factory: :collection 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20180209192525_add_current_organization_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddCurrentOrganizationToUser < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :current_organization_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180416195913_add_terms_accepted_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddTermsAcceptedToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :terms_accepted, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181009212428_add_trial_ends_at_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddTrialEndsAtToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :trial_ends_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190319185820_add_cover_type_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddCoverTypeToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :cover_type, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190514224531_add_test_closed_at_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddTestClosedAtToCollection < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :test_closed_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190523220518_remove_organization_id_from_audiences.rb: -------------------------------------------------------------------------------- 1 | class RemoveOrganizationIdFromAudiences < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_reference :audiences, :organization 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190528210255_rename_name_to_identifier_for_dataset.rb: -------------------------------------------------------------------------------- 1 | class RenameNameToIdentifierForDataset < ActiveRecord::Migration[5.1] 2 | def change 3 | rename_column :datasets, :name, :identifier 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190604040111_add_shape_circle_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddShapeCircleToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :shape_circle_member, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190718204608_add_default_group_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddDefaultGroupToCollection < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :default_group_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200731212721_add_billable_to_organzation.rb: -------------------------------------------------------------------------------- 1 | class AddBillableToOrganzation < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :organizations, :billable, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | __js_test_config 2 | app/assets/javascripts/application.js 3 | app/javascript/vendor/page-metadata-parser.js 4 | node_modules 5 | ./vendor/* 6 | dist 7 | public 8 | styleguide 9 | **/*.min.js 10 | config 11 | -------------------------------------------------------------------------------- /app/controllers/admin/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::DashboardController < ApplicationController 2 | before_action :authenticate_user! 3 | before_action :authorize_shape_admin! 4 | 5 | def index 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20180308233902_add_pic_url_square_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddPicUrlSquareToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :pic_url_square, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180413042009_add_shared_collection_to_group.rb: -------------------------------------------------------------------------------- 1 | class AddSharedCollectionToGroup < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :groups, :current_shared_collection_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180604225632_add_handle_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddHandleToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :handle, :string 4 | add_index :users, :handle, unique: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20180817154020_add_template_helper_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddTemplateHelperToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :show_template_helper, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180927190104_add_status_to_survey_responses.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToSurveyResponses < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :survey_responses, :status, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181212191943_add_more_data_indexing.rb: -------------------------------------------------------------------------------- 1 | class AddMoreDataIndexing < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :collections, :created_at 4 | add_index :items, :created_at 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190729171359_add_terms_version_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddTermsVersionToOrganizations < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :organizations, :terms_version, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191028205029_add_section_type_to_collection_card.rb: -------------------------------------------------------------------------------- 1 | class AddSectionTypeToCollectionCard < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collection_cards, :section_type, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/api_tokens.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :api_token do 3 | application 4 | 5 | trait :for_organization do 6 | organization 7 | application nil 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/javascript/enums/actionEnums.js: -------------------------------------------------------------------------------- 1 | export const UNDO_ACTION_STATUS = { 2 | IDLE: 0, 3 | UNDO: 1, 4 | REDO: 2, 5 | } 6 | 7 | export const POPUP_ACTION_TYPES = { 8 | SNACKBAR: 'snackbar', 9 | ALERT: 'alert', 10 | } 11 | -------------------------------------------------------------------------------- /app/javascript/utils/objectUtils.js: -------------------------------------------------------------------------------- 1 | // simple way to detect if prop changes warrant re-initializing 2 | export const objectsEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b) 3 | 4 | export default { 5 | objectsEqual, 6 | } 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20180406184838_add_current_user_collection_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddCurrentUserCollectionToUser < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :current_user_collection_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180422165218_add_cached_attributes_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddCachedAttributesToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :cached_attributes, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180618224154_add_email_notifications_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddEmailNotificationsToUser < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :notify_through_email, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180727160754_add_last_notification_sent_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddLastNotificationSentToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :last_notification_mail_sent, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180810231839_add_icon_url_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddIconUrlToItems < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :icon_url, :string 4 | remove_column :items, :image, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20180822181719_add_submissions_collection_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddSubmissionsCollectionToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_reference :collections, :submission_box 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181015214646_add_is_cover_to_collection_card.rb: -------------------------------------------------------------------------------- 1 | class AddIsCoverToCollectionCard < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collection_cards, :is_cover, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190107220606_add_hidden_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddHiddenToCollectionCards < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collection_cards, :hidden, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190110222910_add_custom_terms_id_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddCustomTermsIdToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :terms_text_item_id, :bigint 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190122183523_add_subscribed_to_users_thread.rb: -------------------------------------------------------------------------------- 1 | class AddSubscribedToUsersThread < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users_threads, :subscribed, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190220003415_rename_text_data_to_data_content_on_items.rb: -------------------------------------------------------------------------------- 1 | class RenameTextDataToDataContentOnItems < ActiveRecord::Migration[5.1] 2 | def change 3 | rename_column :items, :text_data, :data_content 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190424210415_add_should_recontact_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddShouldRecontactToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :feedback_contact_preference, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190430234650_add_joinable_group_id_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddJoinableGroupIdToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :joinable_group_id, :bigint 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190514000935_add_test_audience_id_to_survey_responses.rb: -------------------------------------------------------------------------------- 1 | class AddTestAudienceIdToSurveyResponses < ActiveRecord::Migration[5.1] 2 | def change 3 | add_belongs_to :survey_responses, :test_audience 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190703185758_add_network_id_to_groups.rb: -------------------------------------------------------------------------------- 1 | class AddNetworkIdToGroups < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :groups, :network_id, :string 4 | add_index :groups, :network_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20201110210451_add_section_name_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddSectionNameToCollectionCards < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collection_cards, :section_name, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/mailer_previews/trial_users_count_exceeded_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class TrialUsersCountExceededMailerPreview < ActionMailer::Preview 2 | def notify 3 | TrialUsersCountExceededMailer.notify(Organization.last) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180627183820_add_profile_template_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddProfileTemplateToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :template_collection_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190924174615_add_children_counter_cache_to_comment.rb: -------------------------------------------------------------------------------- 1 | class AddChildrenCounterCacheToComment < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :comments, :replies_count, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191106185833_add_idea_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddIdeaToCollections < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :idea_id, :integer 4 | add_index :collections, :idea_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191217233558_add_collection_type_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddCollectionTypeToCollections < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :collection_type, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200226214443_add_user_settings_data_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddUserSettingsDataToUser < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :users, :user_settings_data, :jsonb, default: {}, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200626174631_add_parent_snapshot_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddParentSnapshotToCollectionCards < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collection_cards, :parent_snapshot, :jsonb 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/network_invitations.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :network_invitation do 3 | token { SecureRandom.alphanumeric(12) } 4 | user nil 5 | organization factory: :organization_without_groups 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /__js_test_config/helpers/expectTreeToMatchSnapshot.js: -------------------------------------------------------------------------------- 1 | import toJson from 'enzyme-to-json' 2 | 3 | const expectTreeToMatchSnapshot = tree => { 4 | expect(toJson(tree)).toMatchSnapshot() 5 | } 6 | 7 | export default expectTreeToMatchSnapshot 8 | -------------------------------------------------------------------------------- /app/views/deprovision_user_mailer/missing_org_admin.text.erb: -------------------------------------------------------------------------------- 1 | <%= @user.email %> was deprovisioned, and now the organization: "<%= @group.organization.name %>" has no organization admin. 2 | 3 | The org is probably in a somewhat broken state. 4 | -------------------------------------------------------------------------------- /cypress/integration/MarketingPage.feature: -------------------------------------------------------------------------------- 1 | Feature: Visiting the Marketing Page 2 | 3 | Scenario: Non-logged in visitors should see the Marketing Page 4 | Given I visit the Marketing Page 5 | Then I should see a ".MarketingShapeLogo" 6 | -------------------------------------------------------------------------------- /db/migrate/20180702204405_add_profile_template_id_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddProfileTemplateIdToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :profile_template_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180815173801_add_master_template_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddMasterTemplateToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :master_template, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190108234025_add_hide_submissions_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddHideSubmissionsToCollection < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :hide_submissions, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190425182045_add_anyone_can_view_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddAnyoneCanViewToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :anyone_can_view, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190430210655_add_anyone_can_join_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddAnyoneCanJoinToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :anyone_can_join, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191101212918_add_test_show_media_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddTestShowMediaToCollections < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :test_show_media, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191106190741_add_respondent_alias_to_survey_response.rb: -------------------------------------------------------------------------------- 1 | class AddRespondentAliasToSurveyResponse < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :survey_responses, :respondent_alias, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/groups_thread_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe GroupsThread, type: :model do 4 | context 'associations' do 5 | it { should belong_to :group } 6 | it { should belong_to :comment_thread } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 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 'bundler/setup' 8 | load Gem.bin_path('rspec-core', 'rspec') 9 | -------------------------------------------------------------------------------- /db/migrate/20180703192353_add_profile_collection_id_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddProfileCollectionIdToOrganizations < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :profile_collection_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180924221647_add_network_subscription_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddNetworkSubscriptionToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :network_subscription_id, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180928225725_add_test_collection_id_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddTestCollectionIdToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :test_collection_id, :bigint, index: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181015181308_add_getting_started_id_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddGettingStartedIdToCollection < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :getting_started_collection_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181106065121_add_type_indices.rb: -------------------------------------------------------------------------------- 1 | class AddTypeIndices < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :items, :type 4 | add_index :collections, :type 5 | add_index :collection_cards, :type 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20190215192116_add_replaceable_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddReplaceableToCollectionCards < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collection_cards, :show_replace, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190513210422_add_feedback_terms_accepted_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddFeedbackTermsAcceptedToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :feedback_terms_accepted, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20201002180033_add_cached_attributes_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddCachedAttributesToOrganizations < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :organizations, :cached_attributes, :jsonb, default: {} 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20201016211257_add_cover_card_id_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddCoverCardIdToCollectionCards < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collection_cards, :cover_card_id, :integer, default: nil 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /__js_test_config/mocks/fakeUndoStore.js: -------------------------------------------------------------------------------- 1 | const fakeUndoStore = { 2 | pushUndoAction: jest.fn(), 3 | undoLastAction: jest.fn(), 4 | captureUndoKeypress: jest.fn(), 5 | performActionAfterRoute: jest.fn(), 6 | } 7 | 8 | export default fakeUndoStore 9 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /db/migrate/20180329211218_add_invitation_token_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddInvitationTokenToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :invitation_token, :string 4 | add_index :users, :invitation_token 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20180828211535_add_image_contain_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddImageContainToCollectionCards < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collection_cards, :image_contain, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190220192800_add_shared_with_org_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddSharedWithOrgToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :shared_with_organization, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190422230949_add_submissions_enabled_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddSubmissionsEnabledToCollection < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :submissions_enabled, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190529222849_add_respondent_terms_accepted_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddRespondentTermsAcceptedToUser < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :respondent_terms_accepted, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191119202037_change_price_per_response_to_min_price.rb: -------------------------------------------------------------------------------- 1 | class ChangePricePerResponseToMinPrice < ActiveRecord::Migration[5.2] 2 | def change 3 | rename_column :audiences, :price_per_response, :min_price_per_response 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200728220748_add_is_background_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddIsBackgroundToCollectionCards < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collection_cards, :is_background, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/views/test_collection_mailer/notify_closed.text.erb: -------------------------------------------------------------------------------- 1 | Your recent request for <%= pluralize(@sample_size, 'response') %> for <%= @test_collection.name %> has been completed. 2 | 3 | To see how they responded, click below: 4 | 5 | <%= @collection_url %> 6 | -------------------------------------------------------------------------------- /config/appsignal.yml: -------------------------------------------------------------------------------- 1 | production: 2 | active: <%= ENV['APPSIGNAL_PUSH_API_KEY'].present? %> 3 | push_api_key: <%= ENV['APPSIGNAL_PUSH_API_KEY'] %> 4 | name: <%= ENV.fetch('APPSIGNAL_APP_NAME') { 'Shape' } %> 5 | enable_frontend_error_catching: true 6 | -------------------------------------------------------------------------------- /db/migrate/20180131212625_create_organizations.rb: -------------------------------------------------------------------------------- 1 | class CreateOrganizations < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :organizations do |t| 4 | t.string :name 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20180917173119_add_test_status_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddTestStatusToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :test_status, :integer 4 | add_index :collections, :test_status 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20181009212705_add_trial_user_count_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddTrialUserCountToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :trial_users_count, :integer, null: false, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181106211944_add_deactivated_flag_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddDeactivatedFlagToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :deactivated, :boolean, default: false, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190726063501_add_activities_indexes.rb: -------------------------------------------------------------------------------- 1 | class AddActivitiesIndexes < ActiveRecord::Migration[5.2] 2 | def change 3 | add_index :activities, %i[action target_type organization_id], name: 'index_activities_action_target_org' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191112190014_add_survey_responses_id_to_test_results_collection.rb: -------------------------------------------------------------------------------- 1 | class AddSurveyResponsesIdToTestResultsCollection < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :survey_response_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200624153202_add_icon_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddIconToCollections < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :icon, :string 4 | add_column :collections, :show_icon_on_cover, :boolean 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20201016210636_add_cached_attributes_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddCachedAttributesToCollectionCards < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collection_cards, :cached_attributes, :jsonb, default: {} 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/external_record_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe ExternalRecord, type: :model do 4 | context 'associations' do 5 | it { should belong_to :application } 6 | it { should belong_to :externalizable } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/services/search/filters/type_item.rb: -------------------------------------------------------------------------------- 1 | class Search 2 | module Filters 3 | class TypeItem < Base 4 | REGEXP = /type:items?/.freeze 5 | 6 | def options 7 | { index_name: [Item] } 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20181009172408_add_active_users_count_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddActiveUsersCountToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :active_users_count, :integer, null: false, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181016214104_add_in_app_billing_flag_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddInAppBillingFlagToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :in_app_billing, :boolean, default: true, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190103230243_add_filter_to_collection_card.rb: -------------------------------------------------------------------------------- 1 | class AddFilterToCollectionCard < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collection_cards, :filter, :integer, default: CollectionCard.filters[:transparent_gray] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190513232903_add_price_per_response_to_test_audiences.rb: -------------------------------------------------------------------------------- 1 | class AddPricePerResponseToTestAudiences < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :test_audiences, :price_per_response, :decimal, precision: 10, scale: 2 4 | end 5 | end -------------------------------------------------------------------------------- /db/migrate/20191015192616_add_application_to_groups.rb: -------------------------------------------------------------------------------- 1 | class AddApplicationToGroups < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :groups, :application_id, :integer 4 | add_column :applications, :group_icon_url, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20200324231908_add_subgroups_to_group.rb: -------------------------------------------------------------------------------- 1 | class AddSubgroupsToGroup < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :groups, :subgroup_ids, :jsonb, default: [] 4 | add_index :groups, :subgroup_ids, using: :gin 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/views/invitation_mailer/invite.text.erb: -------------------------------------------------------------------------------- 1 | Hello<%= " #{@user.name}" if @user.name.present?%>, 2 | 3 | <%= @mail_helper.invite_message %> 4 | 5 | <%= @mail_helper.support_message if @mail_helper.support_message.present? %> 6 | 7 | <%= @mail_helper.invite_url %> 8 | -------------------------------------------------------------------------------- /cypress/integration/MyCollection.feature: -------------------------------------------------------------------------------- 1 | Feature: Visiting My Collection 2 | 3 | Scenario: I visit my collection and see the "Cypress Test Area" card 4 | Given I login and visit My Collection 5 | Then I should see a collection card named "Cypress Test Area" 6 | -------------------------------------------------------------------------------- /db/migrate/20180320003745_add_resource_identifier_to_roles.rb: -------------------------------------------------------------------------------- 1 | class AddResourceIdentifierToRoles < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :roles, :resource_identifier, :string 4 | add_index :roles, :resource_identifier 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20180410000605_add_archive_field_to_groups.rb: -------------------------------------------------------------------------------- 1 | class AddArchiveFieldToGroups < ActiveRecord::Migration[5.1] 2 | def up 3 | Group.add_archived_column! 4 | end 5 | 6 | def down 7 | Group.try(:remove_archived_column!) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20180524202508_add_organization_id_to_activity.rb: -------------------------------------------------------------------------------- 1 | class AddOrganizationIdToActivity < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :activities, :organization_id, :bigint 4 | add_index :activities, :organization_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190327205722_add_row_col_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddRowColToCollectionCards < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collection_cards, :row, :integer 4 | add_column :collection_cards, :col, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191120180804_add_incentive_paid_to_survey_response.rb: -------------------------------------------------------------------------------- 1 | class AddIncentivePaidToSurveyResponse < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :survey_responses, :incentive_paid_amount, :decimal, precision: 10, scale: 2 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/network_api.rb: -------------------------------------------------------------------------------- 1 | require 'network-api' 2 | 3 | NetworkApi::Base.configure( 4 | url: URI.join('https', ENV['IDEO_SSO_HOST'], ENV['IDEO_SSO_API_PATH']).to_s, 5 | api_token: ENV['IDEO_SSO_API_TOKEN'], 6 | client_id: ENV['IDEO_SSO_CLIENT_ID'], 7 | ) 8 | -------------------------------------------------------------------------------- /db/migrate/20180529175444_add_combined_activities_to_notifications.rb: -------------------------------------------------------------------------------- 1 | class AddCombinedActivitiesToNotifications < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :notifications, :combined_activities_ids, :integer, array: true, default: [] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200514232956_add_start_end_date_to_collection.rb: -------------------------------------------------------------------------------- 1 | class AddStartEndDateToCollection < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :start_date, :datetime 4 | add_column :collections, :end_date, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20180830052131_add_source_to_activities.rb: -------------------------------------------------------------------------------- 1 | class AddSourceToActivities < ActiveRecord::Migration[5.1] 2 | def change 3 | add_reference :activities, :source, polymorphic: true 4 | add_reference :activities, :destination, polymorphic: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190110221600_add_autojoin_emails_to_groups.rb: -------------------------------------------------------------------------------- 1 | class AddAutojoinEmailsToGroups < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :groups, :autojoin_emails, :jsonb, default: [] 4 | add_index :groups, :autojoin_emails, using: :gin 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190528222323_add_status_to_test_audience.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToTestAudience < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :test_audiences, :status, :integer, default: 0 4 | add_column :test_audiences, :closed_at, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191014212029_add_anyone_can_view_to_datasets.rb: -------------------------------------------------------------------------------- 1 | class AddAnyoneCanViewToDatasets < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :datasets, :anyone_can_view, :boolean, default: true 4 | add_index :datasets, :anyone_can_view 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191031202704_add_selected_choice_ids_to_question_answers.rb: -------------------------------------------------------------------------------- 1 | class AddSelectedChoiceIdsToQuestionAnswers < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :question_answers, :selected_choice_ids, :jsonb, null: false, default: [] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/callbacks/slack_controller.rb: -------------------------------------------------------------------------------- 1 | class Callbacks::SlackController < ActionController::Base 2 | def event 3 | Slack::ProcessEventReceived.call(event: params[:event]) if params[:event].present? 4 | render plain: params[:challenge], status: :ok 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/views/deprovision_user_mailer/missing_group_admin.text.erb: -------------------------------------------------------------------------------- 1 | <%= @user.email %> was deprovisioned, and now the group: "<%= @group.name %>" has no admin. No organization admins were available to set as new group admins. 2 | 3 | The group is probably in a somewhat broken state. 4 | -------------------------------------------------------------------------------- /db/migrate/20181025181109_add_trail_expired_email_sent_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddTrailExpiredEmailSentToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :trial_expired_email_sent, :boolean, default: false, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20201021200558_add_background_color_to_items.rb: -------------------------------------------------------------------------------- 1 | class AddBackgroundColorToItems < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :items, :background_color, :string 4 | add_column :items, :background_color_opacity, :float, default: 1 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/mailer_previews/billing_changes_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class BillingChangesMailerPreview < ActionMailer::Preview 2 | def notify 3 | u = User.find_by_email 'smith+manage-invoices@substantial.com' 4 | BillingChangesMailer.notify(u.current_organization.id, 3) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /Procfile.development: -------------------------------------------------------------------------------- 1 | web: bin/rails server -p 3000 2 | webpack: bin/webpack-dev-server 3 | worker: bundle exec sidekiq -e ${RACK_ENV:-development} -C config/sidekiq.yml 4 | cable: ANYCABLE_DEPLOYMENT=true bundle exec anycable --server-command="anycable-go --host=localhost --port=3334" 5 | -------------------------------------------------------------------------------- /app/controllers/api/v1/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::SessionsController < Api::V1::BaseController 2 | skip_before_action :check_api_authentication! 3 | 4 | def destroy 5 | sign_out(current_user) if user_signed_in? 6 | render json: {}, status: 200 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/services/search/filters/type_collection.rb: -------------------------------------------------------------------------------- 1 | class Search 2 | module Filters 3 | class TypeCollection < Base 4 | REGEXP = /type:collections?/.freeze 5 | 6 | def options 7 | { index_name: [Collection] } 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20180710204923_change_templated_from_to_id.rb: -------------------------------------------------------------------------------- 1 | class ChangeTemplatedFromToId < ActiveRecord::Migration[5.1] 2 | def change 3 | rename_column :collection_cards, :templated_from, :templated_from_id 4 | add_index :collection_cards, :templated_from_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20180912174042_add_session_uid_to_survey_responses.rb: -------------------------------------------------------------------------------- 1 | class AddSessionUidToSurveyResponses < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :survey_responses, :session_uid, :text 4 | add_index :survey_responses, :session_uid, unique: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190522210918_add_payment_id_to_test_audiences.rb: -------------------------------------------------------------------------------- 1 | class AddPaymentIdToTestAudiences < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :test_audiences, :network_payment_id, :string 4 | add_column :test_audiences, :launched_by_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190904204944_add_locale_to_user_and_organization.rb: -------------------------------------------------------------------------------- 1 | class AddLocaleToUserAndOrganization < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :organizations, :default_locale, :string, default: 'en' 4 | add_column :users, :locale, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191107222348_add_identifier_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddIdentifierToCollectionCards < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collection_cards, :identifier, :string 4 | add_index :collection_cards, [:identifier, :parent_id] 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/services/mailer_helper/router.rb: -------------------------------------------------------------------------------- 1 | module MailerHelper 2 | class Router 3 | include Rails.application.routes.url_helpers 4 | include ApplicationHelper 5 | 6 | def self.default_url_options 7 | ActionMailer::Base.default_url_options 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/workers/record_paid_survey_response_worker.rb: -------------------------------------------------------------------------------- 1 | class RecordPaidSurveyResponseWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(survey_response_id) 5 | survey_response = SurveyResponse.find(survey_response_id) 6 | survey_response.record_incentive_paid! 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /cypress/integration/items/LinkItem.feature: -------------------------------------------------------------------------------- 1 | Feature: Link Item 2 | 3 | Scenario: Creating a link item from the BCT 4 | Given I login and visit the Test Area 5 | When I create a link item card "https://www.shape.space" using the first hot edge 6 | Then I should see a "GridCard" 7 | -------------------------------------------------------------------------------- /db/migrate/20190201202722_create_applications.rb: -------------------------------------------------------------------------------- 1 | class CreateApplications < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :applications do |t| 4 | t.string :name 5 | t.references :user, index: true 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190805230103_create_global_translations.rb: -------------------------------------------------------------------------------- 1 | class CreateGlobalTranslations < ActiveRecord::Migration[5.2] 2 | def up 3 | Migrations::GlobalTranslationMigration.up 4 | end 5 | 6 | def down 7 | Migrations::GlobalTranslationMigration.down 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /__js_test_config/helpers/expectToMatchSnapshot.js: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme' 2 | import expectTreeToMatchSnapshot from '#/helpers/expectTreeToMatchSnapshot' 3 | 4 | const expectToMatchSnapshot = toRender => { 5 | const tree = shallow(toRender) 6 | expectTreeToMatchSnapshot(tree) 7 | } 8 | -------------------------------------------------------------------------------- /app/views/charges_limit_mailer/notify.text.erb: -------------------------------------------------------------------------------- 1 | <%= @subject %> 2 | 3 | <%= @organization_name %> has <%= @active_users_count %> active users and monthly charge will be <%= number_to_currency(@next_monthly_charge) %>. Please reach out to <%= @organization_name %> to arrange a new billing method. 4 | -------------------------------------------------------------------------------- /app/views/home/login.html.erb: -------------------------------------------------------------------------------- 1 | 9 | 10 | <%= javascript_packs_with_polyfill 'ideo-sso' %> 11 | -------------------------------------------------------------------------------- /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/20171104063543_add_omniauth_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddOmniauthToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :provider, :string 4 | add_index :users, :provider 5 | add_column :users, :uid, :string 6 | add_index :users, :uid 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20180831213535_create_survey_responses.rb: -------------------------------------------------------------------------------- 1 | class CreateSurveyResponses < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :survey_responses do |t| 4 | t.references :test_collection, index: true 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20181101191730_add_cached_test_scores_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddCachedTestScoresToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :cached_test_scores, :jsonb 4 | add_index :collections, :cached_test_scores, using: :gin 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190607215955_add_incentive_status_to_survey_response.rb: -------------------------------------------------------------------------------- 1 | class AddIncentiveStatusToSurveyResponse < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :survey_responses, :incentive_status, :integer 4 | add_index :survey_responses, :incentive_status 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/controllers/api/v1/filestack_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::FilestackController < Api::V1::BaseController 2 | # you have to be authenticated to get this token 3 | def token 4 | # default it will expire in one hour 5 | render json: FilestackFile.security_token(read_only: false) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/javascript/utils/__mocks__/IdeoSSO.js: -------------------------------------------------------------------------------- 1 | export default { 2 | getUserInfo: jest.fn().mockReturnValue(Promise.resolve()), 3 | signIn: jest.fn().mockReturnValue(Promise.resolve()), 4 | signUp: jest.fn().mockReturnValue(Promise.resolve()), 5 | logout: jest.fn().mockReturnValue(Promise.resolve()), 6 | } 7 | -------------------------------------------------------------------------------- /db/migrate/20180928000522_add_open_response_item_and_question_item.rb: -------------------------------------------------------------------------------- 1 | class AddOpenResponseItemAndQuestionItem < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :question_answers, :open_response_item_id, :integer 4 | add_column :collections, :question_item_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/tasks/cached_templates.rake: -------------------------------------------------------------------------------- 1 | namespace :template do 2 | desc 'Caches org most used templates' 3 | task cache_organization_most_used_templates: :environment do 4 | Organization.find_in_batches.each do |batch| 5 | batch.each { |o| o.cache_most_used_template_ids! } 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/models/api_token_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe ApiToken, type: :model do 4 | describe 'callbacks' do 5 | let!(:api_token) { create(:api_token) } 6 | 7 | it 'creates token' do 8 | expect(api_token.token).not_to be_nil 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20180703175717_add_pinned_templated_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddPinnedTemplatedToCollectionCards < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collection_cards, :pinned, :boolean, default: false 4 | add_column :collection_cards, :templated_from, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/deserializers/base_json_deserializer.rb: -------------------------------------------------------------------------------- 1 | class BaseJsonDeserializer < JSONAPI::Deserializable::Resource 2 | # deserialize attributes of this model for parsing 3 | # e.g. { "type" => "collections", "attributes" => { "name" => "..." } } 4 | # will include "name" in params[:collections] 5 | attributes 6 | end 7 | -------------------------------------------------------------------------------- /app/serializers/serializable_data_item.rb: -------------------------------------------------------------------------------- 1 | class SerializableDataItem < SerializableItem 2 | attributes :data_settings, :report_type, :title, :description, :style 3 | 4 | has_many :datasets do 5 | data do 6 | # omitted on purpose to be filled in async 7 | [] 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /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/cable.yml: -------------------------------------------------------------------------------- 1 | production: 2 | adapter: <%= ENV.fetch('ACTION_CABLE_ADAPTER') { 'redis' } %> 3 | url: <%= ENV['REDIS_URL'] %> 4 | development: 5 | adapter: <%= ENV.fetch('ACTION_CABLE_ADAPTER') { 'redis' } %> 6 | url: <%= ENV.fetch('REDIS_URL') { 'redis://localhost:6379' } %> 7 | test: 8 | adapter: test 9 | -------------------------------------------------------------------------------- /db/migrate/20181018175707_add_collection_to_test_to_test_collection.rb: -------------------------------------------------------------------------------- 1 | class AddCollectionToTestToTestCollection < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :collection_to_test_id, :bigint, index: true 4 | add_reference :survey_responses, :user, index: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190104004056_add_autojoin_whitelist_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddAutojoinWhitelistToOrganizations < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :autojoin_domains, :jsonb, default: [] 4 | add_index :organizations, :autojoin_domains, using: :gin 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191009212511_add_subjects_to_comments.rb: -------------------------------------------------------------------------------- 1 | class AddSubjectsToComments < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :comments, :subject_id, :integer 4 | add_column :comments, :subject_type, :string 5 | 6 | add_index :comments, [:subject_id, :subject_type] 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20200124010234_add_font_properties_to_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddFontPropertiesToCollectionCards < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collection_cards, :font_color, :string 4 | add_column :collection_cards, :font_background, :boolean, default: false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/javascript/stores/jsonApi/FilestackFile.js: -------------------------------------------------------------------------------- 1 | import { apiUrl } from '~/utils/url' 2 | 3 | import BaseRecord from './BaseRecord' 4 | 5 | class FilestackFile extends BaseRecord { 6 | static type = 'filestack_files' 7 | static endpoint = apiUrl('filestack_files') 8 | } 9 | 10 | export default FilestackFile 11 | -------------------------------------------------------------------------------- /app/javascript/stores/jsonApi/QuestionChoice.js: -------------------------------------------------------------------------------- 1 | import { apiUrl } from '~/utils/url' 2 | import BaseRecord from './BaseRecord' 3 | 4 | class QuestionChoice extends BaseRecord { 5 | static type = 'question_choices' 6 | static endpoint = apiUrl('question_choices') 7 | } 8 | 9 | export default QuestionChoice 10 | -------------------------------------------------------------------------------- /db/migrate/20181023184040_add_trial_users_count_exceeded_email_sent_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddTrialUsersCountExceededEmailSentToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :trial_users_count_exceeded_email_sent, :boolean, default: false, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20181031164042_add_unarchived_at_fields.rb: -------------------------------------------------------------------------------- 1 | class AddUnarchivedAtFields < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :unarchived_at, :datetime 4 | add_column :collection_cards, :unarchived_at, :datetime 5 | add_column :items, :unarchived_at, :datetime 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/serializers/serializable_admin_test_collection.rb: -------------------------------------------------------------------------------- 1 | class SerializableAdminTestCollection < SerializableSimpleCollection 2 | type 'collections' 3 | 4 | attributes :name, :test_launched_at 5 | 6 | has_many :test_audiences do 7 | data do 8 | @object.paid_test_audiences 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/serializers/serializable_comment_thread.rb: -------------------------------------------------------------------------------- 1 | class SerializableCommentThread < BaseJsonSerializer 2 | type 'comment_threads' 3 | attributes :updated_at 4 | belongs_to :record 5 | 6 | has_one :users_thread do 7 | data do 8 | @object.users_thread_for(@current_user) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/workers/network_user_update_worker.rb: -------------------------------------------------------------------------------- 1 | class NetworkUserUpdateWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(user_id, field) 5 | user = User.find_by(id: user_id) 6 | return unless user&.network_user.present? 7 | 8 | user.network_user.update(field => user.send(field)) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20190517171959_legend_search_source_data_items_dataset_index.rb: -------------------------------------------------------------------------------- 1 | class LegendSearchSourceDataItemsDatasetIndex < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :legend_search_source, :integer 4 | add_index :data_items_datasets, [:data_item_id, :dataset_id], unique: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190523194711_create_audience_organizations.rb: -------------------------------------------------------------------------------- 1 | class CreateAudienceOrganizations < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :audience_organizations do |t| 4 | t.references :audience 5 | t.references :organization 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190613230119_add_incentive_owed_paid_date_to_survey_response.rb: -------------------------------------------------------------------------------- 1 | class AddIncentiveOwedPaidDateToSurveyResponse < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :survey_responses, :incentive_owed_at, :datetime 4 | add_column :survey_responses, :incentive_paid_at, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191030230157_add_archive_batch_index.rb: -------------------------------------------------------------------------------- 1 | class AddArchiveBatchIndex < ActiveRecord::Migration[5.2] 2 | def change 3 | add_index :items, :archive_batch 4 | add_index :collections, :archive_batch 5 | add_index :collection_cards, :archive_batch 6 | add_index :groups, :archive_batch 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/javascript/stores/__mocks__/index.js: -------------------------------------------------------------------------------- 1 | import fakeApiStore from '#/mocks/fakeApiStore' 2 | import fakeUiStore from '#/mocks/fakeUiStore' 3 | import fakeRoutingStore from '#/mocks/fakeRoutingStore' 4 | 5 | export const apiStore = fakeApiStore() 6 | export const routingStore = fakeRoutingStore 7 | export const uiStore = fakeUiStore 8 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/ResizeIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const ResizeIcon = () => ( 4 | 5 | 6 | 7 | 8 | 9 | ) 10 | 11 | export default ResizeIcon 12 | -------------------------------------------------------------------------------- /app/serializers/serializable_question_choice.rb: -------------------------------------------------------------------------------- 1 | class SerializableQuestionChoice < BaseJsonSerializer 2 | type 'question_choices' 3 | attributes :text, 4 | :order, 5 | :question_item_id, 6 | :archived 7 | 8 | attribute :value do 9 | @object.value.to_s 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/workers/notification_mention_mailer_worker.rb: -------------------------------------------------------------------------------- 1 | require 'sidekiq-scheduler' 2 | 3 | class NotificationMentionMailerWorker 4 | include Sidekiq::Worker 5 | 6 | def perform 7 | puts 'Sending mention notification emails' 8 | NotificationDigest.call(type: :mentions, timeframe: 10.minutes.ago) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20180522184536_create_activity_subjects.rb: -------------------------------------------------------------------------------- 1 | class CreateActivitySubjects < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :activity_subjects do |t| 4 | t.references :activity 5 | t.references :subject, polymorphic: true 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /script/review-app-teardown: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script is run once, after the app is destroyed 4 | # Docs: https://devcenter.heroku.com/articles/github-integration-review-apps 5 | 6 | # Remove the redirect URI for this review app to the whitelist of redirect URIs 7 | rake network_api:application_remove_redirect_uri 8 | -------------------------------------------------------------------------------- /spec/support/api/schemas/survey_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "session_uid": { "type": "string" }, 5 | "user_id": { "type": ["integer", "null"] }, 6 | "test_collection_id": { "type": ["integer", "null"] }, 7 | "potential_incentive": { "type": ["number"] } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/serializers/serializable_survey_response.rb: -------------------------------------------------------------------------------- 1 | class SerializableSurveyResponse < BaseJsonSerializer 2 | type 'survey_responses' 3 | attributes :session_uid, :user_id, :test_collection_id 4 | has_many :question_answers 5 | 6 | attribute :potential_incentive do 7 | @object.potential_incentive.to_f 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20180321233803_create_groups_role.rb: -------------------------------------------------------------------------------- 1 | class CreateGroupsRole < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table(:groups_roles) do |t| 4 | t.references :group 5 | t.references :role 6 | end 7 | 8 | add_index :groups_roles, [:group_id, :role_id], unique: true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20180522003321_create_activities.rb: -------------------------------------------------------------------------------- 1 | class CreateActivities < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :activities do |t| 4 | t.references :actor 5 | t.references :target, polymorphic: true 6 | t.integer :action 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20191015211748_create_collection_filters.rb: -------------------------------------------------------------------------------- 1 | class CreateCollectionFilters < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :collection_filters do |t| 4 | t.integer :filter_type 5 | t.string :text 6 | t.references :collection 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /__js_test_config/mocks/fakeActionCableConsumer.js: -------------------------------------------------------------------------------- 1 | const fakeActionCableConsumer = () => { 2 | return { 3 | subscriptions: { 4 | create: (channelConfig, callbackFns) => { 5 | { 6 | perform: jest.fn() 7 | } 8 | } 9 | } 10 | } 11 | } 12 | 13 | export default fakeActionCableConsumer 14 | -------------------------------------------------------------------------------- /app/views/deprovision_user_mailer/missing_collection_editor.text.erb: -------------------------------------------------------------------------------- 1 | <%= @user.email %> was deprovisioned, and now the collection: "<%= @collection.name %>" has no primary editor. 2 | 3 | The org admin group for the collection organization has been added as editor on the collection. 4 | 5 | The collection may not have any active editors. 6 | -------------------------------------------------------------------------------- /app/workers/daily_organization_usage_worker.rb: -------------------------------------------------------------------------------- 1 | class DailyOrganizationUsageWorker 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | Organization.find_in_batches.each do |batch| 6 | batch.pluck(:id).each do |id| 7 | NetworkCreateUsageRecordWorker.perform_async(id) 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20180208012030_create_groups.rb: -------------------------------------------------------------------------------- 1 | class CreateGroups < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :groups do |t| 4 | t.string :name 5 | t.references :organization 6 | t.timestamps 7 | end 8 | 9 | add_column :organizations, :primary_group_id, :integer 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20180522023119_create_notifications.rb: -------------------------------------------------------------------------------- 1 | class CreateNotifications < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :notifications do |t| 4 | t.boolean :read, default: false 5 | t.references :activity 6 | t.references :user 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/tasks/challenge.rake: -------------------------------------------------------------------------------- 1 | namespace :challenge do 2 | desc 'Create challenge audience and assign to organization' 3 | task initialize_challenge_audiences: :environment do 4 | %w[Admins Reviewers Participants].each do |audience_name| 5 | Audience.create(name: audience_name, audience_type: :challenge) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/javascript/stores/jsonApi/SharedRecordMixin.js: -------------------------------------------------------------------------------- 1 | import TitleAndCoverEditingMixin from '~/stores/jsonApi/mixins/TitleAndCoverEditingMixin' 2 | import BaseMixin from '~/stores/jsonApi/mixins/BaseMixin' 3 | 4 | const SharedRecordMixin = superclass => 5 | TitleAndCoverEditingMixin(BaseMixin(superclass)) 6 | 7 | export default SharedRecordMixin 8 | -------------------------------------------------------------------------------- /app/javascript/utils/googleAnalytics/__mocks__/utmUtils.js: -------------------------------------------------------------------------------- 1 | export const storeUtmParams = jest.fn() 2 | 3 | export const getStoredUtmParams = jest 4 | .fn() 5 | .mockReturnValue('?utm_campaign=test-campaign-1') 6 | 7 | export const utmParamsFromLocation = jest 8 | .fn() 9 | .mockReturnValue({ utm_campaign: 'test-campaign-1' }) 10 | -------------------------------------------------------------------------------- /app/services/card_identifier.rb: -------------------------------------------------------------------------------- 1 | class CardIdentifier 2 | def self.call(*objects) 3 | objects = [objects] unless objects.is_a?(Array) 4 | objects.compact.map do |obj| 5 | if obj.is_a?(String) 6 | obj 7 | else 8 | "#{obj.class.name}_#{obj.id}" 9 | end 10 | end.join('-') 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/workers/survey_response_completion_worker.rb: -------------------------------------------------------------------------------- 1 | class SurveyResponseCompletionWorker 2 | include Sidekiq::Worker 3 | sidekiq_options queue: 'critical' 4 | 5 | def perform(survey_response_id) 6 | survey_response = SurveyResponse.find(survey_response_id) 7 | SurveyResponseCompletion.call(survey_response) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /cypress/integration/items/VideoItem.feature: -------------------------------------------------------------------------------- 1 | Feature: Video Item 2 | 3 | Scenario: Creating a video item from the BCT 4 | Given I login and visit the Test Area 5 | When I create a video item card "https://vimeo.com/340255030" using the first hot edge 6 | Then I should see a "GridCard" 7 | Then I should see a ".StyledVideoCover" 8 | -------------------------------------------------------------------------------- /db/migrate/20191120192557_drop_feedback_incentive_record.rb: -------------------------------------------------------------------------------- 1 | class DropFeedbackIncentiveRecord < ActiveRecord::Migration[5.2] 2 | def up 3 | drop_table :feedback_incentive_records 4 | end 5 | 6 | def down 7 | # Not really reversible - we don't need the data 8 | create_table :feedback_incentive_records 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../config/environment', __FILE__) 2 | require 'rails/test_help' 3 | 4 | class ActiveSupport::TestCase 5 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 6 | fixtures :all 7 | 8 | # Add more helper methods to be used by all tests here... 9 | end 10 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3001", 3 | "viewportWidth": 1320, 4 | "viewportHeight": 800, 5 | "requestTimeout": 8000, 6 | "userAgent": "cypress", 7 | "supportFile": "cypress/support/setup.js", 8 | "pluginsFile": "cypress/support/plugins.js", 9 | "chromeWebSecurity": false, 10 | "projectId": "4ervdb" 11 | } 12 | -------------------------------------------------------------------------------- /db/migrate/20181029205523_add_overdue_at_and_has_payment_method_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddOverdueAtAndHasPaymentMethodToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :overdue_at, :timestamp 4 | add_column :organizations, :has_payment_method, :boolean, default: false, null: false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190426224043_create_audiences.rb: -------------------------------------------------------------------------------- 1 | class CreateAudiences < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :audiences do |t| 4 | t.string :name 5 | t.float :price_per_response 6 | t.string :criteria 7 | t.references :organization 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20200310183359_create_app_metrics.rb: -------------------------------------------------------------------------------- 1 | class CreateAppMetrics < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :app_metrics do |t| 4 | t.column :metric, :string 5 | t.column :value, :float 6 | 7 | t.timestamps 8 | end 9 | add_index :app_metrics, %i[metric created_at] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/DropdownIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const DropdownIcon = () => ( 4 | 5 | 6 | Dropdown 7 | 8 | 9 | 10 | 11 | 12 | ) 13 | 14 | export default DropdownIcon 15 | -------------------------------------------------------------------------------- /db/migrate/20180316164813_add_handle_to_group_and_organization.rb: -------------------------------------------------------------------------------- 1 | class AddHandleToGroupAndOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :groups, :handle, :string 4 | add_index :groups, :handle 5 | 6 | add_column :organizations, :handle, :string 7 | add_index :organizations, :handle 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20191015212000_user_collection_filter.rb: -------------------------------------------------------------------------------- 1 | class UserCollectionFilter < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :user_collection_filters do |t| 4 | t.boolean :selected, default: true 5 | t.references :collection_filter 6 | t.references :user 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20180214211104_add_breadcrumb_to_objects.rb: -------------------------------------------------------------------------------- 1 | class AddBreadcrumbToObjects < ActiveRecord::Migration[5.1] 2 | def up 3 | Item.add_breadcrumb_column! 4 | Collection.add_breadcrumb_column! 5 | end 6 | 7 | def down 8 | Item.try(:remove_breadcrumb_column!) 9 | Collection.try(:remove_breadcrumb_column!) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/support/api/schemas/simple_user.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "first_name": { "type": "string" }, 5 | "last_name": { "type": "string" }, 6 | "handle": { "type": "string" }, 7 | "pic_url_square": { "type": "string" } 8 | }, 9 | "required": ["first_name", "last_name", "handle", "pic_url_square"] 10 | } 11 | -------------------------------------------------------------------------------- /app/controllers/users/login_redirect_controller.rb: -------------------------------------------------------------------------------- 1 | # Override Devise controller so that /users/[sign_in/new] are not valid routes 2 | class Users::LoginRedirectController < Devise::SessionsController 3 | before_action :redirect_to_profile_login 4 | 5 | private 6 | 7 | def redirect_to_profile_login 8 | redirect_to login_url 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/javascript/utils/__mocks__/ChannelManager.js: -------------------------------------------------------------------------------- 1 | const fakeChannel = { perform: jest.fn() } 2 | export default { 3 | channels: {}, 4 | subscribe: jest.fn().mockReturnValue(fakeChannel), 5 | unsubscribe: jest.fn(), 6 | unsubscribeAllFromChannel: jest.fn(), 7 | channelId: jest.fn(), 8 | getChannel: jest.fn().mockReturnValue(fakeChannel), 9 | } 10 | -------------------------------------------------------------------------------- /app/javascript/utils/isEmail.js: -------------------------------------------------------------------------------- 1 | // from http://emailregex.com/ 2 | // eslint-disable-next-line 3 | const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 4 | 5 | const isEmail = (text = '') => text.match(EMAIL_REGEX) 6 | 7 | export default isEmail 8 | -------------------------------------------------------------------------------- /db/migrate/20190530193416_add_unique_index_to_question_answer.rb: -------------------------------------------------------------------------------- 1 | class AddUniqueIndexToQuestionAnswer < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_index :question_answers, :question_id 4 | remove_index :question_answers, :survey_response_id 5 | add_index :question_answers, [:survey_response_id, :question_id], unique: true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20190822221612_add_addtl_info_to_application.rb: -------------------------------------------------------------------------------- 1 | class AddAddtlInfoToApplication < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :applications, :invite_url, :string 4 | add_column :applications, :invite_cta, :string 5 | add_column :applications, :email, :string 6 | add_column :applications, :logo_url, :string 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/views/trial_ending_soon_mailer/notify.text.erb: -------------------------------------------------------------------------------- 1 | Add payment method 2 | 3 | Your free trial period is ending soon. To keep your content accessible, please go to the Shape billing page and add a payment method to your account. 4 | 5 | Free trial <%= @ending_in %> 6 | 7 | Trial Expiration Date: <%= @expiration_date %> 8 | 9 | Add payment method: <%= @url %> 10 | -------------------------------------------------------------------------------- /db/migrate/20180514204738_create_comment_threads.rb: -------------------------------------------------------------------------------- 1 | class CreateCommentThreads < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :comment_threads do |t| 4 | t.integer :record_id 5 | t.string :record_type 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :comment_threads, :record_id, unique: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20180514204917_create_comments.rb: -------------------------------------------------------------------------------- 1 | class CreateComments < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :comments do |t| 4 | t.integer :comment_thread_id 5 | t.integer :author_id 6 | t.text :message 7 | 8 | t.timestamps 9 | end 10 | 11 | add_index :comments, :comment_thread_id 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20180221220511_create_filestack_files.rb: -------------------------------------------------------------------------------- 1 | class CreateFilestackFiles < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :filestack_files do |t| 4 | t.string :url, :handle, :filename, :mimetype 5 | t.integer :size 6 | t.timestamps 7 | end 8 | 9 | add_column :items, :filestack_file_id, :integer 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20190426225125_create_test_audiences.rb: -------------------------------------------------------------------------------- 1 | class CreateTestAudiences < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :test_audiences do |t| 4 | t.integer :sample_size 5 | t.references :audience, foreign_key: true 6 | t.references :test_collection, index: true 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20200123191847_add_index_to_items_cached_attributes.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToItemsCachedAttributes < ActiveRecord::Migration[5.2] 2 | def up 3 | add_index :items, "(cached_attributes->>'pending_transcoding_uuid')", name: 'index_items_on_transcoding_uuid' 4 | end 5 | def down 6 | remove_index :items, name: 'index_items_on_transcoding_uuid' 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/mailer_previews/test_collection_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class TestCollectionMailerPreview < ActionMailer::Preview 2 | def notify_closed 3 | test_collection = Collection::TestCollection.joins(:test_audiences).last 4 | TestCollectionMailer.notify_closed( 5 | collection_id: test_collection.id, 6 | user_id: User.last.id, 7 | ) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/home/sign_up.html.erb: -------------------------------------------------------------------------------- 1 | 11 | 12 | <%= javascript_packs_with_polyfill 'ideo-sso' %> 13 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | VENDOR_PATH = File.expand_path('..', __dir__) 3 | Dir.chdir(VENDOR_PATH) do 4 | begin 5 | exec "yarnpkg #{ARGV.join(" ")}" 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20180131216045_create_collections.rb: -------------------------------------------------------------------------------- 1 | class CreateCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :collections do |t| 4 | t.string :name, :type 5 | t.references :organization, foreign_key: true 6 | t.references :cloned_from, references: :collections 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/serializers/concerns/serialized_external_id.rb: -------------------------------------------------------------------------------- 1 | module SerializedExternalId 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | attribute :external_id, if: -> { @current_api_token && @current_api_token.application } do 6 | @object.external_records.find_by(application_id: @current_api_token.application_id).try(:external_id) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/serializers/serializable_legend_item.rb: -------------------------------------------------------------------------------- 1 | class SerializableLegendItem < SerializableItem 2 | attributes :legend_search_source, :style 3 | 4 | has_many :datasets do 5 | data do 6 | # NOTE: this gets all datasets if @current_user.nil so those get filtered out in cached renderer 7 | @object.datasets_viewable_by(@current_user) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/workers/collection_grid/board_migrator_worker.rb: -------------------------------------------------------------------------------- 1 | module CollectionGrid 2 | class BoardMigratorWorker 3 | include Sidekiq::Worker 4 | sidekiq_options queue: 'critical' 5 | 6 | def perform(collection_id) 7 | @collection = Collection.find(collection_id) 8 | CollectionGrid::BoardMigrator.call(collection: @collection) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20200520210612_add_challenge_group_ids_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddChallengeGroupIdsToCollections < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :challenge_admin_group_id, :integer 4 | add_column :collections, :challenge_reviewer_group_id, :integer 5 | add_column :collections, :challenge_participant_group_id, :integer 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/support/api/schemas/question_answer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "question_id": { "type": "string" }, 5 | "idea_id": { "type": ["string", "null"] }, 6 | "answer_text": { "type": ["string", "null"] }, 7 | "answer_number": { "type": ["integer", "null"] }, 8 | "selected_choice_ids": { "type": ["array", "[]"] } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/TestGraphIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const TestGraphIcon = () => ( 4 | 5 | 6 | 7 | 8 | 9 | ) 10 | 11 | export default TestGraphIcon 12 | -------------------------------------------------------------------------------- /db/migrate/20180202214858_create_items.rb: -------------------------------------------------------------------------------- 1 | class CreateItems < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :items do |t| 4 | t.string :name 5 | t.string :type, :image 6 | t.text :content 7 | t.references :cloned_from, references: :items 8 | t.boolean :archived, default: false 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20200729224906_add_collection_style_settings.rb: -------------------------------------------------------------------------------- 1 | class AddCollectionStyleSettings < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :collections, :font_color, :string, default: nil 4 | add_column :collections, :propagate_font_color, :boolean, default: false 5 | add_column :collections, :propagate_background_image, :boolean, default: false 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/global_translation.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: global_translations 4 | # 5 | # id :bigint(8) not null, primary key 6 | # created_at :datetime not null 7 | # updated_at :datetime not null 8 | # 9 | 10 | class GlobalTranslation < ApplicationRecord 11 | include Globalizable 12 | 13 | translates_custom :value 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20191030212834_create_question_choices.rb: -------------------------------------------------------------------------------- 1 | class CreateQuestionChoices < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :question_choices do |t| 4 | t.text :text 5 | t.integer :order 6 | t.integer :value 7 | t.boolean :archived 8 | 9 | t.integer :question_item_id 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/tasks/firestore_sync.rake: -------------------------------------------------------------------------------- 1 | namespace :firestore_sync do 2 | desc 'efficiently reindex models' 3 | task sync_comments_for_last_week: :environment do 4 | CommentThread.where( 5 | 'updated_at > ?', 1.week.ago 6 | ).each do |ct| 7 | ct.store_in_firestore 8 | ct.update_firestore_users_threads # <-- this uses the batch writer 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/javascript/utils/ActionCableConsumer.js: -------------------------------------------------------------------------------- 1 | import ActionCable from 'actioncable' 2 | 3 | // get actionCableUrl from metatags in head 4 | const metatag = document.head.querySelector('[name~=action-cable-url][content]') 5 | const actionCableUrl = metatag ? metatag.content : process.env.ACTION_CABLE_URL 6 | const consumer = ActionCable.createConsumer(actionCableUrl) 7 | 8 | export default consumer 9 | -------------------------------------------------------------------------------- /app/views/layouts/_trackers_and_widgets_body.html.erb: -------------------------------------------------------------------------------- 1 | <% if ENV['GOOGLE_TAGMANAGER_ID'].present? %> 2 | 3 | 5 | 6 | <% end %> 7 | -------------------------------------------------------------------------------- /db/migrate/20190227194651_add_phone_number_and_null_email_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddPhoneNumberAndNullEmailToUsers < ActiveRecord::Migration[5.1] 2 | def up 3 | add_column :users, :phone, :string 4 | change_column :users, :email, :string, null: true 5 | end 6 | def down 7 | remove_column :users, :phone 8 | change_column :users, :email, :string, null: false 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20180302185830_add_archive_fields_to_cards_and_collections.rb: -------------------------------------------------------------------------------- 1 | class AddArchiveFieldsToCardsAndCollections < ActiveRecord::Migration[5.1] 2 | def up 3 | Collection.add_archived_column! 4 | CollectionCard.add_archived_column! 5 | end 6 | 7 | def down 8 | Collection.try(:remove_archived_column!) 9 | CollectionCard.try(:remove_archived_column!) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20180831213853_create_question_answers.rb: -------------------------------------------------------------------------------- 1 | class CreateQuestionAnswers < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :question_answers do |t| 4 | t.references :survey_response, index: true 5 | t.references :question, index: true 6 | t.text :answer_text 7 | t.integer :answer_number 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20190702002222_update_comment_thread_index.rb: -------------------------------------------------------------------------------- 1 | class UpdateCommentThreadIndex < ActiveRecord::Migration[5.2] 2 | def change 3 | remove_index :comment_threads, :organization_id 4 | remove_index :comment_threads, :record_id 5 | 6 | add_index :comment_threads, [:record_id, :record_type, :organization_id], unique: true, name: 'index_comment_threads_on_record_and_org' 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20190815222240_change_last_active_at_to_jsonb.rb: -------------------------------------------------------------------------------- 1 | class ChangeLastActiveAtToJsonb < ActiveRecord::Migration[5.2] 2 | def up 3 | remove_column :users, :last_active_at 4 | add_column :users, :last_active_at, :jsonb, default: {} 5 | end 6 | 7 | def down 8 | remove_column :users, :last_active_at 9 | add_column :users, :last_active_at, :datetime 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20200616222835_create_user_tags.rb: -------------------------------------------------------------------------------- 1 | class CreateUserTags < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :user_tags do |t| 4 | t.references :user, index: false 5 | t.references :record, polymorphic: true, index: false 6 | t.timestamps 7 | end 8 | 9 | add_index :user_tags, %i[user_id record_id record_type], unique: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/javascript/stores/jsonApi/DataItemsDataset.js: -------------------------------------------------------------------------------- 1 | import { apiUrl } from '~/utils/url' 2 | import BaseRecord from './BaseRecord' 3 | import SharedRecordMixin from './SharedRecordMixin' 4 | 5 | class DataItemsDataset extends SharedRecordMixin(BaseRecord) { 6 | static type = 'data_items_datasets' 7 | static endpoint = apiUrl('data_items_datasets') 8 | } 9 | 10 | export default DataItemsDataset 11 | -------------------------------------------------------------------------------- /app/javascript/utils/IdeoSSO.js: -------------------------------------------------------------------------------- 1 | import IdeoSSO from 'ideo-sso-js-sdk' 2 | 3 | window.CONFIG = window.CONFIG || {} 4 | 5 | IdeoSSO.initFromEnv({ 6 | IDEO_SSO_HOST: window.CONFIG.IDEO_SSO_HOST, 7 | IDEO_SSO_CLIENT_ID: window.CONFIG.IDEO_SSO_CLIENT_ID, 8 | BASE_HOST: window.CONFIG.BASE_HOST, 9 | IDEO_SSO_REDIRECT_PATH: window.CONFIG.IDEO_SSO_REDIRECT_PATH, 10 | }) 11 | export default IdeoSSO 12 | -------------------------------------------------------------------------------- /db/migrate/20180202214843_create_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class CreateCollectionCards < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :collection_cards do |t| 4 | t.integer :order, null: false 5 | t.integer :width, :height 6 | t.boolean :reference, default: false 7 | t.references :parent, :collection, :item 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/AddTextIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const AddTextIcon = () => ( 4 | 5 | 6 | 7 | 8 | 9 | ) 10 | 11 | export default AddTextIcon 12 | -------------------------------------------------------------------------------- /db/migrate/20180329014550_users_roles_unique_index.rb: -------------------------------------------------------------------------------- 1 | class UsersRolesUniqueIndex < ActiveRecord::Migration[5.1] 2 | def up 3 | remove_index :users_roles, [:user_id, :role_id] 4 | add_index :users_roles, [:user_id, :role_id], unique: true 5 | end 6 | 7 | def down 8 | remove_index :users_roles, [:user_id, :role_id] 9 | add_index :users_roles, [:user_id, :role_id] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20190520233054_add_default_value_to_audiences_price_per_response.rb: -------------------------------------------------------------------------------- 1 | class AddDefaultValueToAudiencesPricePerResponse < ActiveRecord::Migration[5.1] 2 | def up 3 | change_column :audiences, :price_per_response, :decimal, precision: 10, scale: 2, default: 0 4 | end 5 | def down 6 | change_column :audiences, :price_per_response, :decimal, precision: 10, scale: 2, default: nil 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/javascript/ui/groups/styles.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const GroupIconContainer = styled.div` 4 | display: inline-block; 5 | height: 100%; 6 | width: ${props => (props.width ? props.width : '20px')}; 7 | margin-left: ${props => (props.marginLeft ? props.marginLeft : '10px')}; 8 | vertical-align: middle; 9 | ` 10 | GroupIconContainer.displayName = 'GroupIconContainer' 11 | -------------------------------------------------------------------------------- /db/migrate/20190109231008_add_roles_anchor_collection_id.rb: -------------------------------------------------------------------------------- 1 | class AddRolesAnchorCollectionId < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :items, :roles_anchor_collection_id, :bigint 4 | add_column :collections, :roles_anchor_collection_id, :bigint 5 | 6 | add_index :items, :roles_anchor_collection_id 7 | add_index :collections, :roles_anchor_collection_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/javascript/stores/jsonApi/Tag.js: -------------------------------------------------------------------------------- 1 | import { apiUrl } from '~/utils/url' 2 | 3 | import BaseRecord from './BaseRecord' 4 | 5 | class Tag extends BaseRecord { 6 | static type = 'tags' 7 | static endpoint = apiUrl('tags') 8 | 9 | attributesForAPI = ['name', 'taggings_count'] 10 | 11 | constructor(...args) { 12 | super(...args) 13 | } 14 | } 15 | 16 | Tag.defaults = {} 17 | 18 | export default Tag 19 | -------------------------------------------------------------------------------- /app/views/charges_limit_mailer/notify.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
<%= @subject %>
3 |
4 | <%= @organization_name %> has <%= @active_users_count %> active 5 | users and monthly charge will be 6 | <%= number_to_currency(@next_monthly_charge) %>. Please reach out 7 | to <%= @organization_name %> to arrange a new billing method. 8 |
9 |
10 | -------------------------------------------------------------------------------- /app/workers/organization_shell_refresh_worker.rb: -------------------------------------------------------------------------------- 1 | class OrganizationShellRefreshWorker 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | old_organization_ids = Organization.shell.pluck(:id) 6 | 10.times do 7 | OrganizationShellBuilder.new.save 8 | end 9 | return unless old_organization_ids.present? 10 | 11 | Organization.where(id: old_organization_ids).destroy_all 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/mailer_previews/invoice_payment_failed_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class InvoicePaymentFailedMailerPreview < ActionMailer::Preview 2 | def notify 3 | organization = Organization.last 4 | payment_method_id = NetworkApi::PaymentMethod.find( 5 | organization_id: organization.network_organization.id, 6 | ).first 7 | InvoicePaymentFailedMailer.notify(organization.id, payment_method_id) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/mailer_previews/trial_ending_soon_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class TrialEndingSoonMailerPreview < ActionMailer::Preview 2 | def notify_2_days 3 | TrialEndingSoonMailer.notify(Organization.last, 2) 4 | end 5 | 6 | def notify_1_week 7 | TrialEndingSoonMailer.notify(Organization.last, 7) 8 | end 9 | 10 | def notify_2_weeks 11 | TrialEndingSoonMailer.notify(Organization.last, 14) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /app/javascript/ui/pages/AdminPage.js: -------------------------------------------------------------------------------- 1 | import AdminFeedback from '~/ui/admin/AdminFeedback' 2 | import PageContainer from '~/ui/layout/PageContainer' 3 | 4 | class AdminPage extends React.PureComponent { 5 | render() { 6 | return ( 7 |
8 | 9 | 10 | 11 |
12 | ) 13 | } 14 | } 15 | 16 | export default AdminPage 17 | -------------------------------------------------------------------------------- /db/migrate/20180821185430_add_submission_box_template_id_to_collections.rb: -------------------------------------------------------------------------------- 1 | class AddSubmissionBoxTemplateIdToCollections < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :collections, :submission_template_id, :integer 4 | add_column :collections, :submission_box_type, :integer 5 | 6 | add_index :collections, :submission_template_id 7 | add_index :collections, :template_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/mailer_previews/payment_method_expiring_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class PaymentMethodExpiringMailerPreview < ActionMailer::Preview 2 | def notify 3 | organization = Organization.last 4 | payment_method_id = NetworkApi::PaymentMethod.find( 5 | organization_id: organization.network_organization.id, 6 | ).first 7 | PaymentMethodExpiringMailer.notify(organization.id, payment_method_id) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/support/api/schemas/test_audience.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "sample_size": { "type": ["number", "null"] }, 5 | "audience_id": { "type": "string" }, 6 | "price_per_response": { "type": ["number", "null"] }, 7 | "incentive_per_response": { "type": ["number"] }, 8 | "num_completed_responses": { "type": "number" }, 9 | "status": { "type": "string" } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/services/search/filters/record_id.rb: -------------------------------------------------------------------------------- 1 | class Search 2 | module Filters 3 | class RecordId < Base 4 | # matches either ID/numeric only e.g. "131" 5 | # or a collection/item slug e.g. "131-slug-name" 6 | REGEXP = /\A(\d+)(\-[\-\w]*)?\z/.freeze 7 | 8 | def options 9 | id = @query.scan(REGEXP).flatten.first 10 | { where: { id: id } } 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <% if content_for(:title).present? %> 2 | <%= content_for(:title) %> 3 | <% end %> 4 | 5 | <%= yield %> 6 | 7 | You are receiving this email because you have an account or have been invited to <%= @mail_helper.branding_byline %>. 8 | 9 | Unsubscribe: <%= "#{root_url}user_settings" %> 10 | 11 | (c) <%= Date.today.year %> IDEO 12 | Pier 28 Annex, The Embarcadero 13 | San Francisco, CA 94105 14 | -------------------------------------------------------------------------------- /spec/models/item/video_item_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Item::VideoItem, type: :model do 4 | context 'validations' do 5 | it { should validate_presence_of(:url) } 6 | end 7 | 8 | describe '#image_url' do 9 | let(:item) { create(:video_item) } 10 | 11 | it 'should return thumbnail_url' do 12 | expect(item.image_url).to eq item.thumbnail_url 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/javascript/ui/global/Link.js: -------------------------------------------------------------------------------- 1 | import { Link as RouterLink } from 'react-router-dom' 2 | import styled from 'styled-components' 3 | 4 | import v from '~/utils/variables' 5 | 6 | const Link = styled(RouterLink)` 7 | color: ${v.colors.ctaPrimary}; 8 | text-decoration: none; 9 | &:hover { 10 | text-decoration: underline; 11 | } 12 | ` 13 | Link.displayName = 'Link' 14 | 15 | /** @component */ 16 | export default Link 17 | -------------------------------------------------------------------------------- /app/workers/overdue_notification_worker.rb: -------------------------------------------------------------------------------- 1 | class OverdueNotificationWorker 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | Organization.overdue.find_each do |organization| 6 | days_overdue = (Time.current - organization.overdue_at).to_i / 1.day 7 | 8 | next unless [1, 7, 14, 21].include? days_overdue 9 | 10 | BillingOverdueMailer.notify(organization).deliver_later 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20180712013914_add_archived_info_columns.rb: -------------------------------------------------------------------------------- 1 | class AddArchivedInfoColumns < ActiveRecord::Migration[5.1] 2 | def up 3 | [Group, Item, Collection, CollectionCard].each do |model| 4 | model.add_archived_info_columns! 5 | end 6 | end 7 | 8 | def down 9 | [Group, Item, Collection, CollectionCard].each do |model| 10 | model.try(:remove_archived_info_columns!) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/javascript/stores/jsonApi/UsersThread.js: -------------------------------------------------------------------------------- 1 | import { apiUrl } from '~/utils/url' 2 | 3 | import BaseRecord from './BaseRecord' 4 | 5 | class UsersThread extends BaseRecord { 6 | static type = 'users_threads' 7 | static endpoint = apiUrl('users_threads') 8 | 9 | get currentSubscribed() { 10 | if (this.unsubscribedFromEmail) return false 11 | return this.subscribed 12 | } 13 | } 14 | 15 | export default UsersThread 16 | -------------------------------------------------------------------------------- /app/javascript/ui/global/TextButton.md: -------------------------------------------------------------------------------- 1 | Default button style 2 | 3 | ```jsx 4 | 5 | Action 6 | 7 | ``` 8 | 9 | 10 | "More" grid card button 11 | 12 | ```jsx 13 | 14 | Action 15 | 16 | ``` 17 | 18 | ```jsx 19 | import v from '../../utils/variables' 20 | ; 21 | Action 22 | 23 | ``` 24 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/ChevronLeftIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const ChevronLeftIcon = () => ( 4 | 5 | 6 | 7 | 8 | 9 | ) 10 | 11 | export default ChevronLeftIcon 12 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/ChevronRightIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const ChevronRightIcon = () => ( 4 | 5 | 6 | 7 | 8 | 9 | ) 10 | 11 | export default ChevronRightIcon 12 | -------------------------------------------------------------------------------- /app/javascript/utils/scrolling.js: -------------------------------------------------------------------------------- 1 | export function currentScrollPosition() { 2 | const doc = document.documentElement 3 | const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) 4 | return top 5 | } 6 | 7 | export function setScrollHeight(amount, el = document.documentElement) { 8 | el.scrollTop = parseInt(amount) 9 | } 10 | 11 | export default { 12 | currentScrollPosition, 13 | setScrollHeight, 14 | } 15 | -------------------------------------------------------------------------------- /db/migrate/20190925184346_create_group_hierarchies.rb: -------------------------------------------------------------------------------- 1 | class CreateGroupHierarchies < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :group_hierarchies do |t| 4 | t.references :parent_group, references: :groups 5 | t.jsonb :path 6 | t.references :subgroup, references: :groups 7 | 8 | t.timestamps 9 | end 10 | 11 | add_index :group_hierarchies, :path, using: :gin 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20200401042139_remove_uniqueness_from_handles.rb: -------------------------------------------------------------------------------- 1 | class RemoveUniquenessFromHandles < ActiveRecord::Migration[5.2] 2 | # removing this because handles come from INA username, and INA enforces its own uniqueness 3 | def up 4 | remove_index :users, :handle 5 | add_index :users, :handle 6 | end 7 | def down 8 | remove_index :users, :handle 9 | add_index :users, :handle, unique: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/GoIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const GoIcon = () => ( 4 | 5 | 6 | 10 | 11 | 12 | ) 13 | 14 | export default GoIcon 15 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/MoreIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const MoreIcon = () => ( 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ) 14 | 15 | export default MoreIcon 16 | -------------------------------------------------------------------------------- /config/initializers/sidekiq.rb: -------------------------------------------------------------------------------- 1 | module Sidekiq 2 | module Worker 3 | module ClassMethods 4 | # Adds method that can be used to perform job synchronously 5 | def perform_sync(*args) 6 | new.perform(*args) 7 | end 8 | end 9 | end 10 | end 11 | 12 | if ENV['CYPRESS'].present? 13 | require 'sidekiq/testing' 14 | 15 | # in Cypress, run all worker processes inline 16 | Sidekiq::Testing.inline! 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/urls_controller.rb: -------------------------------------------------------------------------------- 1 | class UrlsController < ApplicationController 2 | # authenticate to prevent this method being used as a general proxy 3 | before_action :authenticate_user! 4 | 5 | def passthru 6 | url = params.require(:url) 7 | html = HTTParty.get(URI.encode(url), timeout: 10, retries: 1) 8 | render html: html.response.body.html_safe 9 | rescue StandardError 10 | head :bad_request 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/interactors/record_organization_usage.rb: -------------------------------------------------------------------------------- 1 | class RecordOrganizationUsage 2 | include Interactor::Organizer 3 | include Interactor::Schema 4 | 5 | schema :organization, 6 | :active_users_initial_count, 7 | :billable_users_count 8 | 9 | organize( 10 | CalculateOrganizationActiveUsers, 11 | CalculateOrganizationBillableUsers, 12 | CreateNetworkUsageRecord, 13 | NotifyBillingChanges, 14 | ) 15 | end 16 | -------------------------------------------------------------------------------- /app/mailers/charges_limit_mailer.rb: -------------------------------------------------------------------------------- 1 | class ChargesLimitMailer < ApplicationMailer 2 | def notify(organization) 3 | @organization_name = organization.name 4 | @active_users_count = organization.active_users_count 5 | @next_monthly_charge = Organization::PRICE_PER_USER * @active_users_count 6 | 7 | @subject = "High monthly bill for #{@organization_name}" 8 | mail to: Shape::SUPPORT_EMAIL, subject: @subject 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/workers/organization_membership_worker.rb: -------------------------------------------------------------------------------- 1 | class OrganizationMembershipWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(user_ids, organization_id) 5 | @users = User.where(id: user_ids) 6 | @organization = Organization.find_by_id(organization_id) 7 | return unless @organization.present? 8 | 9 | @users.each do |user| 10 | @organization.setup_user_membership_and_collections(user) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /bin/webpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= 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 | Webpacker::WebpackRunner.run(ARGV) 16 | -------------------------------------------------------------------------------- /config/initializers/rolify.rb: -------------------------------------------------------------------------------- 1 | Rolify.configure do |config| 2 | # By default ORM adapter is ActiveRecord. uncomment to use mongoid 3 | # config.use_mongoid 4 | 5 | # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false 6 | # config.use_dynamic_shortcuts 7 | 8 | # Configuration to remove roles from database once the last resource is removed. Default is: true 9 | # config.remove_role_if_empty = false 10 | end 11 | -------------------------------------------------------------------------------- /app/javascript/ui/global/Avatar.md: -------------------------------------------------------------------------------- 1 | Displaying a user's avatar with photo set in profile.ideo.com 2 | 3 | ```jsx 4 | 8 | ``` 9 | 10 | 11 | ### Roles Summary — Realtime collaborator 12 | 13 | ```jsx 14 | 19 | ``` 20 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/CheckIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from '~/ui/icons/Icon' 2 | 3 | const CheckIcon = () => ( 4 | 5 | 6 | 10 | 11 | 12 | ) 13 | 14 | export default CheckIcon 15 | -------------------------------------------------------------------------------- /db/migrate/20190513232516_change_price_per_response_for_audiences.rb: -------------------------------------------------------------------------------- 1 | class ChangePricePerResponseForAudiences < ActiveRecord::Migration[5.1] 2 | def up 3 | remove_column :audiences, :price_per_response 4 | add_column :audiences, :price_per_response, :decimal, precision: 10, scale: 2 5 | end 6 | 7 | def down 8 | remove_column :audiences, :price_per_response 9 | add_column :audiences, :price_per_response, :float 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20200302212854_create_network_invitations.rb: -------------------------------------------------------------------------------- 1 | class CreateNetworkInvitations < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :network_invitations do |t| 4 | t.string :token 5 | t.bigint :user_id 6 | t.bigint :organization_id 7 | 8 | t.timestamps 9 | end 10 | 11 | add_index :network_invitations, :token 12 | add_index :network_invitations, %i[user_id organization_id] 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/javascript/ui/grid/covers/PaddedCardCover.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | import v from '~/utils/variables' 4 | 5 | const PaddedCardCover = styled.div` 6 | position: relative; 7 | z-index: ${v.zIndex.gridCardBg + 1}; 8 | padding: 1rem; 9 | 10 | .form { 11 | text-align: center; 12 | input { 13 | padding-top: 5px; 14 | margin-bottom: 10px; 15 | } 16 | } 17 | ` 18 | 19 | export default PaddedCardCover 20 | -------------------------------------------------------------------------------- /app/serializers/serializable_collection_filter.rb: -------------------------------------------------------------------------------- 1 | class SerializableCollectionFilter < BaseJsonSerializer 2 | type 'collection_filters' 3 | attributes( 4 | :text, 5 | :filter_type, 6 | ) 7 | 8 | attribute :selected do 9 | user_filter = UserCollectionFilter.find_by( 10 | user_id: @current_user.id, 11 | collection_filter_id: @object.id, 12 | ) 13 | user_filter.present? ? user_filter.selected : false 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/services/quill_schmoozer.rb: -------------------------------------------------------------------------------- 1 | require 'schmooze' 2 | 3 | class QuillSchmoozer < Schmooze::Base 4 | dependencies Delta: 'quill-delta' 5 | 6 | method :diff, 'function(data1, data2) { 7 | const delta1 = new Delta(data1) 8 | const delta2 = new Delta(data2) 9 | return delta1.diff(delta2) 10 | }' 11 | 12 | def self.instance 13 | new(__dir__) 14 | end 15 | 16 | def self.diff(*args) 17 | instance.diff(*args) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/workers/trial_expired_worker.rb: -------------------------------------------------------------------------------- 1 | class TrialExpiredWorker 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | Organization.billable.where( 6 | trial_expired_email_sent: false, 7 | ).where( 8 | 'trial_ends_at < ?', Time.current 9 | ).find_each do |organization| 10 | TrialExpiredMailer.notify(organization).deliver_later 11 | organization.update_attributes(trial_expired_email_sent: true) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /cypress/integration/collections/CreateTemplate.feature: -------------------------------------------------------------------------------- 1 | Feature: Template 2 | 3 | Scenario: Creating Template from the BCT 4 | Given I login and visit the Test Area 5 | When I create a template collection named "Test Template" using the first hot edge 6 | Then I should see "Test Template" in a "GridCard" 7 | When I navigate to the collection named "Test Template" via the "CollectionCover" 8 | Then I should see "#template" in a ".SubduedHeading1" 9 | -------------------------------------------------------------------------------- /db/migrate/20180523225540_create_groups_threads.rb: -------------------------------------------------------------------------------- 1 | class CreateGroupsThreads < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :groups_threads do |t| 4 | t.references :group, index: false 5 | t.references :comment_thread, index: false 6 | t.datetime :created_at, null: false 7 | end 8 | 9 | add_index :groups_threads, [:group_id, :comment_thread_id], unique: true, name: 'by_groups_comment_thread' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20190520215105_create_test_audience_invitations.rb: -------------------------------------------------------------------------------- 1 | class CreateTestAudienceInvitations < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :test_audience_invitations do |t| 4 | t.belongs_to :test_audience, foreign_key: true 5 | t.belongs_to :user, foreign_key: true 6 | t.string :invitation_token, unique: true 7 | t.datetime :completed_at, null: true 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/api/schemas/notification.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "read": { "type": "boolean" }, 5 | "created_at": { "type": "date" }, 6 | "combined_activities_ids": { "type": ["array", "[]"] }, 7 | "combined_actor_count": { "type": "integer" }, 8 | "activity_id": { "type": "integer" }, 9 | "user_id": { "type": "integer" }, 10 | "identifier": { "type": "string" } 11 | }, 12 | "required": [] 13 | } 14 | -------------------------------------------------------------------------------- /app/javascript/ui/billing/EmptyList.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import v from '~/utils/variables' 3 | 4 | const EmptyList = styled.div` 5 | font-family: Gotham; 6 | font-size: 1.5rem; 7 | line-height: auto; 8 | letter-spacing: 1px; 9 | color: #c6c1bf; 10 | margin: 2rem; 11 | 12 | @media only screen and (max-width: ${v.responsive.muiSmBreakpoint}px) { 13 | font-size: 1.25rem; 14 | } 15 | ` 16 | 17 | export default EmptyList 18 | -------------------------------------------------------------------------------- /app/javascript/ui/global/modals/AddCardModal.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import Modal from '~/ui/global/modals/Modal' 3 | 4 | const AddCardModal = ({ children, close }) => ( 5 | 6 | {children} 7 | 8 | ) 9 | 10 | AddCardModal.propTypes = { 11 | children: PropTypes.node.isRequired, 12 | close: PropTypes.func.isRequired, 13 | } 14 | 15 | export default AddCardModal 16 | -------------------------------------------------------------------------------- /bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= 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 | Webpacker::DevServerRunner.run(ARGV) 16 | -------------------------------------------------------------------------------- /db/migrate/20190117195055_add_defaults_and_indexes_to_cached_attributes.rb: -------------------------------------------------------------------------------- 1 | class AddDefaultsAndIndexesToCachedAttributes < ActiveRecord::Migration[5.1] 2 | def change 3 | change_column_default :collections, :cached_attributes, from: nil, to: {} 4 | change_column_default :items, :cached_attributes, from: nil, to: {} 5 | 6 | remove_index :roles, :resource_identifier 7 | add_index :roles, [:resource_identifier, :name], unique: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20190510215957_create_datasets.rb: -------------------------------------------------------------------------------- 1 | class CreateDatasets < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :datasets do |t| 4 | t.string :type, :name, :measure, :question_type, :url 5 | t.integer :chart_type, :max_domain, :timeframe, :total 6 | t.jsonb :cached_data, :style 7 | t.references :organization 8 | t.references :data_source, polymorphic: true 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/api_schema_matcher.rb: -------------------------------------------------------------------------------- 1 | # Inspired by Thoughtbot 2 | # https://robots.thoughtbot.com/validating-json-schemas-with-an-rspec-matcher 3 | RSpec::Matchers.define :match_json_schema do |schema_name, opts = { strict: true }| 4 | match do |json| 5 | schema_directory = File.join(Dir.pwd, 'spec', 'support', 'api', 'schemas') 6 | validator = JSONSchemaValidator.new(schema_directory) 7 | validator.validate!(schema_name, json, opts) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/SingleCrossIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const SingleCrossIcon = () => ( 4 | 5 | 6 | 14 | 15 | 16 | ) 17 | 18 | export default SingleCrossIcon 19 | -------------------------------------------------------------------------------- /app/javascript/vendor/cypress.js: -------------------------------------------------------------------------------- 1 | import { apiStore, uiStore, undoStore } from '~/stores' 2 | import captureGlobalKeypress from '~/utils/captureGlobalKeypress' 3 | 4 | const cypress = navigator && navigator.userAgent === 'cypress' 5 | if (cypress) { 6 | // expose on the window so that cypress has access 7 | window.apiStore = apiStore 8 | window.uiStore = uiStore 9 | window.undoStore = undoStore 10 | window.captureGlobalKeypress = captureGlobalKeypress 11 | } 12 | -------------------------------------------------------------------------------- /app/serializers/serializable_question_answer.rb: -------------------------------------------------------------------------------- 1 | class SerializableQuestionAnswer < BaseJsonSerializer 2 | type 'question_answers' 3 | attributes( 4 | :answer_text, 5 | :answer_number, 6 | ) 7 | 8 | stringified_attributes( 9 | :question_id, 10 | :idea_id, 11 | ) 12 | 13 | attribute :selected_choice_ids do 14 | @object.selected_choice_ids.map(&:to_s) 15 | end 16 | 17 | belongs_to :survey_response 18 | belongs_to :question 19 | end 20 | -------------------------------------------------------------------------------- /app/services/search/filters/base.rb: -------------------------------------------------------------------------------- 1 | class Search 2 | module Filters 3 | class Base 4 | def initialize(query) 5 | @query = query 6 | end 7 | 8 | def match? 9 | @query.match(self.class::REGEXP) 10 | end 11 | 12 | def remove_regexp 13 | @query.gsub(/#{self.class::REGEXP}\s*/, '') 14 | end 15 | 16 | def modify_query 17 | remove_regexp&.strip 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/services/text_item_highlighter.rb: -------------------------------------------------------------------------------- 1 | class TextItemHighlighter < SimpleService 2 | def initialize(item:, user:) 3 | @item = item 4 | @user = user 5 | end 6 | 7 | def call 8 | unless @item.can_edit?(@user) 9 | # this basically just validates that read-only users are only highlighting 10 | # and not changing the text content 11 | return false if @item.plain_content_changed? 12 | end 13 | 14 | @item.save 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/workers/notification_mailer_worker.rb: -------------------------------------------------------------------------------- 1 | require 'sidekiq-scheduler' 2 | 3 | class NotificationMailerWorker 4 | include Sidekiq::Worker 5 | 6 | def perform 7 | puts 'Sending notification emails' 8 | # timeframe will just look up users that have any notifications in the last 5 hours, 9 | # but the service will only look up the relevant new notifications 10 | NotificationDigest.call(type: :notifications, timeframe: 5.hours.ago) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20180426163602_add_guest_group_id_and_domains_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddGuestGroupIdAndDomainsToOrganizations < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :guest_group_id, :integer 4 | add_column :organizations, :domain_whitelist, :jsonb, default: [] 5 | 6 | # also remove these unused fields 7 | remove_index :organizations, :handle 8 | remove_column :organizations, :handle, :string 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/javascript/ui/global/PlainLink.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { Link } from 'react-router-dom' 3 | 4 | const PlainLink = styled(Link)` 5 | font-size: inherit; 6 | text-decoration: none; 7 | color: ${props => (props.color ? props.color : 'inherit')}; 8 | 9 | &.no-select { 10 | user-select: none; 11 | user-drag: none; 12 | } 13 | ` 14 | PlainLink.displayName = 'PlainLink' 15 | 16 | /** @component */ 17 | export default PlainLink 18 | -------------------------------------------------------------------------------- /app/services/firestore_client.rb: -------------------------------------------------------------------------------- 1 | require 'google/cloud/firestore' 2 | 3 | class FirestoreClient 4 | attr_reader :client 5 | 6 | def initialize 7 | @client = Google::Cloud::Firestore.new 8 | end 9 | 10 | def read(path) 11 | @client.doc(path) 12 | end 13 | 14 | def write(path, data) 15 | @client.transaction do |tx| 16 | tx.set(path, data) 17 | end 18 | end 19 | 20 | def self.client 21 | @client ||= new.client 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /db/migrate/20181105224054_add_charges_limit_flags_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddChargesLimitFlagsToOrganization < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :organizations, :sent_high_charges_low_email, :boolean, default: false, null: false 4 | add_column :organizations, :sent_high_charges_middle_email, :boolean, default: false, null: false 5 | add_column :organizations, :sent_high_charges_high_email, :boolean, default: false, null: false 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/applications.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :application do 3 | transient do 4 | add_orgs [] 5 | end 6 | name { Faker::Company.buzzword } 7 | 8 | after(:create) do |application, evaluator| 9 | if evaluator.add_orgs.present? 10 | evaluator.add_orgs.each do |org| 11 | create(:application_organization, application: application, organization: org) 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/javascript/ui/test_collections/GreetingMessage.js: -------------------------------------------------------------------------------- 1 | import { QuestionText } from '~/ui/test_collections/shared' 2 | import Emoji from '~/ui/icons/Emoji' 3 | 4 | const GreetingMessage = () => { 5 | return ( 6 |
7 | Hey there! 8 | 9 |
10 | ) 11 | } 12 | 13 | export default GreetingMessage 14 | -------------------------------------------------------------------------------- /app/workers/mark_as_overdue_worker.rb: -------------------------------------------------------------------------------- 1 | class MarkAsOverdueWorker 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | Organization.billable.where( 6 | overdue_at: nil, 7 | has_payment_method: false, 8 | ).where( 9 | '"organizations".active_users_count > "organizations".trial_users_count OR "organizations".trial_ends_at < ?', Time.current 10 | ).find_each do |organization| 11 | organization.touch(:overdue_at) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20190606222127_create_payments.rb: -------------------------------------------------------------------------------- 1 | class CreatePayments < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :payments do |t| 4 | t.text :description 5 | t.decimal :amount, :unit_amount, precision: 10, scale: 2 6 | t.integer :quantity, :network_payment_id, :network_payment_method_id 7 | t.references :user, :organization 8 | t.references :purchasable, polymorphic: true 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /script/dev-setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd "$(dirname "$0")/.." 6 | DIR=$(pwd) 7 | 8 | git submodule init 9 | git submodule update 10 | 11 | if [ ! -f "$DIR/.env" ]; then 12 | echo "Missing env file $DIR/.env !" 13 | exit 1 14 | fi 15 | 16 | yarn install 17 | bundle install 18 | # setup runs db:create, db:schema:load and db:seed 19 | bin/rails db:setup 20 | # install git hooks 21 | bundle exec lefthook install 22 | script/override-post-checkout-hook 23 | -------------------------------------------------------------------------------- /spec/workers/daily_organization_usage_worker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe DailyOrganizationUsageWorker, type: :worker do 4 | let!(:organizations) { create_list(:organization_without_groups, 2) } 5 | 6 | describe '#perform' do 7 | it 'queues up NetworkCreateUsageRecordWorkers' do 8 | expect(NetworkCreateUsageRecordWorker).to receive(:perform_async).twice 9 | DailyOrganizationUsageWorker.new.perform 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/workers/survey_response_completion_worker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe SurveyResponseCompletionWorker, type: :worker do 4 | describe '#perform' do 5 | let!(:survey_response) { create(:survey_response) } 6 | it 'calls SurveyResponseCompletion service' do 7 | expect(SurveyResponseCompletion).to receive(:call).with(survey_response) 8 | SurveyResponseCompletionWorker.new.perform(survey_response.id) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/javascript/utils/paramUtils.js: -------------------------------------------------------------------------------- 1 | import queryString from 'query-string' 2 | 3 | // checks if key and value exists within param string 4 | const hasKeyValueParam = (paramString, key, value) => { 5 | if (!paramString) { 6 | return false 7 | } 8 | const params = queryString.parse(paramString) 9 | if (!params) { 10 | return false 11 | } 12 | if (params[key] !== value) { 13 | return false 14 | } 15 | return true 16 | } 17 | 18 | export { hasKeyValueParam } 19 | -------------------------------------------------------------------------------- /app/serializers/serializable_simple_user.rb: -------------------------------------------------------------------------------- 1 | # Note: any user of an organization can load this data for all users in the organization 2 | # So we don't want to expose any personal details such as email 3 | 4 | class SerializableSimpleUser < BaseJsonSerializer 5 | type 'users' 6 | attributes :first_name, :last_name, :handle 7 | 8 | attribute :pic_url_square do 9 | @object.picture || 'https://s3-us-west-2.amazonaws.com/assets.shape.space/user-avatar.png' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20190508180048_create_feedback_incentive_records.rb: -------------------------------------------------------------------------------- 1 | class CreateFeedbackIncentiveRecords < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :feedback_incentive_records do |t| 4 | t.belongs_to :user, foreign_key: true 5 | t.belongs_to :survey_response, foreign_key: true 6 | t.decimal :amount, precision: 10, scale: 2 7 | t.decimal :current_balance, precision: 10, scale: 2 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20200608220051_add_type_to_audiences.rb: -------------------------------------------------------------------------------- 1 | class AddTypeToAudiences < ActiveRecord::Migration[5.2] 2 | def up 3 | add_column :audiences, :audience_type, :integer, null: true 4 | 5 | # if you already seeded audiences 6 | if Object.const_defined?('Audience') 7 | Audience.where(name: %i[Admins Reviewers Participants]).update_all(audience_type: 0) 8 | end 9 | end 10 | 11 | def down 12 | remove_column :audiences, :audience_type 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/lib/url_exists.rb: -------------------------------------------------------------------------------- 1 | class UrlExists 2 | def initialize(url) 3 | @url = url 4 | end 5 | 6 | def call 7 | return false if @url.blank? 8 | 9 | success_codes.include?(get_url_code) 10 | end 11 | 12 | private 13 | 14 | def get_url_code 15 | # After tons of issues with net/http, 16 | # HTTParty is much more reliable 17 | response = HTTParty.get(@url) 18 | response.code 19 | end 20 | 21 | def success_codes 22 | [200, 304] 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/workers/create_activity_notifications_worker.rb: -------------------------------------------------------------------------------- 1 | class CreateActivityNotificationsWorker 2 | include Sidekiq::Worker 3 | 4 | def perform( 5 | activity_id, 6 | omit_user_ids, 7 | omit_group_ids, 8 | combine 9 | ) 10 | @activity = Activity.find(activity_id) 11 | NotificationBuilder.call( 12 | activity: @activity, 13 | omit_user_ids: omit_user_ids, 14 | omit_group_ids: omit_group_ids, 15 | combine: combine, 16 | ) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/app_metric.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: app_metrics 4 | # 5 | # id :bigint(8) not null, primary key 6 | # metric :string 7 | # value :float 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | # Indexes 12 | # 13 | # index_app_metrics_on_metric_and_created_at (metric,created_at) 14 | # 15 | 16 | class AppMetric < ApplicationRecord 17 | validates :metric, :value, presence: true 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20190201191608_create_api_tokens.rb: -------------------------------------------------------------------------------- 1 | class CreateApiTokens < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :api_tokens do |t| 4 | t.text :token, index: true 5 | t.references :application, :organization, :created_by, 6 | index: false 7 | t.timestamps 8 | end 9 | 10 | add_index :api_tokens, 11 | [:application_id, :organization_id], 12 | name: 'index_api_tokens_on_app_id_org_id' 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/invitations_controller.rb: -------------------------------------------------------------------------------- 1 | class InvitationsController < ApplicationController 2 | def accept 3 | token = params.require(:token) 4 | invitation = NetworkInvitation.find_by_token(token) 5 | user = invitation&.user 6 | if user.present? && user.pending? 7 | redirect_to sign_up_url(email: user.email, token: token, redirect: params[:redirect]) 8 | else 9 | redirect_to login_url(email: user&.email, redirect: params[:redirect]) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/models/groups_thread.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: groups_threads 4 | # 5 | # id :bigint(8) not null, primary key 6 | # created_at :datetime not null 7 | # comment_thread_id :bigint(8) 8 | # group_id :bigint(8) 9 | # 10 | # Indexes 11 | # 12 | # by_groups_comment_thread (group_id,comment_thread_id) UNIQUE 13 | # 14 | 15 | class GroupsThread < ApplicationRecord 16 | belongs_to :group 17 | belongs_to :comment_thread 18 | end 19 | -------------------------------------------------------------------------------- /app/serializers/serializable_activity.rb: -------------------------------------------------------------------------------- 1 | class SerializableActivity < BaseJsonSerializer 2 | type 'activities' 3 | attributes :action, :created_at, :content, :actor_id, :target_type, :target_id 4 | attribute :source_name do 5 | if @object.source.present? && @object.source.respond_to?(:name) 6 | @object.source.try(:name) 7 | else 8 | '' 9 | end 10 | end 11 | belongs_to :actor 12 | has_many :subject_users 13 | has_many :subject_groups 14 | belongs_to :target 15 | end 16 | -------------------------------------------------------------------------------- /config/initializers/rack_cors.rb: -------------------------------------------------------------------------------- 1 | # Configure Rack CORS Middleware, so that CloudFront can serve our assets. 2 | # See https://github.com/cyu/rack-cors 3 | # adapted from https://stackoverflow.com/a/36585871/260495 4 | if defined? Rack::Cors 5 | Rails.configuration.middleware.insert_before 0, Rack::Cors do 6 | allow do 7 | origins [ 8 | %r{^https?://[a-zA-Z0-9\-]*\.shape\.space$}, 9 | ] 10 | resource '/assets/*' 11 | resource '/packs/*' 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /__tests__/ui/grid/FoamcoreZoomControls.unit.test.js: -------------------------------------------------------------------------------- 1 | import FoamcoreZoomControls from '~/ui/grid/FoamcoreZoomControls' 2 | 3 | let props, wrapper 4 | 5 | describe('FoamcoreZoomControls', () => { 6 | beforeEach(() => { 7 | props = { 8 | onZoomIn: jest.fn(), 9 | onZoomOut: jest.fn(), 10 | } 11 | wrapper = shallow() 12 | }) 13 | 14 | it('renders snapshot', () => { 15 | expect(wrapper.find('ZoomIconWrapper').exists()).toBe(true) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/htc/TextIcon.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import Icon from '../Icon' 3 | 4 | const TextIcon = props => ( 5 | 6 | 7 | 8 | 9 | 10 | ) 11 | TextIcon.propTypes = { 12 | viewBox: PropTypes.string, 13 | } 14 | TextIcon.defaultProps = { 15 | viewBox: '0 0 31 34', 16 | } 17 | 18 | export default TextIcon 19 | -------------------------------------------------------------------------------- /app/models/concerns/has_activities.rb: -------------------------------------------------------------------------------- 1 | module HasActivities 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | has_many :activities, 6 | as: :target, 7 | class_name: 'Activity', 8 | dependent: :destroy 9 | end 10 | 11 | def cache_activity_count! 12 | return unless respond_to?(:cached_activity_count) 13 | 14 | cache_attribute!( 15 | :cached_activity_count, 16 | activities.count, 17 | touch: false, 18 | ) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/serializers/serializable_simple_group.rb: -------------------------------------------------------------------------------- 1 | class SerializableSimpleGroup < BaseJsonSerializer 2 | type 'groups' 3 | attributes :name, :handle 4 | attribute :is_primary do 5 | @object.primary? 6 | end 7 | attribute :is_guest do 8 | @object.guest? 9 | end 10 | attribute :filestack_file_url do 11 | if @object.filestack_file_url.present? 12 | @object.filestack_file_url 13 | else 14 | 'https://cdn.filestackcontent.com/i4iKADquTQCWMAvyz02R' 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/serializers/serializable_users_thread.rb: -------------------------------------------------------------------------------- 1 | class SerializableUsersThread < BaseJsonSerializer 2 | type 'users_threads' 3 | attributes :last_viewed_at, :updated_at, 4 | # just store these as attributes, don't need the whole relation 5 | :comment_thread_id, :user_id, :subscribed 6 | # this is a calculated value from UsersThread 7 | attribute :unread_count 8 | 9 | attribute :identifier do 10 | "#{@object.comment_thread.organization_id}_#{@object.user_id}" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/javascript/ui/global/Moment.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import v from '~/utils/variables' 3 | 4 | import { defaultTimeFormat } from '~/utils/time' 5 | 6 | const StyledDate = styled.span` 7 | color: ${props => props.color}; 8 | display: inline-block; 9 | font-family: ${v.fonts.sans}; 10 | font-size: 0.75rem; 11 | white-space: nowrap; 12 | ` 13 | 14 | const Moment = ({ date } = {}) => ( 15 | {defaultTimeFormat(date)} 16 | ) 17 | 18 | export default Moment 19 | -------------------------------------------------------------------------------- /app/views/layouts/_metatags_and_favicons.html.erb: -------------------------------------------------------------------------------- 1 | <%= csrf_meta_tags %> 2 | <%= action_cable_meta_tag %> 3 | 4 | 5 | <%= favicon_link_tag "favicons/apple-touch-icon.png", rel: "apple-touch-icon", sizes: "120x120" %> 6 | <%= favicon_link_tag "favicons/favicon-32x32.png", rel: "icon", sizes: "32x32", type: "image/png" %> 7 | <%= favicon_link_tag "favicons/favicon-16x16.png", rel: "icon", sizes: "16x16", type: "image/png" %> 8 | -------------------------------------------------------------------------------- /db/migrate/20190729171554_update_user_terms_accepted.rb: -------------------------------------------------------------------------------- 1 | class UpdateUserTermsAccepted < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :users, :terms_accepted_data, :jsonb, default: {} 4 | 5 | # temporary rename until we drop these columns 6 | rename_column :users, :terms_accepted, :old_terms_accepted 7 | rename_column :users, :feedback_terms_accepted, :old_feedback_terms_accepted 8 | rename_column :users, :respondent_terms_accepted, :old_respondent_terms_accepted 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20180329055510_add_type_and_remove_reference_from_collection_cards.rb: -------------------------------------------------------------------------------- 1 | class AddTypeAndRemoveReferenceFromCollectionCards < ActiveRecord::Migration[5.1] 2 | def up 3 | add_column :collection_cards, :type, :string 4 | remove_column :collection_cards, :reference 5 | 6 | CollectionCard.update_all(type: 'CollectionCard::Primary') 7 | end 8 | 9 | def down 10 | remove_column :collection_cards, :type 11 | add_column :collection_cards, :reference, :boolean, default: false 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/services/text_messenger_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe TextMessenger, type: :service do 4 | let(:phone_number) { '1234567890' } 5 | 6 | describe '#call' do 7 | let(:text_messenger) do 8 | TextMessenger.new( 9 | message: 'hello', 10 | phone_number: phone_number, 11 | ) 12 | end 13 | 14 | it 'should initialize the twilio client' do 15 | expect(Twilio::REST::Client).to receive(:new) 16 | text_messenger.call 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | after_subscribe :track_user 4 | after_unsubscribe :untrack_user 5 | delegate :current_ability, to: :connection 6 | protected :current_ability 7 | 8 | def track_user 9 | ChannelPresenceTracker.track(self.class.name, current_user.id) 10 | end 11 | 12 | def untrack_user 13 | ChannelPresenceTracker.untrack(self.class.name, current_user.id) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/javascript/ui/global/ColorSquare.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import styled from 'styled-components' 3 | 4 | const Square = styled.div` 5 | background-color: ${props => props.color || '#ffffff'}; 6 | display: block; 7 | height: 16px; 8 | width: 16px; 9 | ` 10 | 11 | const ColorSquare = props => 12 | 13 | ColorSquare.propTypes = { 14 | color: PropTypes.string, 15 | } 16 | ColorSquare.defaultProps = { 17 | color: '#ffffff', 18 | } 19 | 20 | export default ColorSquare 21 | -------------------------------------------------------------------------------- /app/javascript/utils/time.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment-mini' 2 | 3 | function defaultFormat(time) { 4 | const now = moment() 5 | const m = moment(time) 6 | if (now.diff(m, 'h') < 24) { 7 | // 4:32pm 8 | return 'LT' 9 | } else if (now.diff(m, 'h') >= 24 && now.diff(m, 'days') < 365) { 10 | // Nov 28th 11 | return 'MMM Do' 12 | } 13 | // Nov 28th, 2017 14 | return 'MMM Do, YYYY' 15 | } 16 | 17 | export function defaultTimeFormat(time) { 18 | return moment(time).format(defaultFormat(time)) 19 | } 20 | -------------------------------------------------------------------------------- /cypress/integration/admin/NewAudienceQuery.feature: -------------------------------------------------------------------------------- 1 | Feature: New Audience Query in Admin 2 | 3 | Scenario: Viewing an audience's criteria 4 | Given I login and visit the Admin area 5 | Then I should see "All Shape Feedback" in a "AdminHeader" 6 | 7 | When I click the new query button for the first audience 8 | Then I should see a "NewQueryCountTextField" 9 | 10 | When I type "25" in "NewQueryCountTextField" 11 | When I blur "NewQueryCountTextField" 12 | Then I should see the "Query the INA" modal 13 | -------------------------------------------------------------------------------- /spec/factories/payments.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :payment do 3 | user 4 | organization 5 | amount 99.95 6 | unit_amount 10 7 | description { Faker::Hipster.sentence } 8 | network_payment_method_id { rand(10_000..100_000) } 9 | 10 | after(:build) do |payment| 11 | payment.purchasable ||= create(:test_audience, test_collection: create(:test_collection)) 12 | end 13 | 14 | trait :paid do 15 | network_payment_id { rand(10_000..100_000) } 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/javascript/ui/test_collections/PaperAirplane.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import AirplaneImg from '~/assets/paper-airplane.png' 3 | 4 | const PaperAirplaneWrapper = styled.div` 5 | display: flex; 6 | justify-content: center; /* align horizontal */ 7 | align-items: center; 8 | ` 9 | 10 | const PaperAirplane = () => ( 11 | 12 | paper airplane 13 | 14 | ) 15 | 16 | export default PaperAirplane 17 | -------------------------------------------------------------------------------- /app/workers/mailing_list_subscription_worker.rb: -------------------------------------------------------------------------------- 1 | class MailingListSubscriptionWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(user_id, list, subscribe) 5 | user = User.find(user_id) 6 | 7 | MailingListSubscription.call( 8 | user: user, 9 | list: list, 10 | subscribe: subscribe, 11 | ) 12 | rescue ActiveRecord::RecordNotFound 13 | # likely in dev / cypress task where the user was already destroyed 14 | logger.debug 'MailingListSubscriptionWorker: user not found.' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20180207202630_rolify_create_roles.rb: -------------------------------------------------------------------------------- 1 | class RolifyCreateRoles < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table(:roles) do |t| 4 | t.string :name 5 | t.references :resource, polymorphic: true 6 | t.timestamps 7 | end 8 | 9 | create_table(:users_roles) do |t| 10 | t.references :user 11 | t.references :role 12 | end 13 | 14 | add_index(:roles, [ :name, :resource_type, :resource_id ]) 15 | add_index(:users_roles, [ :user_id, :role_id ]) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20190207083622_create_external_records.rb: -------------------------------------------------------------------------------- 1 | class CreateExternalRecords < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :external_records do |t| 4 | t.string :external_id 5 | t.belongs_to :application 6 | t.references :externalizable, polymorphic: true, index: { name: 'index_on_externalizable' } 7 | 8 | t.timestamps 9 | end 10 | 11 | add_index :external_records, %i[external_id application_id externalizable_type], unique: true, name: 'index_uniq_external_id' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/workers/searchkick_reindex_worker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe SearchkickReindexWorker, type: :worker do 4 | describe '#perform' do 5 | it 'calls Searchkick::ProcessQueueJob' do 6 | expect(Searchkick::ProcessQueueJob).to receive(:perform_later).with( 7 | class_name: 'Collection', 8 | ) 9 | expect(Searchkick::ProcessQueueJob).to receive(:perform_later).with( 10 | class_name: 'Item', 11 | ) 12 | SearchkickReindexWorker.new.perform 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20180319231721_add_filestack_file_to_groups_and_orgs.rb: -------------------------------------------------------------------------------- 1 | class AddFilestackFileToGroupsAndOrgs < ActiveRecord::Migration[5.1] 2 | def up 3 | remove_column :organizations, :pic_url_square 4 | 5 | add_column :groups, :filestack_file_id, :integer 6 | add_column :organizations, :filestack_file_id, :integer 7 | end 8 | 9 | def down 10 | add_column :organizations, :pic_url_square, :string 11 | 12 | remove_column :groups, :filestack_file_id 13 | remove_column :organizations, :filestack_file_id 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | 4 | def jsonapi_cache_key 5 | [ 6 | self.class.base_class.name, 7 | id, 8 | (updated_at || created_at).to_f, 9 | ].compact.join('_') 10 | end 11 | 12 | # default to false, can be overridden by classes 13 | def searchable? 14 | false 15 | end 16 | 17 | def skip_network_actions? 18 | Rails.env.development? || ENV['CYPRESS'].present? || ENV['SKIP_NETWORK_ACTIONS'].present? 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | # see: 2 | # https://github.com/rails/rails/issues/32700 3 | # and: 4 | # https://github.com/jonleighton/spring-watcher-listen/issues/15 5 | 6 | Spring::Watcher::Listen.class_eval do 7 | def base_directories 8 | %w[app lib config test spec] 9 | .uniq.map { |path| Pathname.new(File.join(root, path)) } 10 | end 11 | end 12 | 13 | %w[ 14 | .ruby-version 15 | .rbenv-vars 16 | .env 17 | Gemfile.lock 18 | Procfile.development 19 | tmp/restart.txt 20 | tmp/caching-dev.txt 21 | ].each { |path| Spring.watch(path) } 22 | -------------------------------------------------------------------------------- /db/migrate/20190513203005_create_data_items_datasets.rb: -------------------------------------------------------------------------------- 1 | class CreateDataItemsDatasets < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :data_items_datasets do |t| 4 | t.references :data_item, :dataset, index: false 5 | t.integer :order, null: false 6 | t.boolean :selected, default: true 7 | t.timestamps 8 | end 9 | 10 | add_index :data_items_datasets, 11 | [:data_item_id, :dataset_id, :order, :selected], 12 | name: 'data_items_datasets_aggregate_index' 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/javascript/ui/global/HoverableDescriptionIcon.md: -------------------------------------------------------------------------------- 1 | The Hoverable Description Icon is a an info icon that shows a description when hovering 2 | 3 | ### Default Hoverable Description Icon 4 | 5 | ```jsx padded 6 | import { StyleguideHolder } from '../../ui/global/styled/layout' 7 | import Button from '../../ui/global/Button' 8 | ; 9 | 14 | 15 | ``` 16 | -------------------------------------------------------------------------------- /app/javascript/ui/icons/OkIcon.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | const OkIcon = () => ( 4 | 5 | 6 | 7 | 8 | 9 | ) 10 | 11 | export default OkIcon 12 | -------------------------------------------------------------------------------- /db/migrate/20190201202932_create_application_organizations.rb: -------------------------------------------------------------------------------- 1 | class CreateApplicationOrganizations < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :application_organizations do |t| 4 | t.references :application, :organization, :root_collection, 5 | index: false 6 | t.timestamps 7 | end 8 | 9 | add_index :application_organizations, 10 | [:application_id, :organization_id], 11 | name: 'index_app_org_on_app_id_org_id', 12 | unique: true 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/workers/update_template_instances_worker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe UpdateTemplateInstancesWorker, type: :worker do 4 | describe '#perform' do 5 | let(:master_template) { create(:collection, master_template: true, num_cards: 3) } 6 | 7 | it 'calls #update_template_instances' do 8 | expect_any_instance_of(TemplateInstanceUpdater).to receive(:call) 9 | UpdateTemplateInstancesWorker.new.perform(master_template.id, master_template.collection_cards.pluck(:id), :update_all) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /__tests__/ui/grid/covers/CollectionCoverTitle.unit.test.js: -------------------------------------------------------------------------------- 1 | import CollectionCoverTitle from '~/ui/grid/covers/CollectionCoverTitle' 2 | import { fakeCollection } from '#/mocks/data' 3 | 4 | const props = { 5 | collection: fakeCollection, 6 | title: 'Hello Dolly', 7 | } 8 | let wrapper 9 | 10 | describe('CollectionCoverTitle', () => { 11 | beforeEach(() => { 12 | wrapper = shallow() 13 | }) 14 | 15 | it('renders the collection name', () => { 16 | expect(wrapper.text()).toContain(props.title) 17 | }) 18 | }) 19 | --------------------------------------------------------------------------------