CCCT-2542 Add Connect Progress Card View - #3812
Conversation
Suggested Review Order
|
📝 WalkthroughWalkthroughIntroduces a reusable Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant XMLLayout
participant ConnectProgressAndStatusCard
participant ProgressStatusViews
XMLLayout->>ConnectProgressAndStatusCard: inflate attributes and initial content
ConnectProgressAndStatusCard->>ProgressStatusViews: update text, visibility, and progress
ConnectProgressAndStatusCard->>ProgressStatusViews: apply sync badge and content colors
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3812 +/- ##
============================================
+ Coverage 26.85% 27.29% +0.44%
- Complexity 4706 4773 +67
============================================
Files 988 991 +3
Lines 58844 59019 +175
Branches 7003 7025 +22
============================================
+ Hits 15801 16111 +310
+ Misses 41131 40974 -157
- Partials 1912 1934 +22 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Currently, consumers have to manually bind each individual property of the card to configure it correctly: This imperatively coupled approach leaves room for developers to accidentally miss properties, which can easily lead to inconsistent UI states or bugs. Can we encapsulate this into a single, cohesive UI State model instead? We could introduce a State data class and expose a single bind method on the component: data class State(
val title: CharSequence? = null,
val progressLabel: CharSequence? = null,
val progressCurrent: Int = 0,
val progressMax: Int = 0,
val progressCaption: CharSequence? = null,
val contentEnabled: Boolean = true,
val semiCircle: SemiCircle? = null,
val sync: Sync? = null,
val info: Info? = null,
) {
data class SemiCircle(
val current: Int,
val max: Int,
val description: CharSequence? = null,
)
data class Sync(
val status: SyncStatus,
val text: CharSequence,
val subtext: CharSequence? = null,
)
class Info(
val message: CharSequence,
val ctaText: CharSequence? = null,
val onCtaClick: (() -> Unit)? = null,
)
}This allows the consumer to bind the entire view state safely in a single atomic update: card.bind(
ConnectProgressAndStatusCard.State(
title = data.title,
progressCurrent = data.completed,
progressMax = data.total,
contentEnabled = data.isActive,
semiCircle = if (data.showSemiCircle) {
State.SemiCircle(data.completed, data.total, data.semiCircleLabel)
} else null,
sync = data.syncMessage?.let {
State.Sync(
status = if (data.hasSyncProblem) SyncStatus.WARNING else SyncStatus.UP_TO_DATE,
text = it,
subtext = data.lastSynced,
)
},
info = data.banner?.let {
State.Info(it, data.ctaLabel, onCtaClick = { openMoreInfo() })
},
),
)
|
|
Based on screenshots, think this PR is implementing outdated/under-iteration designs for task and sync cards, both seems to be undergoing changes in latest iterations. Curious if it's not clear from the Solutions Summary that these cards were going through iterations, product is supposed to mark things getting iterated clearly on this doc and if it's not marked correctly think we should be loud and flag it to product on slack that we are repeatedly implementing Outdated designs due to lack of clarity. |
@shubham1g5 Ah, the tasks message part is my mistake, I knew that was being discussed and left it out of the initial work, then when testing the implementation saw it missing, forgot it was intentional, and added it in. But for the sync part of the card, I don't see any indication in the Solutions Summary section about that being in flux, and based on your link it seems the only good source for that discussion right now is in the Playground section in Figma (really just a side effect of moving the tasks message around). So maybe there's some room for improvement in communicating which parts aren't finalized. Should we move this ticket to blocked until the design is finalized? |
Got it, are you able to flag it to Kim so that she is aware of these gaps.
We should just remove the tasks and syncs card out of scope of this ticket and tackle them as other tickets as we need this work to be merged sooner than later to be able to make progress on the rest of the phase 1 work. |
|
would be nice to clean up commit history on this PR as well |
conroy-ricketts
left a comment
There was a problem hiding this comment.
Looks fantastic! Just had a few comments
| <item name="descriptionTextColor">@color/connect_text_color</item> | ||
| </style> | ||
|
|
||
| <style name="Widget.CommCare.ConnectProgressCard" parent=""> |
There was a problem hiding this comment.
Curiosity question: what's the intention behind explicitly defining an empty parent here?
There was a problem hiding this comment.
I learned a bit here as well. When a style use dot notation like this one, parent styles are automatically inherited from the parent component (i.e. Widget.CommCare here). Setting parent="" disables the behavior so nothing is inherited (preventing possible surprises later if something in the parent widget changes, even though in this case there isn't one right now).
Add ProgressUtils.calculateProgress(current, max) and reuse it in SemiCircleProgressBar and ConnectProgressJobSummaryAdapter, replacing the duplicated max>0 fraction guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reusable Connect card composing a semi-circle indicator, a horizontal progress bar with label/count/caption and an optional info-message banner with a CTA. The whole card is configured atomically through a single bind(State) so callers cannot leave it half-configured; out-of-range progress is logged then coerced. Content colors resolve through a color -> style -> component path and all dp/sp values live in dimens.xml (LinearProgressBar's corner radius included). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover title/label/caption visibility, semi-circle and linear progress binding, count derivation and clamping, contentEnabled recoloring, the info banner and CTA callback, attribute inflation, and color-attribute overrides. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
02ab984 to
2c0a5fa
Compare
4b9f08e to
1843792
Compare
…rogress-and-status-card # Conflicts: # app/res/values/styles.xml
Match node 5940:23149 of the Opportunity Home design: 8dp card corner radius, 8dp progress bar, 24sp metric, 4dp gaps around the bar, and no extra top inset when the card has neither a title nor a semi-circle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CCCT-2542
Technical Summary
Adds
ConnectProgressCard, a reusable Connect view intended for both the Learning Progress and Delivery Progress screens. It composes existing sub-views — an optional semi-circle indicator, a horizontal progress bar (label, count, caption), and an independently controllable info-message banner with an optional CTA — with appearance driven entirely by content properties rather than a state enum. Not yet wired into any live screen; this PR is the building block only.Independently-controllable pieces:
Screenshots
Learning progress:


Delivery progress:
Disabled state with message and CTA:

Safety Assurance
Safety story
What gives confidence:
ConnectProgressCardTestRobolectric suite locally and it passed, and rendered the card in a layout preview/harness to check appearance.Risks to review:
Automated test coverage
ConnectProgressCardTestcovers property-to-view bindings,progressCurrentclamping toprogressMax(including setting current before max), thecontentEnabledgray-out of the progress text and semi-circle, optional-content visibility (title, label, caption, count), the info-message/CTA banner and its click listener, attribute inflation, and per-instance color overrides.