diff --git a/app/controllers/concerns/resource_methods.rb b/app/controllers/concerns/resource_methods.rb index 840981ee6..62df5a612 100644 --- a/app/controllers/concerns/resource_methods.rb +++ b/app/controllers/concerns/resource_methods.rb @@ -320,6 +320,9 @@ def query_part(filter:) if array_col?(col_name: col) col_table = array_table(col_name: col) end + if derived_col?(col_name: col) + col_table = derived_table(col_name: col) + end part = get_query_part(table: col_table, column: col, operation: operation, value: value, top: true, key: key) if (key.include?('responses.')) @@ -450,6 +453,8 @@ def translate_operator(operation:) :not_in when 'equals' :eq + when 'equal' + :eq when '=' :eq when 'does not equal' @@ -459,12 +464,20 @@ def translate_operator(operation:) :not_eq when '!=' :not_eq + when 'is less than' + :lt when '<' :lt + when 'is greater than' + :gt when '>' :gt + when 'is less than or equal to' + :lteq when '<=' :lteq + when 'is greater than or equal to' + :gteq when '>=' :gteq when 'is' diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb index d5665b588..79a9586e7 100644 --- a/app/controllers/people_controller.rb +++ b/app/controllers/people_controller.rb @@ -453,28 +453,38 @@ def approval_query(table:, column:, operation:, value:, label:) end end + def derived_col?(col_name:) + return true if col_name == 'session_count' + false + end - # TODO: on create must have at least one email_addresses_attributes - # def join_tables - # joins = nil - # - # # TODO: check if the filter references to session_assignments.person_id - # if @filters - # people = Arel::Table.new(Person.table_name) - # assignments = Arel::Table.new(SessionAssignment.table_name) - # joins = [ - # people.create_join( - # assignments, - # people.create_on( - # people[:id].eq(assignments[:person_id]) - # ), - # Arel::Nodes::OuterJoin - # ) - # ] - # end - # - # joins - # end + def derived_table(col_name:) + return Arel::Table.new('session_counts') if col_name == 'session_count' + false + end + + def select_fields + Person.select( + ::Person.arel_table[Arel.star], + 'session_counts.session_count' + ) + end + + def join_tables + people = Person.arel_table #Arel::Table.new(Session.table_name) + + session_counts = Person.session_counts.as('session_counts') + joins = [ + people.create_join( + session_counts, + people.create_on( + session_counts[:person_id].eq(people[:id]) + ) + ) + ] + + joins + end def allowed_params %i[ diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index 203c281a2..5469175a7 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -438,7 +438,8 @@ def participant_availabilities person.published_name, person.con_state, person.attendance_type, - person.availabilities.order('start_time').collect{|av| "#{av.start_time} to #{av.end_time}" }.join(";\n"), + # TODO: format time .... + person.availabilities.order('start_time').collect{|av| "#{av.start_time.strftime('%Y-%m-%d %H:%M %Z')} to #{av.end_time.strftime('%Y-%m-%d %H:%M %Z')}" }.join(";\n"), person.session_limits.order('day').collect{|l| "#{l.day ? l.day : 'Global'}: #{l.max_sessions}" }.join(";\n"), person.exclusions.collect{|e| "#{e.title}"}.join(";\n"), person.availability_notes diff --git a/app/controllers/schedule_controller.rb b/app/controllers/schedule_controller.rb new file mode 100644 index 000000000..02d7fd031 --- /dev/null +++ b/app/controllers/schedule_controller.rb @@ -0,0 +1,24 @@ +# Produce a schedule suitable for Conclar +class ScheduleController < ApplicationController + skip_before_action :check_up, :authenticate_person!, only: [:index, :participants] + + def index + sessions = ReportsService.scheduled_sessions + + render json: ActiveModel::Serializer::CollectionSerializer.new( + sessions, + serializer: Conclar::SessionSerializer + ), + content_type: 'application/json' + end + + def participants + participants = ReportsService.scheduled_people + + render json: ActiveModel::Serializer::CollectionSerializer.new( + participants, + serializer: Conclar::ParticipantSerializer + ), + content_type: 'application/json' + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index ee162f40f..3da8736ed 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -143,7 +143,8 @@ def before_update # if time or room have changed removed ignored conflicts p = _permitted_params(model: object_name, instance: @object) if (@object.start_time || @object.room_id) - if p[:start_time] != @object.start_time || p[:room_id] != @object.room_id + if (p.has_key?('start_time') && p['start_time'] != @object.start_time) || + (p.has_key?('room_id') && p['room_id'] != @object.room_id) # so we remove any ignore conflicts for this session cids = @object.ignored_session_conflicts.pluck(:conflict_id) cids += @object.ignored_conflict_sessions.pluck(:conflict_id) diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index a5d35e76c..2abf311b3 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -27,6 +27,8 @@ def index end settings = { + # + env: Rails.env, # 1. model information enums: enums, # 2. con wide role types for assignments diff --git a/app/javascript/components/email_addresses_editor.vue b/app/javascript/components/email_addresses_editor.vue index b18f7ba89..47a5adcc5 100644 --- a/app/javascript/components/email_addresses_editor.vue +++ b/app/javascript/components/email_addresses_editor.vue @@ -33,6 +33,10 @@ + +

You are about to change the primary email address associated with this profile. This will change the login email used for this account.

+

Are you sure you wish to make this change?

+
@@ -42,11 +46,13 @@ import EmailAddressEditor from './email_address_editor.vue' import emailAddressMixin from '../store/email_address.mixin' import {personSessionMixin} from '@/mixins'; import modelUtilsMixin from "@/store/model_utils.mixin"; +import PlanoModal from './plano_modal.vue'; export default { name: 'EmailAddressesEditor', components: { - EmailAddressEditor + EmailAddressEditor, + PlanoModal }, mixins: [ modelUtilsMixin, @@ -68,12 +74,11 @@ export default { default: 'email-addresses-editor' } }, - data() { - return { - emails: [], - additional: [] - } - }, + data: () => ({ + emails: [], + additional: [], + pendingPrimaryChange: null + }), computed: { primary: { get: function() { @@ -102,7 +107,27 @@ export default { ) } }, + onConfirmCancel() { + this.setLists() + this.pendingPrimaryChange = null; + }, + onConfirmOk() { + this.saveEmail(this.pendingPrimaryChange).then( + () => { + this.setLists() + } + ).catch((err) => { + console.log("i caught an error", err) + this.setLists() + }); + this.pendingPrimaryChange = null; + }, onInput(arg) { + if(arg.isdefault) { + this.$bvModal.show('primaryEmailConfirm') + this.pendingPrimaryChange = arg; + return; + } if (arg.id) { this.saveEmail(arg).then( () => { diff --git a/app/javascript/constants/strings.js b/app/javascript/constants/strings.js index c64b68cc5..e9b9efca6 100644 --- a/app/javascript/constants/strings.js +++ b/app/javascript/constants/strings.js @@ -237,10 +237,23 @@ module.exports = { can_record_exceptions: "Recordings excluded topics", name: "Name", pseudonym: "Pseudonym", - languages_fluent_in: "Languages spoken" + languages_fluent_in: "Languages spoken", + can_share: "Permission to share email with other Participants" }, PERSON_SAVE_SUCCESS: "Profile record saved successfully", PERSON_NEVER_LOGGED_IN: "Never logged in", + PERSON_CON_STATE: { + not_set: "Not Set", + applied: "Applied", + vetted: "Vetted", + wait_list: "Wait List", + invite_pending: "Invite Pending", + invited: "Invited", + probable: "Probable", + accepted: "Accepted", + declined: "Declined", + rejected: "Rejected" + }, SURVEY_REDIRECT: "Unfortunately due to the browser refreshing we have lost any answers you filled in. Please fill the survey out again.", SURVEY_PUBLIC_NO_EDIT: "You cannot edit a published survey. Close the survey to enable editing.", @@ -269,7 +282,7 @@ module.exports = { SCHEDULE_DRAFT_CONFIRM_MESSAGE: "This will publish a Draft Schedule to all participants, who will see their own sessions. This action is irreversible and will bring the server down for a short time. Please double check that you wish to perform this action.", SCHEDULE_FIRM_CONFIRM_MESSAGE: "This will publish a Firm Schedule to all participants, who will see their own sessions - live. This action is irreversible. Please double check that you wish to perform this action.", SCHEDULE_DRAFT_SUCCESS_MESSAGE: "Draft schedule has been published successfully", - SCHEDULE_FIRM_SUCCESS_MESSAGE: "Firm schedule has been published successfully", + SCHEDULE_FIRM_SUCCESS_MESSAGE: "Firm schedule has been published successfully", SCHEDULE_APPROVAL_FAIL_TO_LOAD: "Couldn't load the approval form. Try again soon.", // The below is intended to become a way to override defaults in the model mixin easily. Hasn't happened yet though. SPECIFIC_MODEL_SAVE_SUCCESS: { diff --git a/app/javascript/dashboard/dashboard.vue b/app/javascript/dashboard/dashboard.vue index 5761b641b..9072343e5 100644 --- a/app/javascript/dashboard/dashboard.vue +++ b/app/javascript/dashboard/dashboard.vue @@ -1,151 +1,151 @@ diff --git a/app/javascript/people/people.js b/app/javascript/people/people.js index 168c8adef..9c1147437 100644 --- a/app/javascript/people/people.js +++ b/app/javascript/people/people.js @@ -1,3 +1,4 @@ +import {PERSON_CON_STATE, SESSION_STATUS} from "@/constants/strings"; import { personScheduleApprovalStateOptionsForSearch } from "@/store/person_schedule_approval"; export const people_columns = [ @@ -34,27 +35,25 @@ export const people_columns = [ search_key: 'con_state', label: 'Status', type: "select", - // TODO: needs to be driven by settings enums + formatter: (value) => PERSON_CON_STATE[value] || value, choices: [ - {label: "not_set", value: "not_set"}, - {label: "applied", value: "applied"}, - {label: "vetted", value: "vetted"}, - {label: "wait_list", value: "wait_list"}, - {label: "invite_pending", value: "invite_pending"}, - {label: "invited", value: "invited"}, - {label: "probable", value: "probable"}, - {label: "accepted", value: "accepted"}, - {label: "declined", value: "declined"}, - {label: "rejected", value: "rejected"} - ], - operators: ["equal", "does not equal"], + "not_set", "applied", "vetted", "wait_list", "invite_pending", + "invited", "probable", "accepted", "declined", "rejected" + ].map(value => ({label: PERSON_CON_STATE[value], value})), + operators: ["equals", "does not equal"], sortable: false }, { key: 'attendance_type', label: 'Attendance Type' }, - // TODO: derived cols + { + key: 'session_count', + search_key: 'session_count', + label: 'Session Count', + type: "numeric", + operators: ["equals", "does not equal", "is less than", "is less than or equal to", "is greater than", "is greater than or equal to"] + }, { key: 'draft_approval', label: 'Draft Approved', @@ -81,38 +80,6 @@ export const people_columns = [ label: 'Firm Comments', sortable: false }, - // { - // key: 'organization', - // label: 'Organization', - // type: "text", - // sortable: true - // }, - // { - // key: 'job_title', - // label: 'Job Title', - // type: "text", - // sortable: false - // }, - // { - // key: 'registered', - // label: 'Registered', - // type: "radio", - // // TODO: how do we add dynamic choices??? - // choices: [{label: "Yes", value: "true"}, {label: "No", value: "false"}], - // sortable: true - // }, - // { - // key: 'registration_type', - // label: 'Registration Type', - // type: "text", - // sortable: true - // }, - // { - // key: 'registration_number', - // label: 'Registration Number', - // type: "text", - // sortable: true - // }, { key: 'gender', label: 'Gender', diff --git a/app/javascript/people/people_admin_tab.vue b/app/javascript/people/people_admin_tab.vue index a1ef304ec..31f519b03 100644 --- a/app/javascript/people/people_admin_tab.vue +++ b/app/javascript/people/people_admin_tab.vue @@ -1,18 +1,20 @@ @@ -20,6 +22,7 @@ import { makeSelectedFieldMixin } from '@/mixins' import { modelMixinNoProp } from '@/store/model.mixin'; import { personModel as model } from '@/store/person.store'; +import { PERSON_CON_STATE } from '@/constants/strings'; const commentsMixin = makeSelectedFieldMixin('comments'); export default { @@ -29,8 +32,14 @@ export default { commentsMixin ], data: () => ({ - model - }) + model, + PERSON_CON_STATE + }), + computed: { + conventionClasses() { + return (Object.values(this.selected.convention_roles) || []).map(r => r.role[0].toUpperCase() + r.role.substring(1)).join(', ') + } + } } diff --git a/app/javascript/people/people_table.vue b/app/javascript/people/people_table.vue index af6eb31f4..4af0e13fa 100644 --- a/app/javascript/people/people_table.vue +++ b/app/javascript/people/people_table.vue @@ -98,11 +98,10 @@