| Line 56... |
Line 56... |
| 56 |
private static final int AGEING_DAYS = 7;
|
56 |
private static final int AGEING_DAYS = 7;
|
| 57 |
|
57 |
|
| 58 |
/** Most-recent weeks shown in the SLA owner heat-map. */
|
58 |
/** Most-recent weeks shown in the SLA owner heat-map. */
|
| 59 |
private static final int HEATMAP_WEEKS = 6;
|
59 |
private static final int HEATMAP_WEEKS = 6;
|
| 60 |
|
60 |
|
| 61 |
/** Cap on rows returned to a drill-down drawer. */
|
61 |
/** Cap on rows returned to a drill-down drawer. Public so the drawer can say it is showing a slice. */
|
| 62 |
private static final int DRILL_LIMIT = 100;
|
62 |
public static final int DRILL_LIMIT = 100;
|
| 63 |
|
63 |
|
| 64 |
@Autowired
|
64 |
@Autowired
|
| 65 |
private LeadRepository leadRepository;
|
65 |
private LeadRepository leadRepository;
|
| 66 |
|
66 |
|
| 67 |
@Autowired
|
67 |
@Autowired
|
| Line 616... |
Line 616... |
| 616 |
// ---- Drill-down -------------------------------------------------------------------------
|
616 |
// ---- Drill-down -------------------------------------------------------------------------
|
| 617 |
|
617 |
|
| 618 |
/** Lead rows for a drill bucket, shaped for the drawer table. */
|
618 |
/** Lead rows for a drill bucket, shaped for the drawer table. */
|
| 619 |
public List<Map<String, Object>> drill(String bucketType, String bucketKey, Integer regionId,
|
619 |
public List<Map<String, Object>> drill(String bucketType, String bucketKey, Integer regionId,
|
| 620 |
LocalDateTime from, LocalDateTime to) {
|
620 |
LocalDateTime from, LocalDateTime to) {
|
| - |
|
621 |
String type = bucketType == null ? "" : bucketType.trim().toUpperCase();
|
| - |
|
622 |
String key = bucketKey;
|
| - |
|
623 |
// The funnel asks for a stage cumulatively -- "reached CONTACTED" means CONTACTED or anything
|
| - |
|
624 |
// beyond it, which is exactly how the bar was counted. Expand it here, where the happy path
|
| - |
|
625 |
// already lives, rather than teaching the repository the order of the lifecycle.
|
| - |
|
626 |
if ("STAGE_REACHED".equals(type)) {
|
| - |
|
627 |
type = "STAGE_IN";
|
| - |
|
628 |
key = stagesFrom(bucketKey);
|
| - |
|
629 |
}
|
| 621 |
LocalDateTime now = LocalDateTime.now();
|
630 |
LocalDateTime now = LocalDateTime.now();
|
| 622 |
List<Integer> ids = leadRepository.selectLeadIdsForBucket(bucketType, bucketKey, regionId,
|
631 |
List<Integer> ids = leadRepository.selectLeadIdsForBucket(type, key, regionId,
|
| 623 |
now, now.plusHours(1), from, to, DRILL_LIMIT);
|
632 |
now, now.plusHours(1), from, to, DRILL_LIMIT);
|
| 624 |
List<Map<String, Object>> out = new ArrayList<>();
|
633 |
List<Map<String, Object>> out = new ArrayList<>();
|
| 625 |
if (ids == null || ids.isEmpty()) {
|
634 |
if (ids == null || ids.isEmpty()) {
|
| 626 |
return out;
|
635 |
return out;
|
| 627 |
}
|
636 |
}
|
| Line 639... |
Line 648... |
| 639 |
}
|
648 |
}
|
| 640 |
return toRows(ordered);
|
649 |
return toRows(ordered);
|
| 641 |
}
|
650 |
}
|
| 642 |
|
651 |
|
| 643 |
/**
|
652 |
/**
|
| - |
|
653 |
* The given stage and every stage after it on the happy path, comma-separated -- the set the
|
| - |
|
654 |
* cumulative funnel bar counted. An unrecognised stage (a terminal one such as NOT_INTERESTED,
|
| - |
|
655 |
* which is not on the path) passes through unchanged and matches only itself.
|
| - |
|
656 |
*/
|
| - |
|
657 |
private String stagesFrom(String stage) {
|
| - |
|
658 |
String wanted = stage == null ? "" : stage.trim();
|
| - |
|
659 |
int idx = -1;
|
| - |
|
660 |
for (int i = 0; i < FUNNEL.length; i++) {
|
| - |
|
661 |
if (FUNNEL[i].name().equalsIgnoreCase(wanted)) {
|
| - |
|
662 |
idx = i;
|
| - |
|
663 |
break;
|
| - |
|
664 |
}
|
| - |
|
665 |
}
|
| - |
|
666 |
if (idx < 0) {
|
| - |
|
667 |
return wanted;
|
| - |
|
668 |
}
|
| - |
|
669 |
StringBuilder sb = new StringBuilder();
|
| - |
|
670 |
for (int i = idx; i < FUNNEL.length; i++) {
|
| - |
|
671 |
if (sb.length() > 0) {
|
| - |
|
672 |
sb.append(',');
|
| - |
|
673 |
}
|
| - |
|
674 |
sb.append(FUNNEL[i].name());
|
| - |
|
675 |
}
|
| - |
|
676 |
return sb.toString();
|
| - |
|
677 |
}
|
| - |
|
678 |
|
| - |
|
679 |
/**
|
| 644 |
* The Lead-pipeline table: every matching lead, not a client-side slice of the first page.
|
680 |
* The Lead-pipeline table: every matching lead, not a client-side slice of the first page.
|
| 645 |
*
|
681 |
*
|
| 646 |
* <p>Returns the page plus the true total, so the table can report "50 of 229" rather than
|
682 |
* <p>Returns the page plus the true total, so the table can report "50 of 229" rather than
|
| 647 |
* implying the twelve rows it drew are all there are. A non-empty {@code q} searches across all
|
683 |
* implying the twelve rows it drew are all there are. A non-empty {@code q} searches across all
|
| 648 |
* leads and ignores the date window — see {@code LeadRepository.selectPipeline}.
|
684 |
* leads and ignores the date window — see {@code LeadRepository.selectPipeline}.
|