Skip to content

Commit

Permalink
unify roles, invite any role, update deps
Browse files Browse the repository at this point in the history
  • Loading branch information
rpiaggio committed Sep 18, 2024
1 parent eaa86af commit db220e0
Show file tree
Hide file tree
Showing 15 changed files with 103 additions and 86 deletions.
2 changes: 1 addition & 1 deletion common/src/main/scala/explore/model/reusability.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ object reusability:
given [D: Eq]: Reusability[Atom[D]] = Reusability.byEq
given Reusability[ExecutionVisits] = Reusability.byEq
given Reusability[ProgramUserWithRole] = Reusability.byEq
given Reusability[CoIInvitation] = Reusability.byEq
given Reusability[UserInvitation] = Reusability.byEq
given Reusability[IsActive] = Reusability.byEq
given Reusability[PAProperties] = Reusability.byEq
given Reusability[GraphResult] = Reusability.byEq
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,25 @@ package queries.common
import clue.GraphQLOperation
import clue.annotation.GraphQL
import lucuma.schemas.ObservationDB
import explore.model.CoIInvitation
import explore.model.UserInvitation
import explore.model.ProgramInvitation

object InvitationQueriesGQL:
@GraphQL
trait CreateInviteMutation extends GraphQLOperation[ObservationDB]:
val document: String = s"""
mutation($$programId: ProgramId!, $$recipientEmail: String!) {
mutation($$programId: ProgramId!, $$recipientEmail: String!, $$role: ProgramUserRole!) {
createUserInvitation(input: {
programId: $$programId,
recipientEmail: $$recipientEmail,
role: COI
role: $$role
}) {
key
invitation {
id
status
recipientEmail
role
email {
status
}
Expand All @@ -34,7 +35,7 @@ object InvitationQueriesGQL:

object Data:
object CreateUserInvitation:
type Invitation = CoIInvitation
type Invitation = UserInvitation

@GraphQL
trait RevokeInvitationMutation extends GraphQLOperation[ObservationDB]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ package queries.common

import clue.GraphQLSubquery
import clue.annotation.GraphQL
import explore.model.CoIInvitation
import explore.model.UserInvitation
import lucuma.schemas.ObservationDB

@GraphQL
object ProgramInvitationsSubquery
extends GraphQLSubquery.Typed[ObservationDB, CoIInvitation]("CoIInvitation"):
extends GraphQLSubquery.Typed[ObservationDB, UserInvitation]("CoIInvitation"):
override val subquery: String = """
{
id
recipientEmail
role
status
email {
status
Expand Down
14 changes: 7 additions & 7 deletions explore/src/main/scala/explore/programs/ProgramDetailsTile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import explore.model.Constants
import explore.model.ProgramDetails
import explore.model.ProgramTimes
import explore.model.ProgramUserWithRole
import explore.model.enums.ProgramUserRole
import lucuma.core.enums.ProgramUserRole
import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.core.model.Semester
Expand All @@ -28,10 +28,10 @@ object ProgramDetailsTile:
private type Props = ProgramDetailsTile

val component = ScalaFnComponent[Props]: props =>
val details: ProgramDetails = props.programDetails
val thesis: Boolean = details.allUsers.exists(_.thesis.exists(_ === true))
val support: List[ProgramUserWithRole] =
details.allUsers.filter(_.role.contains_(ProgramUserRole.Support))
val details: ProgramDetails = props.programDetails
val thesis: Boolean = details.allUsers.exists(_.thesis.exists(_ === true))
val supportPrimary: List[ProgramUserWithRole] =
details.allUsers.filter(_.role === ProgramUserRole.SupportPrimary)

<.div(ExploreStyles.ProgramDetailsTile)(
<.div(ExploreStyles.ProgramDetailsInfoArea)(
Expand All @@ -49,8 +49,8 @@ object ProgramDetailsTile:
),
<.div(ExploreStyles.ProgramDetailsInfoArea)(
// The Contact scientists are the program SUPPORT role which has been requested to be split into two (3278): "Principal Support" and "Additional Support".
FormInfo(support.map(_.nameWithEmail).mkString(", "), "Contact Scientists")
.when(support.nonEmpty)
FormInfo(supportPrimary.map(_.nameWithEmail).mkString(", "), "Contact Scientists")
.when(supportPrimary.nonEmpty)
// FormInfo("", "Principal Support"),
// FormInfo("", "Additional Support"),
// The two Notifications flags are user-settable and determine whether the archive sends email notifications for new data and whether the ODB sends notifications for expired timing windows (3388, 3389)
Expand Down
17 changes: 12 additions & 5 deletions explore/src/main/scala/explore/programs/SupportUsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

package explore.programs

import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.react.common.ReactFnProps
import cats.data.NonEmptySet
import crystal.react.View
import explore.model.ProgramUserWithRole
import explore.users.ProgramUsersTable
import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.core.model.Program
import explore.proposal.ProgramUsersTable
import lucuma.react.common.ReactFnProps
import lucuma.core.enums.ProgramUserRole

case class SupportUsers(
programId: Program.Id,
Expand All @@ -21,4 +23,9 @@ object SupportUsers:
private type Props = SupportUsers

private val component = ScalaFnComponent[Props]: props =>
ProgramUsersTable(props.programId, props.users, props.readonly)
ProgramUsersTable(
props.programId,
props.users,
NonEmptySet.of(ProgramUserRole.SupportPrimary, ProgramUserRole.SupportSecondary),
props.readonly
)
21 changes: 11 additions & 10 deletions explore/src/main/scala/explore/proposal/InviteUserPopup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import eu.timepit.refined.types.string.NonEmptyString
import explore.Icons
import explore.components.ui.ExploreStyles
import explore.model.AppContext
import explore.model.CoIInvitation
import explore.model.UserInvitation
import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.core.data.EmailAddress
import lucuma.core.data.EmailPred
import lucuma.core.enums.ProgramUserRole
import lucuma.core.model.Program
import lucuma.core.validation.InputValidSplitEpi
import lucuma.react.common.ReactFnProps
Expand All @@ -40,12 +41,13 @@ import queries.common.InvitationQueriesGQL.CreateInviteMutation.Data

case class InviteUserPopup(
pid: Program.Id,
invitations: View[List[CoIInvitation]],
role: ProgramUserRole,
invitations: View[List[UserInvitation]],
ref: OverlayPanelRef
) extends ReactFnProps(InviteUserPopup.component)

object InviteUserPopup:
val MailValidator: InputValidSplitEpi[EmailAddress] =
private val MailValidator: InputValidSplitEpi[EmailAddress] =
// Scala doesn't like type aliases with refined types?
InputValidSplitEpi.refinedString[EmailPred].asInstanceOf[InputValidSplitEpi[EmailAddress]]

Expand All @@ -69,23 +71,21 @@ object InviteUserPopup:
viewKey: View[Option[String]]
): IO[Unit] =
(createInvite.set(CreateInviteProcess.Running).to[IO] *>
CreateInviteMutation[IO].execute(pid, email.value.value)).attempt
.flatMap {
CreateInviteMutation[IO].execute(pid, email.value.value, props.role)).attempt
.flatMap:
case Left(e) =>
Logger[IO].error(e)("Error creating invitation") *>
createInvite.set(CreateInviteProcess.Error).to[IO]
case Right(r) =>
props.invitations.mod(r.createUserInvitation.invitation :: _).to[IO] *>
viewKey.set(r.createUserInvitation.key.some).to[IO] *>
createInvite.set(CreateInviteProcess.Done).to[IO]
}

OverlayPanel(
closeOnEscape = true,
onHide = key.set(None) >> emailView.set(None).runAsyncAndForget
)(
<.div(
PrimeStyles.Dialog,
<.div(PrimeStyles.Dialog)(
<.div(PrimeStyles.DialogHeader, "Create CoI invitation"),
<.div(PrimeStyles.DialogContent)(
<.div(LucumaPrimeStyles.FormColumnCompact)(
Expand All @@ -110,8 +110,9 @@ object InviteUserPopup:
)
),
<.div(PrimeStyles.DialogFooter)(
Message(text = "Error submitting user invite, try later",
severity = Message.Severity.Error
Message(
text = "Error submitting user invite, try later",
severity = Message.Severity.Error
).when(inviteState.get === CreateInviteProcess.Error),
Button(
icon = Icons.Close,
Expand Down
26 changes: 19 additions & 7 deletions explore/src/main/scala/explore/proposal/ProgramUsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@

package explore.proposal

import cats.data.NonEmptySet
import cats.data.NonEmptyList
import cats.syntax.all.*
import crystal.react.*
import explore.Icons
import explore.components.Tile
import explore.model.CoIInvitation
import explore.model.ProgramUserWithRole
import explore.model.ProposalTabTileIds
import explore.model.UserInvitation
import explore.users.ProgramUserInvitations
import explore.users.ProgramUsersTable
import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.core.enums.InvitationStatus
Expand All @@ -22,6 +25,7 @@ import lucuma.react.primereact.Button
import lucuma.react.primereact.OverlayPanelRef
import lucuma.ui.primereact.*
import lucuma.ui.syntax.all.given
import lucuma.core.enums.ProgramUserRole

enum CreateInviteProcess(private val tag: String) derives Enumerated:
case Idle extends CreateInviteProcess("idle")
Expand All @@ -36,13 +40,18 @@ case class ProgramUsers(
pid: Program.Id,
readOnly: Boolean,
users: View[List[ProgramUserWithRole]],
invitations: View[List[CoIInvitation]],
invitations: View[List[UserInvitation]],
state: View[ProgramUsersState]
) extends ReactFnProps(ProgramUsers.component)

object ProgramUsers:
private type Props = ProgramUsers

def inviteControl(readOnly: Boolean, ref: OverlayPanelRef, state: View[ProgramUsersState]) =
private def inviteControl(
readOnly: Boolean,
ref: OverlayPanelRef,
state: View[ProgramUsersState]
) =
Button(
severity = Button.Severity.Secondary,
size = Button.Size.Small,
Expand All @@ -57,7 +66,7 @@ object ProgramUsers:
pid: Program.Id,
readOnly: Boolean,
users: View[List[ProgramUserWithRole]],
invitations: View[List[CoIInvitation]],
invitations: View[List[UserInvitation]],
ref: OverlayPanelRef
) =
Tile(
Expand All @@ -66,12 +75,15 @@ object ProgramUsers:
ProgramUsersState(CreateInviteProcess.Idle)
)(ProgramUsers(pid, readOnly, users, invitations, _), (s, _) => inviteControl(readOnly, ref, s))

private type Props = ProgramUsers

private val component =
ScalaFnComponent[Props]: props =>
<.div(
ProgramUsersTable(props.pid, props.users, props.readOnly),
ProgramUsersTable(
props.pid,
props.users,
NonEmptySet.of(ProgramUserRole.Pi, ProgramUserRole.Coi, ProgramUserRole.CoiRO),
props.readOnly
),
React
.Fragment(
"Pending invitations",
Expand Down
7 changes: 4 additions & 3 deletions explore/src/main/scala/explore/proposal/ProposalEditor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@ import explore.components.TileController
import explore.components.ui.*
import explore.model.AppContext
import explore.model.CallForProposal
import explore.model.CoIInvitation
import explore.model.ExploreGridLayouts
import explore.model.ProgramTimeRange
import explore.model.ProgramUserWithRole
import explore.model.Proposal
import explore.model.ProposalAttachment
import explore.model.ProposalTabTileIds
import explore.model.UserInvitation
import explore.model.enums.GridLayoutSection
import explore.model.layout.LayoutsMap
import explore.undo.*
import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.core.enums.ProgramUserRole
import lucuma.core.model.Program
import lucuma.core.model.User
import lucuma.react.common.ReactFnProps
Expand All @@ -50,7 +51,7 @@ case class ProposalEditor(
undoStacks: View[UndoStacks[IO, Proposal]],
timeEstimateRange: Pot[Option[ProgramTimeRange]],
users: View[List[ProgramUserWithRole]],
invitations: View[List[CoIInvitation]],
invitations: View[List[UserInvitation]],
attachments: View[List[ProposalAttachment]],
authToken: Option[NonEmptyString],
cfps: List[CallForProposal],
Expand Down Expand Up @@ -157,7 +158,7 @@ object ProposalEditor:
)

<.div(ExploreStyles.MultiPanelTile)(
InviteUserPopup(props.programId, props.invitations, overlayRef),
InviteUserPopup(props.programId, ProgramUserRole.Coi, props.invitations, overlayRef),
TileController(
props.optUserId,
resize.width.getOrElse(1),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package explore.proposal
package explore.users

import cats.effect.IO
import cats.syntax.all.*
Expand All @@ -12,8 +12,8 @@ import explore.Icons
import explore.components.*
import explore.components.ui.ExploreStyles
import explore.model.AppContext
import explore.model.CoIInvitation
import explore.model.IsActive
import explore.model.UserInvitation
import explore.model.reusability.given
import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.html_<^.*
Expand All @@ -29,19 +29,19 @@ import lucuma.ui.syntax.all.given
import lucuma.ui.table.*
import queries.common.InvitationQueriesGQL.*

case class ProgramUserInvitations(invitations: View[List[CoIInvitation]], readOnly: Boolean)
case class ProgramUserInvitations(invitations: View[List[UserInvitation]], readOnly: Boolean)
extends ReactFnProps(ProgramUserInvitations.component)

object ProgramUserInvitations:
private type Props = ProgramUserInvitations

private case class TableMeta(
isActive: View[IsActive],
invitations: View[List[CoIInvitation]],
invitations: View[List[UserInvitation]],
readOnly: Boolean
)

private val ColDef = ColumnDef.WithTableMeta[CoIInvitation, TableMeta]
private val ColDef = ColumnDef.WithTableMeta[UserInvitation, TableMeta]

private val KeyId: ColumnId = ColumnId("id")
private val EmailId: ColumnId = ColumnId("email")
Expand All @@ -57,13 +57,13 @@ object ProgramUserInvitations:

private def column[V](
id: ColumnId,
accessor: CoIInvitation => V
): ColumnDef.Single.WithTableMeta[CoIInvitation, V, TableMeta] =
accessor: UserInvitation => V
): ColumnDef.Single.WithTableMeta[UserInvitation, V, TableMeta] =
ColDef(id, accessor, columnNames(id))

private def columns(
ctx: AppContext[IO]
): List[ColumnDef.WithTableMeta[CoIInvitation, ?, TableMeta]] =
): List[ColumnDef.WithTableMeta[UserInvitation, ?, TableMeta]] =
import ctx.given

List(
Expand All @@ -73,11 +73,9 @@ object ProgramUserInvitations:
EmailStatusId,
_.emailStatus,
"Email Status",
cell = { cell =>
cell.value
.map(es => <.span(es.tag.toUpperCase).withTooltip(es.description))
.getOrElse(<.span())
}
cell = _.value
.map(es => <.span(es.tag.toUpperCase).withTooltip(es.description))
.getOrElse(<.span())
),
ColDef(
RevokeId,
Expand Down
Loading

0 comments on commit db220e0

Please sign in to comment.