Module: DailyFocus::Prompt

Extended by:
TimeOffPlannedWorkDayHelper
Defined in:
app/services/daily_focus/prompt.rb

Overview

Short user-facing prompts for tool-based Daily Focus (no pre-injected CRM dumps).

Constant Summary collapse

EXCLUDE_ETAILERS_SQL =

E-tailer / marketplace accounts (Amazon, Costco, Home Depot, RONA, TJX, and
any other "E-commerce - *" report grouping) are excluded from Daily Focus: they
transact through established EDI, not rep nurturing, so they only add noise to a
briefing meant for professional and homeowner accounts that need human contact.
Both view_activities and view_customers expose the customer's report_grouping; the
COALESCE keeps rows whose grouping is NULL (a bare NULL NOT LIKE … would drop them).
The named list catches the e-tailer groupings that predate the "E-commerce - *"
convention (RONA Canada, Home Depot, …) — it comes from
Customer::ReportGrouping::ETAILER_GROUPINGS so the two can't disagree.

"COALESCE(report_grouping, '') NOT LIKE 'E-commerce%' " \
"AND COALESCE(report_grouping, '') NOT IN (#{Customer::ReportGrouping.etailer_groupings_sql_list})"

Class Method Summary collapse

Methods included from TimeOffPlannedWorkDayHelper

time_off_planned_work_day_planned_hours, time_off_planned_work_day_summary, time_off_planned_work_day_usual_summary

Class Method Details

.coaching_note_section(primary_employee) ⇒ Object

Standing manager coaching instruction for this rep, set on the Daily Focus
review page and reused every day until cleared. Injected right after the
briefing ask (see core_body) so Sunny treats it as a first-class priority
ahead of the mechanical SQL sections. nil when the manager hasn't set one.
Public because both audiences use it — SupportSections calls it too.



61
62
63
64
65
66
67
68
69
70
71
# File 'app/services/daily_focus/prompt.rb', line 61

def self.coaching_note_section(primary_employee)
  body = primary_employee.daily_focus_coaching_note&.body
  return nil if body.blank?

  <<~TEXT.squish
    MANAGER COACHING FOCUS — your manager set specific priorities for
    #{primary_employee.full_name} today. Treat these as high-priority instructions:
    surface a prominent "Coaching Focus" section and let them shape which accounts
    and actions you highlight, on top of the standard briefing. Instructions: #{body}
  TEXT
end

.focus_date_for_conversation(conversation) ⇒ Object



20
21
22
23
24
25
26
27
# File 'app/services/daily_focus/prompt.rb', line 20

def self.focus_date_for_conversation(conversation)
  raw = conversation&.&.dig('daily_focus_date')
  return Date.current if raw.blank?

  Date.iso8601(raw.to_s)
rescue ArgumentError
  Date.current
end

.instructions_prefill(primary_employee:, covered_employees: [], date: Date.current) ⇒ Object



29
30
31
# File 'app/services/daily_focus/prompt.rb', line 29

def self.instructions_prefill(primary_employee:, covered_employees: [], date: Date.current)
  user_message(primary_employee: primary_employee, covered_employees: covered_employees, date: date)
end

.user_message(primary_employee:, covered_employees: [], date: Date.current, audience: nil) ⇒ Object

Audience defaults from the employee's roles (DailyFocus.audience_for), so callers
never thread it through — pass it only to force a variant (tests).



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'app/services/daily_focus/prompt.rb', line 35

def self.user_message(primary_employee:, covered_employees: [], date: Date.current, audience: nil)
  audience ||= DailyFocus.audience_for(primary_employee)
  covered = Array(covered_employees).compact.uniq
  preload_planned_availability!([primary_employee, *covered], date)
  planned = planned_work_availability_section(
    primary_employee: primary_employee,
    covered_employees: covered,
    date: date
  )
  tz = TimezoneHints.prompt_section(primary_employee: primary_employee, covered_employees: covered)
  body = if audience.to_s == 'support'
           SupportSections.body(primary_employee, covered, date)
         else
           core_body(primary_employee, covered)
         end
  parts = [body]
  parts << planned if planned.present?
  parts << tz
  parts.join(' ')
end