Skip to content

Commit ba1ee21

Browse files
authored
feature: Dr. Rai Progress Component (#5618)
**Story card:** [sc-15820](https://app.shortcut.com/simpledotorg/story/15820/create-the-dr-rai-frontend-progress-component) ## Because This is one of three parts of the Dr. Rai Component. The progress component shows the progress of a goal through a period, and the outcome of goals in previous periods. ## This addresses Adding the Dr. Rai Progress component to the dashboard. ## Test instructions .
1 parent 038811b commit ba1ee21

File tree

10 files changed

+557
-58
lines changed

10 files changed

+557
-58
lines changed

app/assets/stylesheets/application.scss

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,3 +802,259 @@ span.inactive {
802802
.no-card-gap-filler p {
803803
color: #A0A0A0;
804804
}
805+
806+
.action-progress-block {
807+
width: 100%;
808+
background: #feffd8;
809+
display: grid;
810+
grid-template-columns: 1fr 1fr;
811+
gap: 20px;
812+
padding: 20px;
813+
border-radius: 4px;
814+
position: relative;
815+
}
816+
817+
@scope (.action-progress-block) {
818+
h3 {
819+
font-size: 20px;
820+
font-weight: bold;
821+
color: #483d04;
822+
font-family: $font-condensed;
823+
}
824+
825+
ul {
826+
margin: 6px 0 0;
827+
list-style-type: disc;
828+
padding-left: 1.5em;
829+
}
830+
831+
p {
832+
// font-family: $font-condensed;
833+
margin-bottom: 2px;
834+
font-size: 16px;
835+
}
836+
837+
.progress-options {
838+
position: absolute;
839+
top: 4px;
840+
right: 4px;
841+
font-size: 20px;
842+
843+
.dots-button {
844+
cursor: pointer;
845+
border: 2px solid #eceeb1;
846+
border-radius: 3px;
847+
height: 24px;
848+
width: 30px;
849+
overflow: hidden;
850+
display: flex;
851+
align-items: center;
852+
justify-content: center;
853+
gap: 3px;
854+
}
855+
.dots-button:focus {
856+
background: #eceeb1;
857+
}
858+
}
859+
.progress-options-inner {
860+
position: relative;
861+
}
862+
863+
.popover {
864+
width: 112px;
865+
border: 4px solid #d5d5d550;
866+
border-radius: 8px;
867+
position: absolute;
868+
bottom: 0;
869+
right: 0;
870+
overflow: hidden;
871+
padding: 4px 0;
872+
873+
ul {
874+
list-style-type: none;
875+
padding: 0;
876+
margin: 0;
877+
}
878+
879+
hr {
880+
margin: 2px 8px;
881+
}
882+
883+
button {
884+
padding: 8px 16px;
885+
font: "Roboto", sans-serif;
886+
width: 100%;
887+
text-align: left;
888+
}
889+
890+
button.edit:hover {
891+
background: #ecf2fe !important;
892+
color: #007eff;
893+
}
894+
895+
button.delete:hover {
896+
background: #feecec !important;
897+
color: #d10000;
898+
}
899+
}
900+
901+
// @scope (.popover) {
902+
// .edit {
903+
// background-color: #c4e2ff;
904+
// padding: 0.5rem;
905+
// width: 100%;
906+
// text-align: center;
907+
// font-size: 1rem;
908+
909+
// border-radius: 0.375rem;
910+
911+
// &:hover {
912+
// background-color: #b2d9ff;
913+
// }
914+
// }
915+
// }
916+
917+
.progress-right-col {
918+
padding-right: 20px;
919+
}
920+
921+
.progress-statement {
922+
text-align: right;
923+
margin: 12px 0 6px;
924+
font-weight: 500;
925+
font-size: 16px;
926+
font-weight: 500;
927+
font-family: $font-condensed;
928+
}
929+
930+
.progress-bar {
931+
background: #eceeb1;
932+
width: 100%;
933+
height: 36px;
934+
border-radius: 0.375rem;
935+
overflow: hidden;
936+
position: relative;
937+
938+
.progress-bar-fill {
939+
background: #d7cd06;
940+
height: 100%;
941+
position: relative;
942+
display: block;
943+
}
944+
945+
.progress-number {
946+
position: absolute;
947+
top: 1px;
948+
color: #6a6500;
949+
font-size: 1.5rem;
950+
font-weight: bold;
951+
display: inline-block;
952+
right: 0;
953+
transform: translateX(100%);
954+
transform: translateX(calc(100% + 8px));
955+
}
956+
957+
.progress-number-over-88-percent {
958+
transform: translateX(-8px);
959+
text-align: right;
960+
}
961+
}
962+
}
963+
964+
.action-block {
965+
display: flex;
966+
flex-direction: column;
967+
gap: 20px;
968+
margin-top: 16px;
969+
}
970+
971+
.actions-block button {
972+
border: 0;
973+
background-color: inherit;
974+
}
975+
976+
.actions-header {
977+
display: flex;
978+
align-items: center;
979+
gap: 16px;
980+
margin-bottom: 13px;
981+
}
982+
983+
.actions-header h4 {
984+
margin-right: 16px;
985+
margin-bottom: 0;
986+
padding: 0;
987+
}
988+
989+
.actions-header-button {
990+
display: flex;
991+
align-items: center;
992+
gap: 16px;
993+
font-weight: 600;
994+
color: black;
995+
}
996+
997+
.action-header-selected {
998+
color: #ff3355;
999+
text-decoration: underline;
1000+
text-decoration-thickness: 4px;
1001+
text-underline-offset: 6px;
1002+
}
1003+
1004+
.actions-header-add-action {
1005+
letter-spacing: 0.05em;
1006+
text-transform: uppercase;
1007+
font-weight: 600;
1008+
font-family: $font-condensed;
1009+
font-size: 14px;
1010+
color: #016de4;
1011+
margin-left: 12px;
1012+
1013+
&:hover {
1014+
text-decoration: underline;
1015+
text-decoration-thickness: 4px;
1016+
text-underline-offset: 6px;
1017+
}
1018+
}
1019+
1020+
.empty-goals-block {
1021+
background: #ebebeb;
1022+
border: 2px dashed rgba(0, 0, 0, 0.15);
1023+
border-radius: 0.75rem;
1024+
box-shadow: inset 0 2px 8px 0 rgba(0, 0, 0, 0.04);
1025+
height: 180px;
1026+
display: flex;
1027+
flex-direction: column;
1028+
align-items: center;
1029+
justify-content: center;
1030+
text-align: center;
1031+
gap: 8px;
1032+
}
1033+
1034+
.empty-goals-message {
1035+
font-family: $font-condensed;
1036+
margin-top: 0;
1037+
margin-bottom: 0.5rem;
1038+
font-size: 1.125rem; // 18px
1039+
color: rgba(0, 0, 0, 0.6);
1040+
}
1041+
1042+
.empty-goals-add-button {
1043+
background: #c5def8;
1044+
color: #016de4;
1045+
border-radius: 4px;
1046+
padding: 6px 0;
1047+
text-transform: uppercase;
1048+
letter-spacing: 0.08em;
1049+
font-weight: 600;
1050+
font-size: 14px;
1051+
min-width: 208px;
1052+
display: inline-block;
1053+
text-align: center;
1054+
cursor: pointer;
1055+
1056+
&:hover {
1057+
background: #b2d9ff;
1058+
color: #0156b2;
1059+
}
1060+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<div id="dr-rai--progress">
2+
<div class="actions-block">
3+
<div class="actions-header">
4+
<h4>Action plans</h4>
5+
<% quarterlies.each do |period, info| %>
6+
<%# TODO: Do a JS which moves the "action-header-selected" onClick %>
7+
<%= link_to human_readable(period), "#{request.path}?#{request.query_parameters.merge(selected_quarter: period.value.to_s).to_param}", class: classes_for_period(period) %>
8+
<% end %>
9+
<button class="actions-header-add-action">&#x2b; Add an action</button>
10+
</div>
11+
12+
<% if period_goals.present? %>
13+
<% period_goals.each do |goal| %>
14+
<div class="action-progress-block">
15+
<div class="progress-options">
16+
<button targetpopover="progress-options">
17+
<i class="fa-solid fa-ellipsis"></i>
18+
</button>
19+
<%# <div class="popover" popover="manual" id="progress-options"> %>
20+
<%# <ul> %>
21+
<%# <li class="edit">Edit</li> %>
22+
<%# <hr /> %>
23+
<%# <li class="delete">Delete</li> %>
24+
<%# </ul> %>
25+
<%# </div> %>
26+
</div>
27+
<div>
28+
<h3>Call 1,000 overdue patients by 30-Jun</h3>
29+
<ul>
30+
<li>
31+
<p>Man a stand at Holi festival event near town hall - 15th Mar</p>
32+
</li>
33+
<li>
34+
<p>Provide phone data for overdue calling</p>
35+
</li>
36+
<li>
37+
<p>Test Whatsapp messaging with patients that have 2 calls with no answer</p>
38+
</li>
39+
</ul>
40+
</div>
41+
<div class="progress-right-col">
42+
<p class="progress-statement">0 of 1200 overdue patients</p>
43+
<div class="progress-bar">
44+
<div
45+
class="progress-bar-fill"
46+
style="width: 89%"
47+
>
48+
<span
49+
class="progress-number progress-number-over-88-percent"
50+
>89%</span
51+
>
52+
</div>
53+
</div>
54+
</div>
55+
</div>
56+
<% end %>
57+
<% else %>
58+
<div class="empty-goals-block">
59+
<p class="empty-goals-message">
60+
Add an action for <%= start_of(selected_period) %> to <%= end_of(selected_period) %>
61+
</p>
62+
<p class="empty-goals-message">
63+
No actions for <%= start_of(selected_period) %> to <%= end_of(selected_period) %>
64+
</p>
65+
<button class="empty-goals-add-button">
66+
&#x2b; Add an action
67+
</button>
68+
</div>
69+
<% end %>
70+
</div>
71+
</div>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Root component for Dr. Rai Reports
2+
class Dashboard::DrRaiReport < ApplicationComponent
3+
# FIXME(:selected_period): Write out JS for this component's interactivity.
4+
# The whole current period thing only works onMount — sticking to frontend
5+
# terms here — after then, it's useless. This is because a view-component in
6+
# Rails, which this is, is technically a server-side render scoped to its own
7+
# local variables, and exposing methods as callable from the view. It doesn't
8+
# handle state. This means interactivity cannot be handled at the view
9+
# component layer. In lay man's terms, when someone selects another quarter
10+
# to view, we need to do a full page refresh if we are depending on the view
11+
# component; a full page refresh passing in the selected quarter. We need JS
12+
13+
attr_reader :quarterlies
14+
attr_accessor :selected_period
15+
16+
def initialize(quarterlies, region, selected_quarter = nil)
17+
@quarterlies = quarterlies
18+
@region = region
19+
@selected_period = if selected_quarter.nil?
20+
current_period
21+
else
22+
Period.new(type: :quarter, value: selected_quarter)
23+
end
24+
@goals = {}
25+
end
26+
27+
def current_period
28+
Period.current.to_quarter_period
29+
end
30+
31+
def start_of period
32+
period.begin.strftime("%b-%-d")
33+
end
34+
35+
def end_of period
36+
period.end.strftime("%b-%-d")
37+
end
38+
39+
def classes_for_period period
40+
raise "#{period} is not a Period" unless period.is_a? Period
41+
candidates = ["actions-header-button"]
42+
candidates << "action-header-selected" if period == selected_period
43+
candidates.join(" ")
44+
end
45+
46+
def period_goals
47+
@goals[selected_period]
48+
end
49+
50+
def human_readable thing
51+
case thing
52+
when Period
53+
human_readable_period thing
54+
end
55+
end
56+
57+
private
58+
59+
def human_readable_period period
60+
period.value.to_s.tr("-", " ")
61+
end
62+
end

0 commit comments

Comments
 (0)