From 56a6184c58f44b7fa8227342b4175f829d5cf082 Mon Sep 17 00:00:00 2001 From: "Ted M. Young" Date: Wed, 31 Jan 2024 08:30:02 -0800 Subject: [PATCH] Fixed issue #196 - create separate display of any in-progress Ensemble --- .../in/web/member/EnsembleSummaryView.java | 6 - .../in/web/member/InProgressEnsembleView.java | 32 ++ .../in/web/member/MemberController.java | 10 +- .../mobreg/application/EnsembleService.java | 19 +- .../com/jitterted/mobreg/domain/Ensemble.java | 5 + src/main/resources/static/tailwind.css | 318 +++--------------- .../resources/templates/member-register.html | 28 ++ .../web/member/EnsembleSummaryViewTest.java | 27 -- .../member/InProgressEnsembleViewTest.java | 57 ++++ .../in/web/member/MemberControllerTest.java | 34 +- .../application/EnsembleServiceTest.java | 44 +++ 11 files changed, 261 insertions(+), 319 deletions(-) create mode 100644 src/main/java/com/jitterted/mobreg/adapter/in/web/member/InProgressEnsembleView.java create mode 100644 src/test/java/com/jitterted/mobreg/adapter/in/web/member/InProgressEnsembleViewTest.java diff --git a/src/main/java/com/jitterted/mobreg/adapter/in/web/member/EnsembleSummaryView.java b/src/main/java/com/jitterted/mobreg/adapter/in/web/member/EnsembleSummaryView.java index 5a3042c1..4d3f26b4 100644 --- a/src/main/java/com/jitterted/mobreg/adapter/in/web/member/EnsembleSummaryView.java +++ b/src/main/java/com/jitterted/mobreg/adapter/in/web/member/EnsembleSummaryView.java @@ -89,12 +89,6 @@ static Status createStatusFor(Ensemble ensemble, MemberStatus memberStatusForEns }; } - private static String memberStatusToViewString(Ensemble ensemble, MemberId memberId) { - return ensemble.statusFor(memberId, ZonedDateTime.now()) - .toString() - .toLowerCase(); - } - private static List transform(MemberService memberService, Stream memberIdStream) { return memberIdStream .map(memberService::findById) diff --git a/src/main/java/com/jitterted/mobreg/adapter/in/web/member/InProgressEnsembleView.java b/src/main/java/com/jitterted/mobreg/adapter/in/web/member/InProgressEnsembleView.java new file mode 100644 index 00000000..b0827526 --- /dev/null +++ b/src/main/java/com/jitterted/mobreg/adapter/in/web/member/InProgressEnsembleView.java @@ -0,0 +1,32 @@ +package com.jitterted.mobreg.adapter.in.web.member; + +import com.jitterted.mobreg.application.MemberService; +import com.jitterted.mobreg.domain.Ensemble; +import com.jitterted.mobreg.domain.MemberId; + +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Stream; + +public record InProgressEnsembleView(String name, + String url, + String startTime, + List participants, + List spectators) { + + public static InProgressEnsembleView from(Ensemble ensemble, MemberService memberService) { + return new InProgressEnsembleView(ensemble.name(), + ensemble.meetingLink().toString(), + ensemble.startDateTime() + .toLocalTime() + .format(DateTimeFormatter.ofPattern("hh:mm a")), + namesOf(ensemble.acceptedMembers(), memberService), + namesOf(ensemble.spectators(), memberService)); + } + + public static List namesOf(Stream memberIds, MemberService memberService) { + return memberIds + .map(memberId -> memberService.findById(memberId).firstName()) + .toList(); + } +} diff --git a/src/main/java/com/jitterted/mobreg/adapter/in/web/member/MemberController.java b/src/main/java/com/jitterted/mobreg/adapter/in/web/member/MemberController.java index 684e5c8e..35628f22 100644 --- a/src/main/java/com/jitterted/mobreg/adapter/in/web/member/MemberController.java +++ b/src/main/java/com/jitterted/mobreg/adapter/in/web/member/MemberController.java @@ -20,6 +20,7 @@ import java.time.ZonedDateTime; import java.util.List; +import java.util.Optional; @Controller public class MemberController { @@ -58,8 +59,13 @@ public String showEnsemblesForUser(Model model, public void addEnsemblesToModel(Model model, Member member) { MemberId memberId = member.getId(); - List availableEnsembles = ensembleService.allUpcomingEnsembles(ZonedDateTime.now()); - List availableEnsembleSummaryViews = EnsembleSummaryView.from(availableEnsembles, memberId, memberService, EnsembleSortOrder.ASCENDING_ORDER); + Optional inProgressEnsemble = + ensembleService.inProgressEnsemble(ZonedDateTime.now(), memberId) + .map(ensemble -> InProgressEnsembleView.from(ensemble, memberService)); + model.addAttribute("inProgressEnsembleViewOptional", inProgressEnsemble); + + List upcomingEnsembles = ensembleService.allUpcomingEnsembles(ZonedDateTime.now()); + List availableEnsembleSummaryViews = EnsembleSummaryView.from(upcomingEnsembles, memberId, memberService, EnsembleSortOrder.ASCENDING_ORDER); model.addAttribute("upcomingEnsembles", availableEnsembleSummaryViews); List pastEnsembles = ensembleService.allInThePastFor(memberId, ZonedDateTime.now()); diff --git a/src/main/java/com/jitterted/mobreg/application/EnsembleService.java b/src/main/java/com/jitterted/mobreg/application/EnsembleService.java index fe373624..3f24b190 100644 --- a/src/main/java/com/jitterted/mobreg/application/EnsembleService.java +++ b/src/main/java/com/jitterted/mobreg/application/EnsembleService.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Optional; import java.util.function.Consumer; +import java.util.stream.Stream; import static java.util.function.Predicate.not; @@ -102,6 +103,7 @@ public void triggerEnsembleScheduledNotification(Ensemble ensemble) { LOGGER.info("Done notifying for Ensemble {}", ensemble.name()); } + // TODO: return Stream<> instead of List<> public List allEnsembles() { return ensembleRepository.findAll(); } @@ -129,14 +131,25 @@ public List allUpcomingEnsembles(ZonedDateTime now) { } public List allInThePastFor(MemberId memberId, ZonedDateTime now) { - return allEnsembles() - .stream() - .filter(not(Ensemble::isCanceled)) + return allEnsemblesNotCanceled() .filter(ensemble -> ensemble.isRegistered(memberId)) .filter(ensemble -> ensemble.endTimeIsInThePast(now)) .toList(); } + public Stream allEnsemblesNotCanceled() { + return allEnsembles() + .stream() + .filter(not(Ensemble::isCanceled)); + } + + public Optional inProgressEnsemble(ZonedDateTime now, MemberId memberId) { + return allEnsemblesNotCanceled() + .filter(ensemble -> ensemble.isInProgress(now)) + .filter(ensemble -> ensemble.isRegistered(memberId)) + .findFirst(); + } + public Optional findById(EnsembleId ensembleId) { return ensembleRepository.findById(ensembleId); } diff --git a/src/main/java/com/jitterted/mobreg/domain/Ensemble.java b/src/main/java/com/jitterted/mobreg/domain/Ensemble.java index affc2c1e..d31b1d05 100644 --- a/src/main/java/com/jitterted/mobreg/domain/Ensemble.java +++ b/src/main/java/com/jitterted/mobreg/domain/Ensemble.java @@ -284,6 +284,11 @@ public String toString() { '}'; } + public boolean isInProgress(ZonedDateTime now) { + return now.isAfter(startDateTime()) && now.isBefore(startDateTime() + .plus(duration)); + } + record WhenSpaceRsvp(When when, Space space, Rsvp rsvp) { // @formatter: off private static final Map STATE_TO_STATUS = Map.ofEntries( diff --git a/src/main/resources/static/tailwind.css b/src/main/resources/static/tailwind.css index 7d12da0d..2d25dc00 100644 --- a/src/main/resources/static/tailwind.css +++ b/src/main/resources/static/tailwind.css @@ -818,6 +818,10 @@ select { margin-top: 2rem; } +.mt-3 { + margin-top: 0.75rem; +} + .block { display: block; } @@ -934,6 +938,10 @@ select { grid-template-columns: repeat(4, minmax(0, 1fr)); } +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + .flex-row { flex-direction: row; } @@ -981,18 +989,6 @@ select { margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); } -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.space-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); -} - .divide-x > :not([hidden]) ~ :not([hidden]) { --tw-divide-x-reverse: 0; border-right-width: calc(1px * var(--tw-divide-x-reverse)); @@ -1047,10 +1043,6 @@ select { border-radius: 0.375rem; } -.rounded-sm { - border-radius: 0.125rem; -} - .border { border-width: 1px; } @@ -1090,20 +1082,15 @@ select { border-color: rgb(209 213 219 / var(--tw-border-opacity)); } -.border-green-200 { +.border-green-300 { --tw-border-opacity: 1; - border-color: rgb(187 247 208 / var(--tw-border-opacity)); + border-color: rgb(134 239 172 / var(--tw-border-opacity)); } .border-transparent { border-color: transparent; } -.border-green-300 { - --tw-border-opacity: 1; - border-color: rgb(134 239 172 / var(--tw-border-opacity)); -} - .bg-blue-100 { --tw-bg-opacity: 1; background-color: rgb(219 234 254 / var(--tw-bg-opacity)); @@ -1139,11 +1126,6 @@ select { background-color: rgb(21 128 61 / var(--tw-bg-opacity)); } -.bg-indigo-100 { - --tw-bg-opacity: 1; - background-color: rgb(224 231 255 / var(--tw-bg-opacity)); -} - .bg-indigo-600 { --tw-bg-opacity: 1; background-color: rgb(79 70 229 / var(--tw-bg-opacity)); @@ -1169,19 +1151,14 @@ select { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } -.bg-yellow-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 249 195 / var(--tw-bg-opacity)); -} - .bg-yellow-50 { --tw-bg-opacity: 1; background-color: rgb(254 252 232 / var(--tw-bg-opacity)); } -.bg-blue-700 { +.bg-green-50 { --tw-bg-opacity: 1; - background-color: rgb(29 78 216 / var(--tw-bg-opacity)); + background-color: rgb(240 253 244 / var(--tw-bg-opacity)); } .bg-opacity-80 { @@ -1192,6 +1169,14 @@ select { padding: 1rem; } +.p-2 { + padding: 0.5rem; +} + +.p-3 { + padding: 0.75rem; +} + .px-10 { padding-left: 2.5rem; padding-right: 2.5rem; @@ -1322,14 +1307,6 @@ select { padding-top: 1.5rem; } -.pb-2 { - padding-bottom: 0.5rem; -} - -.pt-2\.5 { - padding-top: 0.625rem; -} - .text-left { text-align: left; } @@ -1422,6 +1399,14 @@ select { line-height: 1.5; } +.leading-8 { + line-height: 2rem; +} + +.leading-10 { + line-height: 2.5rem; +} + .text-black { --tw-text-opacity: 1; color: rgb(0 0 0 / var(--tw-text-opacity)); @@ -1477,24 +1462,19 @@ select { color: rgb(22 163 74 / var(--tw-text-opacity)); } -.text-green-700 { - --tw-text-opacity: 1; - color: rgb(21 128 61 / var(--tw-text-opacity)); -} - .text-green-800 { --tw-text-opacity: 1; color: rgb(22 101 52 / var(--tw-text-opacity)); } -.text-indigo-600 { +.text-green-900 { --tw-text-opacity: 1; - color: rgb(79 70 229 / var(--tw-text-opacity)); + color: rgb(20 83 45 / var(--tw-text-opacity)); } -.text-indigo-700 { +.text-indigo-600 { --tw-text-opacity: 1; - color: rgb(67 56 202 / var(--tw-text-opacity)); + color: rgb(79 70 229 / var(--tw-text-opacity)); } .text-purple-400 { @@ -1552,6 +1532,11 @@ select { color: rgb(133 77 14 / var(--tw-text-opacity)); } +.text-green-700 { + --tw-text-opacity: 1; + color: rgb(21 128 61 / var(--tw-text-opacity)); +} + .underline { text-decoration-line: underline; } @@ -1574,34 +1559,6 @@ select { box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.shadow-\[0_4px_9px_-4px_\#3b71ca\] { - --tw-shadow: 0 4px 9px -4px #3b71ca; - --tw-shadow-colored: 0 4px 9px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-lg { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-xl { - --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-blue-800\/50 { - --tw-shadow-color: rgb(30 64 175 / 0.5); - --tw-shadow: var(--tw-shadow-colored); -} - -.shadow-blue-500\/50 { - --tw-shadow-color: rgb(59 130 246 / 0.5); - --tw-shadow: var(--tw-shadow-colored); -} - .ring-1 { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); @@ -1617,22 +1574,6 @@ select { --tw-ring-opacity: 0.05; } -.transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.duration-150 { - transition-duration: 150ms; -} - -.ease-in-out { - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - .even\:bg-gray-50:nth-child(even) { --tw-bg-opacity: 1; background-color: rgb(249 250 251 / var(--tw-bg-opacity)); @@ -1664,6 +1605,11 @@ select { background-color: rgb(0 0 0 / var(--tw-bg-opacity)); } +.hover\:bg-blue-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} + .hover\:bg-gray-100:hover { --tw-bg-opacity: 1; background-color: rgb(243 244 246 / var(--tw-bg-opacity)); @@ -1674,11 +1620,6 @@ select { background-color: rgb(249 250 251 / var(--tw-bg-opacity)); } -.hover\:bg-indigo-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(199 210 254 / var(--tw-bg-opacity)); -} - .hover\:bg-indigo-700:hover { --tw-bg-opacity: 1; background-color: rgb(67 56 202 / var(--tw-bg-opacity)); @@ -1689,31 +1630,6 @@ select { background-color: rgb(185 28 28 / var(--tw-bg-opacity)); } -.hover\:bg-blue-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(37 99 235 / var(--tw-bg-opacity)); -} - -.hover\:bg-blue-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(29 78 216 / var(--tw-bg-opacity)); -} - -.hover\:bg-indigo-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(99 102 241 / var(--tw-bg-opacity)); -} - -.hover\:bg-blue-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); -} - -.hover\:bg-blue-400:hover { - --tw-bg-opacity: 1; - background-color: rgb(96 165 250 / var(--tw-bg-opacity)); -} - .hover\:text-blue-600:hover { --tw-text-opacity: 1; color: rgb(37 99 235 / var(--tw-text-opacity)); @@ -1734,65 +1650,25 @@ select { color: rgb(49 46 129 / var(--tw-text-opacity)); } -.hover\:shadow-\[0_8px_9px_-4px_rgba\(59\2c 113\2c 202\2c 0\.3\)\2c 0_4px_18px_0_rgba\(59\2c 113\2c 202\2c 0\.2\)\]:hover { - --tw-shadow: 0 8px 9px -4px rgba(59,113,202,0.3),0 4px 18px 0 rgba(59,113,202,0.2); - --tw-shadow-colored: 0 8px 9px -4px var(--tw-shadow-color), 0 4px 18px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - .focus\:border-indigo-500:focus { --tw-border-opacity: 1; border-color: rgb(99 102 241 / var(--tw-border-opacity)); } -.focus\:bg-blue-600:focus { - --tw-bg-opacity: 1; - background-color: rgb(37 99 235 / var(--tw-bg-opacity)); -} - -.focus\:bg-blue-800:focus { - --tw-bg-opacity: 1; - background-color: rgb(30 64 175 / var(--tw-bg-opacity)); -} - -.focus\:shadow-\[0_8px_9px_-4px_rgba\(59\2c 113\2c 202\2c 0\.3\)\2c 0_4px_18px_0_rgba\(59\2c 113\2c 202\2c 0\.2\)\]:focus { - --tw-shadow: 0 8px 9px -4px rgba(59,113,202,0.3),0 4px 18px 0 rgba(59,113,202,0.2); - --tw-shadow-colored: 0 8px 9px -4px var(--tw-shadow-color), 0 4px 18px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - .focus\:outline-none:focus { outline: 2px solid transparent; outline-offset: 2px; } -.focus\:outline-2:focus { - outline-width: 2px; -} - -.focus\:outline-offset-1:focus { - outline-offset: 1px; -} - -.focus\:outline-offset-2:focus { - outline-offset: 2px; -} - -.focus\:ring-2:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.focus\:ring-0:focus { +.focus\:ring-1:focus { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); } -.focus\:ring-1:focus { +.focus\:ring-2:focus { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); } @@ -1806,60 +1682,12 @@ select { --tw-ring-color: rgb(252 165 165 / var(--tw-ring-opacity)); } -.focus\:ring-offset-2:focus { - --tw-ring-offset-width: 2px; -} - .focus\:ring-offset-1:focus { --tw-ring-offset-width: 1px; } -.focus-visible\:bg-blue-600:focus-visible { - --tw-bg-opacity: 1; - background-color: rgb(37 99 235 / var(--tw-bg-opacity)); -} - -.focus-visible\:bg-blue-900:focus-visible { - --tw-bg-opacity: 1; - background-color: rgb(30 58 138 / var(--tw-bg-opacity)); -} - -.focus-visible\:bg-blue-800:focus-visible { - --tw-bg-opacity: 1; - background-color: rgb(30 64 175 / var(--tw-bg-opacity)); -} - -.focus-visible\:outline-none:focus-visible { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.focus-visible\:outline:focus-visible { - outline-style: solid; -} - -.focus-visible\:outline-2:focus-visible { - outline-width: 2px; -} - -.focus-visible\:outline-offset-2:focus-visible { - outline-offset: 2px; -} - -.focus-visible\:outline-indigo-600:focus-visible { - outline-color: #4f46e5; -} - -.focus-visible\:ring-1:focus-visible { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - -.focus-visible\:ring-2:focus-visible { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +.focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; } .active\:bg-black:active { @@ -1867,35 +1695,15 @@ select { background-color: rgb(0 0 0 / var(--tw-bg-opacity)); } -.active\:bg-blue-700:active { - --tw-bg-opacity: 1; - background-color: rgb(29 78 216 / var(--tw-bg-opacity)); -} - .active\:bg-blue-400:active { --tw-bg-opacity: 1; background-color: rgb(96 165 250 / var(--tw-bg-opacity)); } -.active\:shadow-\[0_8px_9px_-4px_rgba\(59\2c 113\2c 202\2c 0\.3\)\2c 0_4px_18px_0_rgba\(59\2c 113\2c 202\2c 0\.2\)\]:active { - --tw-shadow: 0 8px 9px -4px rgba(59,113,202,0.3),0 4px 18px 0 rgba(59,113,202,0.2); - --tw-shadow-colored: 0 8px 9px -4px var(--tw-shadow-color), 0 4px 18px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.disabled\:cursor-default:disabled { - cursor: default; -} - .disabled\:cursor-not-allowed:disabled { cursor: not-allowed; } -.disabled\:text-gray-400:disabled { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity)); -} - .group:hover .group-hover\:block { display: block; } @@ -1916,30 +1724,6 @@ select { --tw-text-opacity: 1; color: rgb(243 244 246 / var(--tw-text-opacity)); } - - .dark\:shadow-\[0_4px_9px_-4px_rgba\(59\2c 113\2c 202\2c 0\.5\)\] { - --tw-shadow: 0 4px 9px -4px rgba(59,113,202,0.5); - --tw-shadow-colored: 0 4px 9px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - } - - .dark\:hover\:shadow-\[0_8px_9px_-4px_rgba\(59\2c 113\2c 202\2c 0\.2\)\2c 0_4px_18px_0_rgba\(59\2c 113\2c 202\2c 0\.1\)\]:hover { - --tw-shadow: 0 8px 9px -4px rgba(59,113,202,0.2),0 4px 18px 0 rgba(59,113,202,0.1); - --tw-shadow-colored: 0 8px 9px -4px var(--tw-shadow-color), 0 4px 18px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - } - - .dark\:focus\:shadow-\[0_8px_9px_-4px_rgba\(59\2c 113\2c 202\2c 0\.2\)\2c 0_4px_18px_0_rgba\(59\2c 113\2c 202\2c 0\.1\)\]:focus { - --tw-shadow: 0 8px 9px -4px rgba(59,113,202,0.2),0 4px 18px 0 rgba(59,113,202,0.1); - --tw-shadow-colored: 0 8px 9px -4px var(--tw-shadow-color), 0 4px 18px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - } - - .dark\:active\:shadow-\[0_8px_9px_-4px_rgba\(59\2c 113\2c 202\2c 0\.2\)\2c 0_4px_18px_0_rgba\(59\2c 113\2c 202\2c 0\.1\)\]:active { - --tw-shadow: 0 8px 9px -4px rgba(59,113,202,0.2),0 4px 18px 0 rgba(59,113,202,0.1); - --tw-shadow-colored: 0 8px 9px -4px var(--tw-shadow-color), 0 4px 18px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - } } @media (min-width: 640px) { @@ -2003,12 +1787,6 @@ select { margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse))); } - .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0px * var(--tw-space-y-reverse)); - } - .sm\:truncate { overflow: hidden; text-overflow: ellipsis; diff --git a/src/main/resources/templates/member-register.html b/src/main/resources/templates/member-register.html index 27c98395..c182e5fe 100644 --- a/src/main/resources/templates/member-register.html +++ b/src/main/resources/templates/member-register.html @@ -68,6 +68,34 @@

+

+ In Progress +

+
+
+

Ensemble Name

+

+ + Zoom Link + +

+

Started At 9:00am

+
+
+

PARTICIPANTS

+

Participant

+
+
+

SPECTATORS

+

Spectator

+
+
+ + +

Upcoming Ensembles diff --git a/src/test/java/com/jitterted/mobreg/adapter/in/web/member/EnsembleSummaryViewTest.java b/src/test/java/com/jitterted/mobreg/adapter/in/web/member/EnsembleSummaryViewTest.java index bb4f1f1b..ac7f84bd 100644 --- a/src/test/java/com/jitterted/mobreg/adapter/in/web/member/EnsembleSummaryViewTest.java +++ b/src/test/java/com/jitterted/mobreg/adapter/in/web/member/EnsembleSummaryViewTest.java @@ -133,33 +133,6 @@ private static FutureEnsembleMemberFixture createEnsemble1DayInFutureAndCreateMe private record FutureEnsembleMemberFixture(Ensemble ensemble, Member member, MemberService memberService) { } - @Nested - class InProgressEnsemble { - - // in "Upcoming Ensemble" list: appears as In-Progress only if NOW is after start time, NOW is before end time, only if member (POV) is REGISTERED - - @Test - void startsInTheFutureRegardlessOfMemberIsNotInProgress() { - Ensemble ensemble = EnsembleFactory.withIdOf1AndOneDayInTheFuture(); - - EnsembleSummaryView ensembleSummaryView = EnsembleSummaryView - .toView(ensemble, IRRELEVANT_MEMBER_ID, STUB_MEMBER_SERVICE); - - assertThat(ensembleSummaryView.inProgress()) - .isFalse(); - } - - @Test - void startTimeInThePastEndTimeInTheFutureIsInProgress() { - - } - -// @Test -// void endTimeIsInThePastIsNotInProgress() { -// -// } - } - @Nested class StatusLinksOnly { diff --git a/src/test/java/com/jitterted/mobreg/adapter/in/web/member/InProgressEnsembleViewTest.java b/src/test/java/com/jitterted/mobreg/adapter/in/web/member/InProgressEnsembleViewTest.java new file mode 100644 index 00000000..0d0255a7 --- /dev/null +++ b/src/test/java/com/jitterted/mobreg/adapter/in/web/member/InProgressEnsembleViewTest.java @@ -0,0 +1,57 @@ +package com.jitterted.mobreg.adapter.in.web.member; + +import com.jitterted.mobreg.application.MemberService; +import com.jitterted.mobreg.application.TestMemberBuilder; +import com.jitterted.mobreg.domain.Ensemble; +import com.jitterted.mobreg.domain.Member; +import com.jitterted.mobreg.domain.ZonedDateTimeFactory; +import org.junit.jupiter.api.Test; + +import java.net.URI; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class InProgressEnsembleViewTest { + + // appears as In-Progress only if NOW is after start time, NOW is before end time, only if member (POV) is REGISTERED + + @Test + void hasAllInformationFiledIn() throws Exception { + Ensemble ensemble = new Ensemble("in-progress", + new URI("https://zoom.us/inprogress"), + ZonedDateTimeFactory.zoneDateTimeUtc(2024, 2, 1, 11)); + TestMemberBuilder memberBuilder = new TestMemberBuilder(); + Member participant1 = memberBuilder.withFirstName("Participant 1").buildAndSave(); + Member participant2 = memberBuilder.withFirstName("Participant 2").buildAndSave(); + Member spectator1 = memberBuilder.withFirstName("Spectator 1").buildAndSave(); + MemberService memberService = memberBuilder.memberService(); + ensemble.joinAsParticipant(participant1.getId()); + ensemble.joinAsParticipant(participant2.getId()); + ensemble.joinAsSpectator(spectator1.getId()); + + InProgressEnsembleView inProgressEnsembleView = InProgressEnsembleView.from(ensemble, memberService); + + assertThat(inProgressEnsembleView) + .isEqualTo(new InProgressEnsembleView("in-progress", + "https://zoom.us/inprogress", + "11:00 AM", // hour = 11 + List.of("Participant 1", "Participant 2"), + List.of("Spectator 1"))); + } + + + // this test makes no sense, since the presentation doesn't decide whether or not an individual member sees a specific ensemble + // that's decided by the EnsembleService (Application Layer) +// @Test +// void startTimeInThePastEndTimeInTheFutureMemberIsNotRegisteredThenIsNotInProgress() { +// } + + // Member won't even see an Ensemble from the past unless they had registered anyway +// @Test +// void endTimeIsInThePastMemberRegisteredIsNotInProgress() { +// +// } + + +} \ No newline at end of file diff --git a/src/test/java/com/jitterted/mobreg/adapter/in/web/member/MemberControllerTest.java b/src/test/java/com/jitterted/mobreg/adapter/in/web/member/MemberControllerTest.java index ab14e4d0..524a6cf6 100644 --- a/src/test/java/com/jitterted/mobreg/adapter/in/web/member/MemberControllerTest.java +++ b/src/test/java/com/jitterted/mobreg/adapter/in/web/member/MemberControllerTest.java @@ -26,6 +26,7 @@ import java.time.ZonedDateTime; import java.util.List; +import java.util.Optional; import java.util.Set; import static org.assertj.core.api.Assertions.*; @@ -37,18 +38,24 @@ class MemberControllerTest { class ViewModels { @Test - void registerViewShowsPastAndFutureEnsembles() { - Fixture fixture = createFixture(new Ensemble("Future Ensemble", - ZonedDateTime.now().plusDays(2))); + void registerViewShowsInProgressAndPastAndFutureEnsembles() { + Ensemble upcomingEnsemble = new Ensemble("Future Ensemble", + ZonedDateTime.now().plusDays(2)); + Fixture fixture = createFixture(upcomingEnsemble); AuthFixture authFixture = createAuthUser(fixture.memberService); - Ensemble ensemble1 = fixture.ensembleService.scheduleEnsemble("Past Ensemble 1", - ZonedDateTime.now() - .minusDays(12)); - fixture.ensembleService.joinAsParticipant(ensemble1.getId(), authFixture.memberId); - Ensemble ensemble2 = fixture.ensembleService.scheduleEnsemble("Past Ensemble 2", - ZonedDateTime.now() - .minusDays(5)); - fixture.ensembleService.joinAsSpectator(ensemble2.getId(), authFixture.memberId); + Ensemble ensemble1 = fixture.ensembleService() + .scheduleEnsemble("Past Ensemble 1", + ZonedDateTime.now().minusDays(12)); + fixture.ensembleService().joinAsParticipant(ensemble1.getId(), authFixture.memberId); + Ensemble ensemble2 = fixture.ensembleService() + .scheduleEnsemble("Past Ensemble 2", + ZonedDateTime.now().minusDays(5)); + fixture.ensembleService().joinAsSpectator(ensemble2.getId(), authFixture.memberId); + Ensemble inProgressEnsemble = fixture.ensembleService() + .scheduleEnsemble("In Progress", + ZonedDateTime.now().minusMinutes(5)); + fixture.ensembleService() + .joinAsParticipant(inProgressEnsemble.getId(), authFixture.memberId); Model model = new ConcurrentModel(); fixture.memberController.showEnsemblesForUser(model, @@ -63,6 +70,11 @@ void registerViewShowsPastAndFutureEnsembles() { assertThat(pastEnsembles) .as("Should be 2 Past Ensembles in the Model") .hasSize(2); + Optional inProgressEnsembleViewOptional = (Optional) model.getAttribute("inProgressEnsembleViewOptional"); + assertThat(inProgressEnsembleViewOptional) + .isPresent() + .get() + .isExactlyInstanceOf(InProgressEnsembleView.class); } @Test diff --git a/src/test/java/com/jitterted/mobreg/application/EnsembleServiceTest.java b/src/test/java/com/jitterted/mobreg/application/EnsembleServiceTest.java index 83e7771f..8ea48e6b 100644 --- a/src/test/java/com/jitterted/mobreg/application/EnsembleServiceTest.java +++ b/src/test/java/com/jitterted/mobreg/application/EnsembleServiceTest.java @@ -11,10 +11,12 @@ import com.jitterted.mobreg.domain.Member; import com.jitterted.mobreg.domain.MemberId; import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import java.time.ZonedDateTime; import java.util.List; +import java.util.Optional; import static com.jitterted.mobreg.domain.ZonedDateTimeFactory.zoneDateTimeUtc; import static org.assertj.core.api.Assertions.*; @@ -146,6 +148,48 @@ void pastEnsemblesForMemberShowOnlyThoseForWhichMemberRegisteredAndNotCanceled() .containsExactly(registeredNotCanceledEnsemble); } + @Nested + class InProgressEnsemble { + @Test + void isEmptyWhenNoEnsembleIsInProgress() { + Fixture fixture = createFixture(EnsembleFactory.withIdOf1AndOneDayInTheFuture()); + fixture.ensembleService().scheduleEnsemble("In The Past", ZonedDateTime.now().minusDays(1)); + + Optional optionalEnsemble = fixture.ensembleService() + .inProgressEnsemble(ZonedDateTime.now(), fixture.memberId()); + + assertThat(optionalEnsemble) + .isEmpty(); + } + + @Test + void isEmptyWhenEnsembleInProgressAndMemberNotRegistered() { + ZonedDateTime startDateTime = ZonedDateTime.now().plusMinutes(1); + Fixture fixture = createFixture(EnsembleFactory.withStartTime(startDateTime)); + + ZonedDateTime now = startDateTime.plusSeconds(1); + Optional optionalEnsemble = fixture.ensembleService() + .inProgressEnsemble(now, fixture.memberId()); + + assertThat(optionalEnsemble) + .isEmpty(); + } + + @Test + void isPresentWhenInProgressAndMemberIsRegistered() { + ZonedDateTime startDateTime = ZonedDateTime.now().plusMinutes(1); + Fixture fixture = createFixture(EnsembleFactory.withStartTime(startDateTime)); + fixture.ensembleService().joinAsParticipant(fixture.ensemble().getId(), fixture.memberId()); + + ZonedDateTime now = startDateTime.plusSeconds(1); + Optional optionalEnsemble = fixture.ensembleService() + .inProgressEnsemble(now, fixture.memberId()); + + assertThat(optionalEnsemble) + .isPresent(); + } + } + // //-- Encapsulated Setup Fixtures