Class: DailyFocusController
- Inherits:
-
CrmController
- Object
- ActionController::Base
- ApplicationController
- CrmController
- DailyFocusController
- Defined in:
- app/controllers/daily_focus_controller.rb
Constant Summary collapse
- DAILY_FOCUS_TOOL_SERVICES =
DailyFocus::TOOL_SERVICE_KEYS
- DAILY_FOCUS_TOOL_HANDLES =
%w[sales-management app-db support-cases content-search].freeze
- WORK_DAY_INDICATOR_UNSET =
Object.new.freeze
- DAILY_FOCUS_BRIEFING_ACTIVE_STATUSES =
Row statuses that mean this rep has (or shares) a Daily Focus artifact for the day.
%i[processing pending approved failed covered].freeze
Constants included from Controllers::ReferenceFindable
Controllers::ReferenceFindable::ID_EMBEDDED_PATTERNS
Constants included from Controllers::AnalyticsEvents
Controllers::AnalyticsEvents::MAX_QUEUED_EVENTS, Controllers::AnalyticsEvents::SESSION_KEY
Constants included from Controllers::ErrorRendering
Controllers::ErrorRendering::NON_CONTENT_PATH_PREFIXES
Constants included from Www::SeoHelper
Www::SeoHelper::AWARDS, Www::SeoHelper::CA_ADDRESS, Www::SeoHelper::CA_BUSINESS_HOURS, Www::SeoHelper::CA_CONTACT_POINT, Www::SeoHelper::CA_CURRENCIES, Www::SeoHelper::CA_DESCRIPTION, Www::SeoHelper::CA_FOUNDING_DATE, Www::SeoHelper::CA_GLOBAL_LOCATION_NUMBER, Www::SeoHelper::CA_LEGAL_NAME, Www::SeoHelper::CA_LOCAL_BUSINESS, Www::SeoHelper::CA_ONLINE_STORE, Www::SeoHelper::CA_RETURN_POLICY, Www::SeoHelper::CA_SALES_DEPARTMENT, Www::SeoHelper::CA_SERVICE_AREA, Www::SeoHelper::CA_URL, Www::SeoHelper::CA_VAT_ID, Www::SeoHelper::CA_WAREHOUSE_DEPARTMENT, Www::SeoHelper::CA_WAREHOUSE_HOURS, Www::SeoHelper::COMPANY_EMAIL, Www::SeoHelper::COMPANY_LOGO, Www::SeoHelper::COMPANY_NAME, Www::SeoHelper::COMPANY_SLOGAN, Www::SeoHelper::EXPERTISE, Www::SeoHelper::FAX_NUMBER, Www::SeoHelper::GS1_COMPANY_PREFIX, Www::SeoHelper::ISO6523_CODE, Www::SeoHelper::PAYMENT_METHODS, Www::SeoHelper::PHONE_NUMBER, Www::SeoHelper::PRIMARY_NAICS, Www::SeoHelper::REFUND_TYPE, Www::SeoHelper::RETURN_FEES, Www::SeoHelper::RETURN_METHOD, Www::SeoHelper::RETURN_POLICY_CATEGORY, Www::SeoHelper::SECONDARY_NAICS, Www::SeoHelper::SOCIAL_PROFILES, Www::SeoHelper::US_ADDRESS, Www::SeoHelper::US_BUSINESS_HOURS, Www::SeoHelper::US_CONTACT_POINT, Www::SeoHelper::US_CURRENCIES, Www::SeoHelper::US_DESCRIPTION, Www::SeoHelper::US_FOUNDING_DATE, Www::SeoHelper::US_GLOBAL_LOCATION_NUMBER, Www::SeoHelper::US_IMAGE, Www::SeoHelper::US_LEGAL_NAME, Www::SeoHelper::US_LOCAL_BUSINESS, Www::SeoHelper::US_ONLINE_STORE, Www::SeoHelper::US_RETURN_POLICY, Www::SeoHelper::US_SALES_DEPARTMENT, Www::SeoHelper::US_SERVICE_AREA, Www::SeoHelper::US_TAX_ID, Www::SeoHelper::US_URL, Www::SeoHelper::US_WAREHOUSE_DEPARTMENT, Www::SeoHelper::US_WAREHOUSE_HOURS
Constants included from IconHelper
IconHelper::CUSTOM_ICON_MAP, IconHelper::CUSTOM_SVG_DIR, IconHelper::DEFAULT_FAMILY
Instance Method Summary collapse
-
#approve ⇒ Object
Approve a daily focus briefing and mark it as approved for its target employee.
-
#generate ⇒ Object
Creates a new "Daily Focus" briefing for the specified employee, marks it as processing, shares it with sales leadership, enqueues background analysis, and updates the review UI.
-
#generate_with_instructions ⇒ Object
Creates a new "Daily Focus" briefing conversation for the specified employee, seeds it with tool-service defaults and a prefill instruction prompt, shares it with sales leadership, and redirects the user to the assistant view for that conversation.
-
#reject ⇒ Object
Rejects a daily focus briefing, removes its processing lock, destroys the associated conversation, and updates the review UI.
- #review ⇒ Object
-
#summary ⇒ Object
Render the Daily Focus summary for the current date.
-
#widget_index ⇒ Object
Render the Daily Focus widget for the target employee.
Methods inherited from CrmController
#access_denied, #context_id, #context_object, #crm_home_path, #current_ability, #default_url_options, #download_temp, #get_tempfile_path_for_download, #initialize_crm_lazy_chunks, #record_not_found, #redirect_to_job_or_fallback, #render_edit_action, #set_context, #set_download_path, #stash_file_for_temp_download
Methods inherited from ApplicationController
#account_impersonated?, #add_to_flash, #append_token, #bypass_forgery_protection?, #chat_enabled?, #cloudflare_cleared?, #default_catalog, #default_url_options, #enable_turbo_frames, #find_publication, #fix_invalid_accept_header, #init_js_utils, #is_globals_call?, #layout_by_resource, #locale_store, #redirect_to, #require_employee_for_crm, #set_base_host, #set_real_ip, #set_report_errors_for, #should_render_layout?, #stamp_impersonation_context, #warmlyyours_canada_ip?, #warmlyyours_ip?, #y
Methods included from Controllers::ReturnPathHandling
#check_for_return_path, #redirect_to_return_path_or_default
Methods included from Controllers::AnalyticsEvents
#consume_queued_analytics_events, #track_event
Methods included from Controllers::DeviceDetection
Methods included from Controllers::SubdomainDetection
#is_crm_request?, #is_www_request?, #json_request?
Methods included from Controllers::TrackingDetection
#bot_request?, #gdpr_country?, #gdpr_country_data, #prevent_bots, #set_tracking_cookie, #track_visitor?
Methods included from Controllers::AcceleratedFileSending
#send_file_accelerated, #send_upload_accelerated
Methods included from Controllers::ErrorRendering
#excp_string, #mail_to_for_error_reporting, #render_400, #render_404, #render_406, #render_410, #render_500, #render_invalid_authenticity_token, #render_ip_spoof_error, #safe_referer_or_fallback
Methods included from Controllers::TurnstileVerification
#load_turnstile_script_tag, #turnstile_lazy_widget, #turnstile_script_tag, #turnstile_widget, #validate_turnstile!
Methods included from Controllers::CloudflareCaching
edge_cached, #edge_cached_action?, #reset_cloudflare_cache, #set_cloudflare_cache, #skip_session
Methods included from Controllers::Webpackable
#preload_webpack_fonts, #webpack_css_include, #webpack_css_url, #webpack_js_include, #wpd_is_running?
Methods included from Controllers::Localizable
#cloudflare_country_locale, #determine_request_locale, #geocoder_locale, #guest_user_locale_check, #locale_optional_www_auth_path?, #param_locale, #set_locale, #set_request_locale, #skip_localization?, #warmlyyours_ip_locale
Methods included from Controllers::Authenticable
#access_denied, #authenticate_account, #authenticate_account!, #authenticate_account_from_login_token!, #authenticate_account_from_token!, #check_is_a_manager, #check_is_a_sales_manager, #check_is_an_admin, #check_is_an_employee, #check_party, #clear_mismatched_guest_user, #create_guest_user, #credentials?, #current_or_guest_user, #current_or_guest_user_id_read_only, #current_user, #devise_mapping, #fully_logged_in?, #generate_bot_id, #guest_user, #identifiable?, #init_current_user, #initialize_guest, #load_context_user, #logging_in, #resource, #resource_name, #restrict_access_for_non_employees, #scrubbed_request_path, #user_object, #warn_on_session_guest_id_leak
Methods included from ApplicationHelper
#better_number_to_currency, #check_force_logout, #check_or_cross, #check_or_times, #error_messages, #general_disclaimer_on_product_installation_and_local_codes, #gridjs_from_html_table, #gridjs_table, #is_wy_ip, #line_break, #parent_layout, #pass_or_fail, #render_error_messages_list, #render_video_card, #resolved_auth_form_turbo_frame, #return_path_or, #safe_css_color, #set_return_path_if_present, #set_section_if_present, #tab_frame_id, #to_underscore, #track_page?, #turbo_section_wrapper, #turbo_tabs_request?, #url_on_same_domain_as_request, #widget_index_daily_focus_index_path, #working_hours?, #yes_or_no, #yes_or_no_highlighted, #yes_or_no_with_check_or_cross, #youtube_video
Methods included from UppyUploaderHelper
#file_uploader, #image_uploader, #large_file_uploader_s3, #lead_sketch_uploader, #rma_image_uploader, #rma_image_uploader_s3, #uppy_uploader, #video_uploader
Methods included from Www::ImagesHelper
#image_asset_tag, #image_asset_url
Methods included from Www::SeoHelper
#add_page_schema, #canada?, #company_social_links, #ensure_context_json, #json_ld_script_tag, #local_business_schema, #online_store_id, #online_store_schema, #page_main_entity, #page_main_entity_json, #render_auto_collection_page_schema, #render_collection_page_schema, #render_local_business_schema, #render_online_store_schema, #render_page_schemas, #render_page_video_schemas, #render_webpage_schema, #render_webpage_schema_with_collections, #usa?
Methods included from UrlsHelper
#catalog_breadcrumb_links, #catalog_link, #catalog_link_for_product_line, #catalog_link_for_sku, #cms_link, #delocalized_path, #path_to_sales_product_sku, #path_to_sales_product_sku_for_product_line, #path_to_sales_product_sku_for_product_line_slug, #product_line_from_catalog_link, #protocol_neutral_url, #sanitize_external_url, #valid_external_url?
Methods included from IconHelper
#account_nav_icon, #fa_icon, #star_rating_html
Instance Method Details
#approve ⇒ Object
Approve a daily focus briefing and mark it as approved for its target employee.
Updates the conversation's user and title (using the conversation's daily_focus_date), removes the daily_focus_target_employee_id and daily_focus_status metadata keys, enqueues an approval email to the employee, and responds by replacing the review row and stats via Turbo Stream or by redirecting to the review page.
If the conversation has no daily_focus_target_employee_id, responds with HTTP 422 Unprocessable Entity.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'app/controllers/daily_focus_controller.rb', line 52 def approve conversation = AssistantConversation.find(params[:id]) target_id = conversation.daily_focus_target_employee_id return head(:unprocessable_entity) unless target_id rep = Employee.find(target_id) brief_date = review_date_param_for(conversation) conversation.update!( user_id: rep.id, title: "Daily Focus — #{brief_date.strftime('%A, %B %-d, %Y')}" ) AssistantConversation.where(id: conversation.id).update_all("metadata = metadata - 'daily_focus_target_employee_id' - 'daily_focus_status'") conversation.reload InternalMailer.daily_focus_approved(employee: rep, conversation: conversation).deliver_later respond_to do |format| format.turbo_stream do rd = review_date_param_for(conversation) covered_ids = Array(conversation.daily_focus_covered_employee_ids).map(&:to_i) covered_by_id = Employee.where(id: covered_ids).index_by(&:id) covered_streams = covered_ids.filter_map do |cid| cov = covered_by_id[cid] next unless cov turbo_stream.replace( "daily-focus-row-#{cov.id}", partial: 'daily_focus/review_row', locals: review_row_locals(cov, :covered, conversation, for_date: rd, covering_rep: rep) ) end render turbo_stream: [ turbo_stream.replace( "daily-focus-row-#{rep.id}", partial: 'daily_focus/review_row', locals: review_row_locals(rep, :approved, conversation, for_date: rd) ), *covered_streams, turbo_stream.replace("daily-focus-stats", partial: 'daily_focus/stats_cards', locals: stats_card_locals(for_date: rd)) ] end format.html { redirect_to review_daily_focus_path(date: review_date_param_for(conversation)), notice: "Daily focus approved for #{rep.full_name}" } end end |
#generate ⇒ Object
Creates a new "Daily Focus" briefing for the specified employee, marks it as processing, shares it with sales leadership, enqueues background analysis, and updates the review UI.
The action:
- creates an Assistant conversation titled for the employee and today's date and stores daily-focus metadata on it,
- shares the conversation with sales leadership,
- enqueues
DailyFocusAnalysisRepWorkerto generate the briefing content, - responds with a Turbo Stream that replaces the employee's review row and the stats cards, or redirects to the review page with a notice for HTML requests.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'app/controllers/daily_focus_controller.rb', line 160 def generate employee = Employee.find(params[:employee_id]) cleanup_stale_daily_focus(employee) title = "Daily Focus — #{employee.full_name} — #{Date.current.strftime('%A, %B %-d, %Y')}" conversation = Assistant::SunnyAgent.create!(user: current_user, title: title) conversation.update!(metadata: conversation..merge( 'daily_focus_date' => Date.current.iso8601, 'daily_focus_target_employee_id' => employee.id, 'daily_focus_covered_employee_ids' => [], 'daily_focus_status' => 'processing', 'conversation_type' => 'daily_briefing', 'tool_services' => DailyFocus::TOOL_SERVICE_KEYS )) DailyFocus::ConversationSharing.share_with_sales_leadership!(conversation, shared_by: current_user) DailyFocusAnalysisRepWorker.perform_async(employee.id, conversation.id, []) respond_to do |format| format.turbo_stream do render turbo_stream: [ turbo_stream.replace( "daily-focus-row-#{employee.id}", partial: 'daily_focus/review_row', locals: review_row_locals(employee, :processing, conversation, for_date: Date.current) ), turbo_stream.replace("daily-focus-stats", partial: 'daily_focus/stats_cards', locals: stats_card_locals(for_date: Date.current)) ] end format.html { redirect_to review_daily_focus_path(date: Date.current.iso8601), notice: "Daily focus generation queued for #{employee.full_name}" } end end |
#generate_with_instructions ⇒ Object
Creates a new "Daily Focus" briefing conversation for the specified employee, seeds it with tool-service defaults and a prefill instruction prompt, shares it with sales leadership, and redirects the user to the assistant view for that conversation.
The action:
- Loads the target employee and removes any stale/failed processing briefings for that employee for today.
- Creates an Assistant::SunnyAgent conversation and sets metadata including
daily_focus_date,daily_focus_target_employee_id,daily_focus_status('processing'),conversation_type('daily_briefing'), andtool_services(the configured daily-focus services). - Shares the conversation with sales leadership.
- Stores a long instructional prefill in
conversation.draft_prefilland default tool handles inconversation.default_tool_handlesso they survive any redirect and a refresh. - Redirects to the assistant view for the newly created conversation.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'app/controllers/daily_focus_controller.rb', line 203 def generate_with_instructions employee = Employee.find(params[:employee_id]) cleanup_stale_daily_focus(employee) today = Date.current.strftime('%A, %B %-d, %Y') title = "Daily Focus — #{employee.full_name} — #{today}" conversation = Assistant::SunnyAgent.create!(user: current_user, title: title) conversation.update!(metadata: conversation..merge( 'daily_focus_date' => Date.current.iso8601, 'daily_focus_target_employee_id' => employee.id, 'daily_focus_covered_employee_ids' => [], 'daily_focus_status' => 'processing', 'conversation_type' => 'daily_briefing', 'tool_services' => DailyFocus::TOOL_SERVICE_KEYS, 'draft_prefill' => DailyFocus::Prompt.instructions_prefill(primary_employee: employee, covered_employees: []), 'default_tool_handles' => DAILY_FOCUS_TOOL_HANDLES )) DailyFocus::ConversationSharing.share_with_sales_leadership!(conversation, shared_by: current_user) redirect_to assistant_path(conversation) end |
#reject ⇒ Object
Rejects a daily focus briefing, removes its processing lock, destroys the associated conversation, and updates the review UI.
Finds the AssistantConversation by params[:id], releases its processing lock if held, and destroys it. If the conversation lacks a linked target employee, responds with HTTP 422 Unprocessable Entity.
Responds to:
- turbo_stream: replaces the target employee's review row (marking it as
:missing) and refreshes the stats cards for the briefing date. - html: redirects to the review page for the briefing date with a notice.
The action uses the conversation's stored daily_focus_date to determine the review date and whether generation of missing briefings should be allowed in the replaced row.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'app/controllers/daily_focus_controller.rb', line 107 def reject conversation = AssistantConversation.find(params[:id]) target_id = conversation.daily_focus_target_employee_id return head(:unprocessable_entity) unless target_id rep = Employee.find_by(id: target_id) review_date = review_date_param_for(conversation) covered_ids = Array(conversation.daily_focus_covered_employee_ids).map(&:to_i) conversation.release_processing_lock! if conversation.processing_by_id.present? conversation.destroy! respond_to do |format| format.turbo_stream do ag = (review_date == Date.current) covered_by_id = Employee.where(id: covered_ids).index_by(&:id) covered_streams = covered_ids.filter_map do |cid| cov = covered_by_id[cid] next unless cov turbo_stream.replace( "daily-focus-row-#{cov.id}", partial: 'daily_focus/review_row', locals: review_row_locals(cov, :missing, nil, allow_generate_missing: ag, for_date: review_date) ) end render turbo_stream: [ turbo_stream.replace( "daily-focus-row-#{rep.id}", partial: 'daily_focus/review_row', locals: review_row_locals( rep, :missing, nil, allow_generate_missing: ag, for_date: review_date ) ), *covered_streams, turbo_stream.replace("daily-focus-stats", partial: 'daily_focus/stats_cards', locals: stats_card_locals(for_date: review_date)) ] end format.html { redirect_to review_daily_focus_path(date: review_date.iso8601), notice: "Daily focus rejected for #{rep.full_name}" } end end |
#review ⇒ Object
43 44 45 |
# File 'app/controllers/daily_focus_controller.rb', line 43 def review build_review_data end |
#summary ⇒ Object
Render the Daily Focus summary for the current date.
Computes daily focus counts for Date.current and renders the Crm::DailyFocusSummaryComponent
with processing, pending, approved, and missing counts.
29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'app/controllers/daily_focus_controller.rb', line 29 def summary counts = compute_focus_counts(Date.current) respond_to do |format| format.html do render Crm::DailyFocusSummaryComponent.new( processing_count: counts[:processing], pending_count: counts[:pending], approved_count: counts[:approved], missing_count: counts[:missing] ) end end end |
#widget_index ⇒ Object
Render the Daily Focus widget for the target employee.
Ensures the target employee is loaded, sets @conversation to that employee's daily focus conversation, and renders the Crm::DailyFocusWidgetComponent as HTML.
16 17 18 19 20 21 22 |
# File 'app/controllers/daily_focus_controller.rb', line 16 def load_employee @conversation = find_daily_focus_conversation respond_to do |format| format.html { render Crm::DailyFocusWidgetComponent.new(employee: target_employee, conversation: @conversation) } end end |